🛃

【iOS】UISegmentedControlのセグメントにtitleとimageを同時に設定する方法

2023/05/24に公開

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しか反映されていませんでした。

settitle-setimage-demo

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は、setTitlesetImageを同時に使用することが出来ない
  • UIImageにタイトルを埋め込めば、文字列と画像をセグメント状にセットできる

おわりに

苦肉の策で文字列を埋め込んだ画像を作成する方法で対応しましたが、もっとこうした方が良い等ありましたら優しく教えていただければと思います。

littleossa

参考

https://developer.apple.com/documentation/uikit/uisegmentedcontrol/1618556-settitle
https://developer.apple.com/documentation/uikit/uisegmentedcontrol/1618582-setimage
https://stackoverflow.com/questions/13457876/uisegmentedcontrol-with-image-and-title

Discussion