はじめに
UILabelを角丸にしたいのに角丸にならない!という経験をしたことある人、結構多いんじゃないでしょうか?
label.layer.cornerRadius = 10
この時以下のように、1行追加すると途端に角丸になります。
label.layer.cornerRadius = 10
label.clipsToBounds = true
clipsToBounds=true
とは一体何なんでしょうか?
UILabelとUIButtonの構造の違い
混乱の原因となっているのはUIButton
の挙動で、こっちは1行で角丸になります。
button.layer.cornerRadius = 10
ここで、UILabel
とUIButton
の構造をHierarchyを用いて観察してみます。
以下のように、layer.cornerRadius
のみ設定した状態だと画像のようになります。
label.layer.cornerRadius = 10
button.layer.cornerRadius = 10
やはりUILabel
だけうまく角丸になりません。
ここで実行中に以下のボタンをクリックすることで構造を見ることができます。
ここで大事なのは、UIButtonのclipsToBoundsもfalseであること。それは以下のように確認されます。
そもそもclipsToBoundsは何をしている?
公式ドキュメントでは、
A Boolean value that determines whether subviews are confined to the bounds of the view.
とあります。
ここでのbounds
とはViewの枠と捉えてよいので、(frameとは座標系の差しかない)、Viewの枠の外側に要素を描画するかどうかということになります。
外側にはみ出るとはどういう場合か、については、以下の記事が大変参考になります。
では、根本的な原因は何なのでしょうか。
構造が異なることが原因
ここで大事なのは、UILabelとUIButtonの層の数が異なるということです。
UIButton
はこのように3つの層からできており、一番後ろのView(UIButton)のSubviewとして_UISystemBackgroundView
とUILabel
があることが確認できます。
一方UILabel
は一つしか層がありません。
さて、ここで以下の2行を追加して、UILabel
のboundsを描画してみましょう。
label.layer.borderColor = CGColor(red: 1, green: 0, blue: 0, alpha: 1)
label.layer.borderWidth = 2.0
すると以下のようになります。
したがって、boundsはしっかり角丸になっているが、背景の要素がboundsをはみ出て描画されていることが原因だとわかります。
UIButtonが角丸になる原因の考察
UIKit
のパーツは内部構造がブラックボックス化されているため完全な理由は分かりませんが、Hierarchyからヒントをもらうことは可能です。
最下層のViewをクリックすると、以下のような情報が出てきます。
UILabel
UIButton
UIButton
はただのCALayer
で実装されていますが、UILabel
は特注の_UILabelLayer
なるもので実装されていることがわかります。
ここで、dump(button.layer)
などしてみる(printでも可)と、button.layer
でアクセスされるのはこのCALayer
であることがわかります。
したがって、
- UIButton.layer.cornerRadiusは最下層のlayerを角丸にするため、boundsどおり角丸になる
- UILabel.layer.cornerRaduisでは、_UILabelLayerで背景とboundsが別の層にあると考えられ、bounds通りに角丸にならない
と考えられます。
UILabelを角丸にする方法
原理を理解したところで、角丸にする方法をおさらいしましょう。
layerを角丸にする
label.layer.cornerRadius = 10
label.clipsToBounds = true
また、layerをboundsの形に合わせるという処理として、
label.layer.cornerRadius = 10
label.layer.masksToBounds = true
ともかけます。
まとめ
UIView
の内部構造が秘匿されているため完全に原因を究明できないのが残念ですが、Hierarchyを駆使すればここまで構造を把握することができます。
ここまで理解できたら、clipsToBoundsと書くのも苦じゃないですね!