🛃
【iOS】UISegmentedControlのセグメントにtitleとimageを同時に設定する方法
UISegmentedControl
にtitleとimageを設定しようとすると、そのどちらかしか設定出来なかった為、両方を同時に設定する方法を調べました。
環境
- Xcode 14.3
- iOS 16.2
setTitleとsetImageを一緒に使用できない
UISegmentedControl
には、titleをセットするメソッドとUIImageをセットするメソッドがあるのですが、
/// titleをセット
func setTitle(_ title: String?, forSegmentAt segment: Int)
/// UIImageをセット
func setImage(_ image: UIImage?, forSegmentAt segment: Int)
このメソッドのドキュメントに一つのセグメントに対して両方同時に使用できないことが記載させています。
A segment can have only an image or a title; it can’t have both.
セグメントには画像またはタイトルのみを含めることができます。両方を持つことはできません。
実際に試してみる
viewDidLoad()
で各セグメントをセットアップしています。
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var segmentedControl: UISegmentedControl!
override func viewDidLoad() {
super.viewDidLoad()
setUp()
}
private func setUp() {
// 左端
segmentedControl.setTitle("タイトル", forSegmentAt: 0)
// 中央
segmentedControl.setImage(UIImage(systemName: "face.smiling"), forSegmentAt: 1)
// 右端
segmentedControl.setTitle("タイトル", forSegmentAt: 2)
segmentedControl.setImage(UIImage(systemName: "face.smiling"), forSegmentAt: 2)
}
}
右端のセグメントには、setTitle(, forSegemtAt:)
とsetImage(, forSegemtAt:)
を実行していますが、結果としては後に実行したUIImage
しか反映されていませんでした。
UIImageにテキストを埋め込む
titleとimageは同時にはセットできない為、テキストを埋め込んだUIImage
を作成して、それをsetImage(, forSegemtAt:)
する方法で対応します。
UIImage+Extension
テキストを文字列に埋め込むExtensionメソッドを作成しました。
extension UIImage {
func embeddedText(_ text: String, font: UIFont = UIFont.systemFont(ofSize: 13), isStartedFromText: Bool = false) -> UIImage {
let expectedTextSize = text.size(withAttributes: [.font: font])
let margin: CGFloat = 8
let width = expectedTextSize.width + self.size.width + margin
let height = max(expectedTextSize.height, self.size.width)
// 文字列埋め込み後のサイズ
let size = CGSize(width: width, height: height)
let renderer = UIGraphicsImageRenderer(size: size)
return renderer.image { context in
let textPointX = isStartedFromText ? 0 : self.size.width + margin
let textPointY = (height - expectedTextSize.height) / 2
let textPoint = CGPoint(x: textPointX, y: textPointY)
text.draw(at: textPoint, withAttributes: [.font: font])
let imagePointX = isStartedFromText ? expectedTextSize.width + margin : 0
let imagePointY = (height - self.size.height) / 2
let rect = CGRect(x: imagePointX,
y: imagePointY,
width: self.size.width,
height: self.size.height)
self.draw(in: rect)
}
}
}
各引数は、
- text
- 画像に埋め込む文字列
- font
- 画像に埋め込む文字列のフォント
- isStartedFromText
- 埋め込んだ文字列から画像が始まるかどうか
処理の内容としては、文字列と画像のサイズを計算して、組み合わせて一つのUIImage
を作成しています。
- 文字列と画像間のマージンを
8
にしていますが、特に指定はありません。 - デフォルトのフォントを
UIFont.systemFont(ofSize: 13)
にしていますが、特に指定はありません。
デモ
コード変更
setUp()
内のコードを書き換えます。
private func setUp() {
// 左端
segmentedControl.setTitle("タイトル", forSegmentAt: 0)
// 中央
segmentedControl.setImage(UIImage(systemName: "face.smiling"), forSegmentAt: 1)
// 右端
+ let textEmbeddedImage = UIImage(systemName: "face.smiling")?.embeddedText("タイトル")
+ segmentedControl.setImage(textEmbeddedImage, forSegmentAt: 2)
- segmentedControl.setImage(UIImage(systemName: "face.smiling"), forSegmentAt: 2)
- segmentedControl.setTitle("タイトル", forSegmentAt: 2)
}
結果
無事に文字列が埋め込まれたUIImage
が表示されました。
まとめ
-
UISegmentedControl
は、setTitle
とsetImage
を同時に使用することが出来ない -
UIImage
にタイトルを埋め込めば、文字列と画像をセグメント状にセットできる
おわりに
苦肉の策で文字列を埋め込んだ画像を作成する方法で対応しましたが、もっとこうした方が良い等ありましたら優しく教えていただければと思います。
参考
Discussion