1
2

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.

[Swift] カスタムコンポーネントを使用した TabBar

Last updated at Posted at 2021-02-11

はじめに

iPhone には様々なアプリケーションがあり、時に変わったデザインをしたものを見ることがあります。
その中でも TabBar のカスタムデザインはたくさんあり、アプリケーションを利用する上で目にする機会が多い UI だと思います。

そこで、今回は中央に UIButton がある TabBar を作成していきたいと思います。

環境

  • Xcode 12.4
  • Swift 5.3.2

完成図

Completed image

解説

この手順で解説していきたいと思います。

  • UIView を継承した CustomBar クラスを作る。
    • UIBezierPath を用いて CustomBar となる図形を描画する。
  • UIView を継承した CustomTabBar クラスを作る
    • CustomTabBar クラスに UIButton を追加する。

UIView を継承したクラス CustomBar を作る。

draw(_rect:)View を描画する時、View の幅と高さを指定することができるメソッドです。
ここに UIBezierPath を使い図形を描画していきます。 (完成図推奨)

CustomBar.swift

final class CustomBar: UIView {

    let barHeight:CGFloat = 48

    // イニシャライザ
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        backgroundColor = .clear
    }
    
    // 必須イニシャライザ
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func draw(_ rect: CGRect) {
        let screenWidth:CGFloat = UIScreen.main.bounds.width
        let screenHeight:CGFloat = UIScreen.main.bounds.height
        let bottomInset = UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0

        let path = UIBezierPath()
        
        // 円弧の中央となる値
        let center = CGPoint(x: screenWidth / 2, y: screenHeight - (barHeight + bottomInset))
        // 円の半径
        let radius: CGFloat = 35
        // 円弧の始まりの位置
        let startAngle: CGFloat = CGFloat(Double.pi) * 2 * 180 / 360
        // 円弧の終わりの位置
        let endAngle: CGFloat = CGFloat(Double.pi) * 2 * 360 / 360

        // 円弧を描く (clockwise: は true = 時計回り, false = 反時計周り)
        path.addArc(withCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: false)
        
        // 円弧の終わりから右上の角に向かって線を引く
        path.addLine(to: CGPoint(x: screenWidth, y: screenHeight - (barHeight + bottomInset)))
        //  右上の角から右下の角に線を引く
        path.addLine(to: CGPoint(x: screenWidth, y: screenHeight))
        //  右下の角から左下の角に線を引く
        path.addLine(to: CGPoint(x: 0, y: screenHeight))
        // 左下の角から左上の角に線を引く
        path.addLine(to: CGPoint(x: 0, y: screenHeight - (barHeight + bottomInset)))

        // 描画を終了する
        path.close

        // 図の塗りつぶし (注意:内側を塗りつぶすわけではない。)
        // 上記の手順で図を描画すれば問題ない
        // 手順によっては外側が塗りつぶされる
        UIColor().white.setFill()
        path.fill()
    }

}

UIView を継承した CustomTabBar クラスを作る

先に作成した CustomBar を追加します。

CustomTabBar.swift

class CustomTabBar: UIView {

     let customBar: Bar = {
        let bar = Bar()
        bar.layer.shadowColor = UIColor.black.cgColor
        bar.layer.shadowOffset = CGSize(width: 0, height: 8)
        bar.layer.shadowRadius = 8
        bar.layer.shadowOpacity = 0.26
        return bar
    }()
    
    // イニシャライザ
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        backgroundColor = .clear
        
        // draw(rect:) で描画サイズは指定しているので view 自体のサイズは全画面を指定します。
        customBar.frame = UIScreen.main.bounds
        addSubview(customBar)
    }
    
    // 必須イニシャライザ
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

RootViewController を作成

無事に図形の描画が行えているはずなので、確認して見たいと思います。

RootViewController.swift

class RootViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // view の背景色
        self.view.background = UIColor(red: 86/255, green: 36/255, blue:228/255)
        
        // CustomTabBar の生成
        let customTabBar = CustomTabBar(frame: self.view.bounds)
        self.view.addSubview(customTabBar)
    }
}

期待通り TabBar が表示されました👏
後は中央に UIButton を追加すれば完成ですね。

UIButton を追加

CustomTabBar クラスに UIButton を追加します。

CustomTabBar.swift

class CustomTabBar: UIView {
    
    // 中央のボタン
    let middleButton: UIButton = {
        let button = UIButton(type: .custom)
        button.backgroundColor = .rgb(red: 86, green: 36, blue: 228)
        button.tintColor = .white
        // ボタンで利用したい機能を示す画像を設定
        button.setImage(#imageLiteral(resourceName: "add"), for: .normal)
        button.layer.cornerRadius = 56 / 2
        button.layer.shadowColor = UIColor.black.cgColor
        button.layer.shadowOffset = CGSize(width: 0, height: 6)
        button.layer.shadowRadius = 6
        button.layer.shadowOpacity = 0.26
        return button
    }()

    let customBar: Bar = {
        let bar = Bar()
        bar.layer.shadowColor = UIColor.black.cgColor
        bar.layer.shadowOffset = CGSize(width: 0, height: 8)
        bar.layer.shadowRadius = 8
        bar.layer.shadowOpacity = 0.26
        return bar
    }()
    
    // イニシャライザ
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        backgroundColor = .clear
        
        // draw(rect:) で描画サイズは指定しているので view 自体のサイズは全画面を指定します。
        customBar.frame = UIScreen.main.bounds
        addSubview(customBar)

        let width:CGFloat = UIScreen.main.bounds.width
        let height:CGFloat = UIScreen.main.bounds.height
        let bottomInset = UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0
        
        let middleButtonWidthHeight: CGFloat = 56
        let middleButtonRadius: CGFloat = middleButtonWidthHeight / 2
        
        let x: CGFloat = (width / 2) - middleButtonRadius
        let y: CGFloat = height - (customBar.barHeight + bottomInset + middleButtonRadius)
        middleButton.frame = CGRect(x: x, y: y, width: middleButtonWidthHeight, height: middleButtonWidthHeight)
        addSubview(middleButton)
    }
    
    // 必須イニシャライザ
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

UIButton の追加も出来たので最後にアプリを起動してみて問題がないか確認してみましょう。
問題がなければ、完成図のようになっていると思います。

ソースコード (全文)

CustomBar.swift

final class CustomBar: UIView {

    let barHeight:CGFloat = 48

    // イニシャライザ
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        backgroundColor = .clear
    }
    
    // 必須イニシャライザ
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func draw(_ rect: CGRect) {
        let screenWidth:CGFloat = UIScreen.main.bounds.width
        let screenHeight:CGFloat = UIScreen.main.bounds.height
        let bottomInset = UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0

        let path = UIBezierPath()
        
        // 円弧の中央となる値
        let center = CGPoint(x: screenWidth / 2, y: screenHeight - (barHeight + bottomInset))
        // 円の半径
        let radius: CGFloat = 35
        // 円弧の始まりの位置
        let startAngle: CGFloat = CGFloat(Double.pi) * 2 * 180 / 360
        // 円弧の終わりの位置
        let endAngle: CGFloat = CGFloat(Double.pi) * 2 * 360 / 360

        // 円弧を描く (clockwise: は true = 時計回り, false = 反時計周り)
        path.addArc(withCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: false)
        
        // 円弧の終わりから右上の角に向かって線を引く
        path.addLine(to: CGPoint(x: screenWidth, y: screenHeight - (barHeight + bottomInset)))
        //  右上の角から右下の角に線を引く
        path.addLine(to: CGPoint(x: screenWidth, y: screenHeight))
        //  右下の角から左下の角に線を引く
        path.addLine(to: CGPoint(x: 0, y: screenHeight))
        // 右下の角から左上の角に線を引く
        path.addLine(to: CGPoint(x: 0, y: screenHeight - (barHeight + bottomInset)))

        // 描画を終了する
        path.close

        // 図の塗りつぶし (注意:内側を塗りつぶすわけではない。)
        // 上記の手順で図を描画すれば問題ない
        // 手順によっては外側が塗りつぶされる
        UIColor().white.setFill()
        path.fill()
    }

}

CustomTabBar.swift

class CustomTabBar: UIView {
    
    // 中央のボタン
    let middleButton: UIButton = {
        let button = UIButton(type: .custom)
        button.backgroundColor = .rgb(red: 86, green: 36, blue: 228)
        button.tintColor = .white
        // ボタンで利用したい機能を示す画像を設定
        button.setImage(#imageLiteral(resourceName: "add"), for: .normal)
        button.layer.cornerRadius = 56 / 2
        button.layer.shadowColor = UIColor.black.cgColor
        button.layer.shadowOffset = CGSize(width: 0, height: 6)
        button.layer.shadowRadius = 6
        button.layer.shadowOpacity = 0.26
        return button
    }()

    let customBar: Bar = {
        let bar = Bar()
        bar.layer.shadowColor = UIColor.black.cgColor
        bar.layer.shadowOffset = CGSize(width: 0, height: 8)
        bar.layer.shadowRadius = 8
        bar.layer.shadowOpacity = 0.26
        return bar
    }()
    
    // イニシャライザ
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        backgroundColor = .clear
        
        // draw(rect:) で描画サイズは指定しているので view 自体のサイズは全画面を指定します。
        customBar.frame = UIScreen.main.bounds
        addSubview(customBar)

        let width:CGFloat = UIScreen.main.bounds.width
        let height:CGFloat = UIScreen.main.bounds.height
        let bottomInset = UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0
        
        let middleButtonWidthHeight: CGFloat = 56
        let middleButtonRadius: CGFloat = middleButtonWidthHeight / 2
        
        let x: CGFloat = (width / 2) - middleButtonRadius
        let y: CGFloat = height - (customBar.barHeight + bottomInset + middleButtonRadius)
        middleButton.frame = CGRect(x: x, y: y, width: middleButtonWidthHeight, height: middleButtonWidthHeight)
        addSubview(middleButton)
    }
    
    // 必須イニシャライザ
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

RootViewController.swift

class RootViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // view の背景色
        self.view.background = UIColor(red: 86/255, green: 36/255, blue:228/255)
        
        // CustomTabBar の生成
        let customTabBar = CustomTabBar(frame: self.view.bounds)
        self.view.addSubview(customTabBar)
    }
}

さいごに

今回は特殊なデザインの TabBar をカスタムコンポーネントで作成してみました。
どうだったでしょうか?
既存の UI も良いですが、このように少し変わった UI に興味を持っていくのも良いと思います。

おまけ

CustomTabBar クラスに追加した UIButton の処理は Protocol・Delegate を使うと良いと思います。
既存の UITabBar のように左右に Button を付けたい場合は CustomTabBar クラスに追加すると良いと思います。

参考文献

draw(_:) | Apple Developer Documentation
UIBezierPath | Apple Developer Documentation

1
2
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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?