Swift

iOS13のUISegmentedControlを、iOS12以前のスタイルで再現する方法

2020-03-15

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!
    }
}

-Swift

Copyright© てくてくライフ , 2020 All Rights Reserved.