5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

iOS13のUISegmentedControlをiOS12の見た目に実装する

Last updated at Posted at 2019-12-05

##環境

  • Xcode11.2
  • Swift5.1

はじめに

今回のXcode11の登場で知らないうちに変わっているiOSの標準UIの中に UISegmentedControl があります。
世界ーのデザイン会社であるAppleのデザイナーとエンジニアが生み出した標準UIなので、1-2年以内にはこのUISegmentedControlがユーザにとっては標準になる事なのでしょう。

しかし、新しいデザインとはそれなりに古い認識や先入観を破壊してくるもの
既にストアに出ているアプリのテイストと必ず合うとは限りません。
仕事の関係上、ユーザの反応をきちんと伺うまで、なかなかXcode10.xから離れられないiOS開発者の方も居ると思います。

最初はUIの変更の程度なのでそこまで難しくないと考えていました。
しかし、普通にbackgroundColorやtintColorを指定するだけでは中々うまくいかず、すぐに終わるかと思いましたが結果2~3時間程の時間を要してしまいました。

間違っている場合、遠慮なくコメントを頂けたらと思います。

iOS13(Xcode11)からのUISegmentedControlのデザイン

スクリーンショット 2019-11-19 12 04 35

UIImageのextensionを追加

extension UIImage {
    convenience init?(color: UIColor, size: CGSize) {
        let rect = CGRect(origin: .zero, size: size)
        UIGraphicsBeginImageContextWithOptions(size, false, 1)
        color.setFill()
        UIRectFill(rect)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        guard let cgImage = image?.cgImage else { return nil }
        self.init(cgImage: cgImage)
    }
}

こちらこのままコピ&ペーストして頂いても動きます。
まずはUIImageを初期化する際に引数のcolorを指定してリサイズしてくれたUIImageを返してくれる拡張を追加します。

convenience initializerについては以下の記事をそのまま引用すると

convenience initializerは他のイニシャライザに処理を委譲(delegate)することができます。
つまりイニシャライザの中で別のイニシャライザを呼ぶことが出来ます。

[Swift] convenienceイニシャライザとdesignated(指定)イニシャライザ

UISegmentedControlのextensionを追加

extension UISegmentedControl {
    func iOS12Style() {
        if #available(iOS 13.0, *) {
            let unselectedTintImage = UIImage(color: .gray, size: CGSize(width: 1, height: 30))
            let selectedTintImage = UIImage(color: .blue, size: CGSize(width: 1, height: 30))
            let highlightImage = UIImage(color: UIColor.blue.withAlphaComponent(0.20), size: CGSize(width: 1, height: 30))

            // 選択されていない時のUISegmentedControlの背景
            setBackgroundImage(unselectedTintImage, for: .normal, barMetrics: .default)

            // 選択時のUISegmentedControlの背景
            setBackgroundImage(selectedTintImage, for: .selected, barMetrics: .default)

            // 選択中のUISegmentedControlのハイライト時の背景
            setBackgroundImage(highlightImage, for: .highlighted, barMetrics: .default)

            setTitleTextAttributes([.foregroundColor: UIColor.gray], for: .selected)
            setTitleTextAttributes([.foregroundColor: UIColor.blue], for: .normal)
            setDividerImage(selectedTintImage, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
        }
    }
}

// 選択中のUISegmentedControlのハイライト時の背景

こちらの説明が少し分かりにくいと思いますが、iOS13からsegmentを切り替えるトリガーが、タップ時に加えてスクロールに選択中のsegmentが追従してくるような仕様に変わりました。
その際の背景色(と言うか画像)が指定できるので、iOS12の仕様に合わせて少し薄めの背景色を設定してあげました。(これがないと結構お粗末な感じになるのであった方が個人的にはあった方が良いと思います)

結果 こんな感じになります

iOS12のデザインっぽくする事ができたのではないでしょうか??
スクリーンショット 2019-11-19 12 04 35

ソースコード全体

import UIKit

final class ViewController: UIViewController {

    @IBOutlet private weak var segment: UISegmentedControl!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        segment.iOS12Style()
    }
}

extension UIImage {
    convenience init?(color: UIColor, size: CGSize) {
        let rect = CGRect(origin: .zero, size: size)
        UIGraphicsBeginImageContextWithOptions(size, false, 1)
        color.setFill()
        UIRectFill(rect)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        guard let cgImage = image?.cgImage else { return nil }
        self.init(cgImage: cgImage)
    }
}

extension UISegmentedControl {
    func iOS12Style() {
        if #available(iOS 13.0, *) {
            let unselectedTintImage = UIImage(color: .gray, size: CGSize(width: 1, height: 30))
            let selectedTintImage = UIImage(color: .blue, size: CGSize(width: 1, height: 30))
            let highlightImage = UIImage(color: UIColor.blue.withAlphaComponent(0.20), size: CGSize(width: 1, height: 30))

            // 選択されていない時のUISegmentedControlの背景
            setBackgroundImage(unselectedTintImage, for: .normal, barMetrics: .default)

            // 選択時のUISegmentedControlの背景
            setBackgroundImage(selectedTintImage, for: .selected, barMetrics: .default)

            // 選択中のUISegmentedControlのハイライト時の背景
            setBackgroundImage(highlightImage, for: .highlighted, barMetrics: .default)

            setTitleTextAttributes([.foregroundColor: UIColor.gray], for: .selected)
            setTitleTextAttributes([.foregroundColor: UIColor.blue], for: .normal)
            setDividerImage(selectedTintImage, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
        }
    }
}

まとめ

いい感じに表現できたので良かったです。

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?