はじめに
iPhone には様々なアプリケーションがあり、時に変わったデザインをしたものを見ることがあります。
その中でも TabBar
のカスタムデザインはたくさんあり、アプリケーションを利用する上で目にする機会が多い UI
だと思います。
そこで、今回は中央に UIButton
がある TabBar
を作成していきたいと思います。
環境
- Xcode 12.4
- Swift 5.3.2
完成図
![]() |
---|
解説
この手順で解説していきたいと思います。
-
UIView
を継承したCustomBar
クラスを作る。-
UIBezierPath
を用いてCustomBar
となる図形を描画する。
-
-
UIView
を継承したCustomTabBar
クラスを作る-
CustomTabBar
クラスにUIButton
を追加する。
-
UIView を継承したクラス CustomBar を作る。
draw(_rect:)
は View
を描画する時、View
の幅と高さを指定することができるメソッドです。
ここに UIBezierPath
を使い図形を描画していきます。 (完成図推奨)
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
を追加します。
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 を作成
無事に図形の描画が行えているはずなので、確認して見たいと思います。
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
を追加します。
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
の追加も出来たので最後にアプリを起動してみて問題がないか確認してみましょう。
問題がなければ、完成図のようになっていると思います。
ソースコード (全文)
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()
}
}
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")
}
}
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