iOS13では、UISegmentedControlのデザインが大きく変更となっています。
本来はOS標準のデザインの方が保守性の観点から望ましいですが、既存のデザインに準拠したい場合もあるかと思います。
この記事では、iOS13で従来のUISegmentedControlを表現する方法を紹介します。
実装方法

実装は、UISegementedControlを継承したサブクラスを作成して再現します。
import UIKit
class CustomSegmentedControl: UISegmentedControl {
private let color = UIColor.orange // 従来のtintColor
override init(items: [Any]?) {
super.init(items: items) // このときにoverride init(frame:_)も呼ばれる
}
override init(frame: CGRect) {
super.init(frame: frame)
initialize()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
initialize()
}
override func layoutSubviews() {
super.layoutSubviews()
// iOS12以前のスタイルにするための追加処理
if #available(iOS 13.0, *) {
layer.cornerRadius = 4.0
// 文字がセグメントいっぱいのときに見切れが発生するためAutoLayoutを調整
for subView in subviews {
for subsubView in subView.subviews {
guard let label = subsubView as? UILabel else { continue }
label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
label.leftAnchor.constraint(equalTo: subView.leftAnchor),
label.topAnchor.constraint(equalTo: subView.topAnchor),
label.rightAnchor.constraint(equalTo: subView.rightAnchor),
label.bottomAnchor.constraint(equalTo: subView.bottomAnchor),
])
}
}
// 選択状態のセグメントはタップしても反応しないようにする
let segmetens = subviews.filter { type(of: $0).description() == "UISegment" }
for segment in segments {
for segmentSubView in segment.subviews {
guard let label = segmentSubView as? UILabel else { continue }
if label.text == titleForSegment(at: selectedSegmentIndex) {
// 選択されたセグメントは選択不可
segment.isUserInteractionEnabled = false
// isUserInteractionEnabledにfalseを設定すると、設定した背景が効かなかったので背景色を指定
segment.backgroundColor = color
} else {
// 選択されていないセグメントは選択可能
segment.isUserInteractionEnabled = true
segment.backgroundColor = .clear
}
}
}
}
}
private func initialize() {
iOS12Style()
}
private func iOS12Style() {
if #available(iOS 13.0, *) {
let unselectedColor = UIColor.white
let highlightedColor = UIColor(white: 0.8, alpha: 0.2)
// 背景色
setBackgroundImage(color.rectImage(width: 1, height: 1), for: .selected, barMetrics: .default)
setBackgroundImage(unselectedColor.rectImage(width: 1, height: 1), for: .normal, barMetrics: .default)
setBackgroundImage(highlightedColor.rectImage(width: 1, height: 1), for: .highlighted, barMetrics: .default)
// 文字色
setTitleTextAttributes([.foregroundColor: unselectedColor], for: .selected)
setTitleTextAttributes([.foregroundColor: color], for: .normal)
// 境界部分
setDividerImage(color.rectImage(width: 1, height: 1), forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
// 枠
layer.borderWidth = 1
layer.borderColor = color.cgColor
} else {
tintColor = color
}
}
}
import UIKit
extension UIColor {
func rectImage(width: CGFloat, height: CGFloat) -> UIImage {
let rect = CGRect(x: 0, y: 0, width: width, height: height)
UIGraphicsBeginImageContext(rect.size)
let contextRef = UIGraphicsGetCurrentContext()
contextRef?.setFillColor(self.cgColor)
contextRef?.fill(rect)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img!
}
}