iPhone XR, XS, XS Maxにも対応しました。
僕はもともとWeb Applicationの開発をしてた人なので、スマホアプリのUIもWebViewを使って書いていました。(正確に言うとIonicとかOnsenUIとか) Web UIでも工夫次第ではネイティブUIにひけをとらないのですが、アニメーションやデバイスセンサを扱う部分など、やはりネイティブにはかないません。
そこで、iOSのネイティブUI(UIKit)でフロントを作ろうとしたのですが「Reactヤベェ!Angularヤベェ」とWebの世界でいきっていた人にとっては辛い例があったので備忘録代わりにまとめました。
UITabBarのアイコンのタイトルを消したい
iOS標準のTabBarはこんな感じですよね。アイコンの下にタイトルがあるオーソドックスなUIです。
しかし、InstagramのTabBarのようにアイコンのみのシンプルなものが欲しい場合もあるでしょう。
最初はdisableLabel = trueのようなプロパティをfalseにすることで消えるかなっと楽観的に考えていたのですが、どうやらない!
https://developer.apple.com/documentation/uikit/uitabbaritem を見るとtitleというgetter setterプロパティがありました。
ということで、titleをoverrideしちゃいましょう。するとラベルが消えますがアイコンの位置がずれてしまうのでimageInsetsもoverrideして位置を調整します。
import UIKit
class CustomTabBarItem: UITabBarItem {
override var title: String? {
get {
return nil
}
set {
super.title = title
}
}
override var imageInsets: UIEdgeInsets {
get {
return UIEdgeInsets(top: 5, left: 0, bottom: -5, right: 0)
}
set {
super.imageInsets = imageInsets
}
}
}
iOS11ではUIEdgeInsetsを調整してもアイコンが天地中央にならない!
こんな感じです。要はtop, bottom, right, left全て0にすると言うことです。
#availableを使ってiOS11のみoffsetを0にすることでiOS11以下のバージョンもサポートすることができます。
class CustomTabBar: UITabBar {
override func layoutSubviews() {
super.layoutSubviews()
var offset: CGFloat = 6.0
if #available(iOS 11.0, *), traitCollection.horizontalSizeClass == .regular {
offset = 0.0
}
let imageInset = UIEdgeInsets(
top: offset,
left: 0.0,
bottom: -offset,
right: 0.0
)
for tabBarItem in items ?? [] {
tabBarItem.title = ""
tabBarItem.imageInsets = imageInset
}
}
UITabBarの高さを変更したい
TabBarの高さをカスタマイズするにはUITabBarの継承クラスを作りfunc sizeThatFitsをoverrideします。
この時に注意したいのがiPhone XやXR, XSという変な画面の余計な端末素晴らしく画期的な端末向けのレイアウトです。今までホームボタンがあった場所が画面になっているのでその分の高さを取得してTabBarの高さに加えてやらないと、TabBarがiPhone Xの丸っこいエッジに落ちて悲惨なことになります。
追記
self.safeAreaInsets.bottomでセーフエリアの下の高さを取得することができますが、この値はviewWillLayoutSubviews が実行された後に更新されます。つまり、viewWillLayoutSubviewsこれが実行される前は0が返ってくるのでUIApplication.shared.windows[0].safeAreaInsets.bottomを用います。
import UIKit
class CustomUITabBar: UITabBar {
// ここでTabBarの高さを設定
let tabBarHeight: CGFloat = 70
override func sizeThatFits(_ size: CGSize) -> CGSize {
super.sizeThatFits(size)
let safeAreaBottom = UIApplication.shared.windows[0].safeAreaInsets.bottom
var size = super.sizeThatFits(size)
if #available(iOS 11.0, *) {
size.height = safeAreaBottom + tabBarHeight
} else {
size.height = tabBarHeight
}
return size
}
}
アイコンのカラーを変えたいけどRGB表記だるいわ
iOS界隈では割と一般的な工夫かもしれませんが、UIColorを指定したい場合はUIColor(red:0.20, green:0.20, blue:0.20, alpha:1.0)のようにRGB + Alphaで指定しなければなりません。僕みたいにWeb作ってたマンからすると面倒で仕方ない。CSS(まぁSass使いますけど)みたいに#333333と16進で指定したいところです。
実装としてはUIColorを拡張してinitializerにhexとalphaの引数を持たせて内部で通常のRGBに直します。ポイントはhexをStringとしてとることです。数値型だと0xを先頭につけなければなりません。CSSいじってたマンにとっては0xFFFFFFなんてちょっとキモイですね。
import UIKit
extension UIColor {
convenience init(hex: String, alpha: CGFloat = 1.0) {
if hex.count == 6 {
let red: CGFloat = CGFloat((rgb & 0xFF0000) >> 16) / 255.0
let green: CGFloat = CGFloat((rgb & 0x00FF00) >> 8) / 255.0
let blue: CGFloat = CGFloat(rgb & 0x0000FF) / 255.0
}
self.init(red: red, green: green, blue: blue, alpha: alpha)
}
}