それはグラデーションを扱ってるときに起きました
どうもIBで作ったサイズのまま変わらないでグラデーションがかかっているのです・・・
どんな状況?
まずは下の記事を参考にGradationView
なるものを作りました。
Swiftで背景にグラデーションを設定する
import UIKit
class GradationView: UIView {
@IBInspectable var topColor: UIColor = .clear
@IBInspectable var bottomColor: UIColor = .white
func setGradation() {
let gradientLayer = CAGradientLayer()
gradientLayer.frame.size = self.frame.size
gradientLayer.colors = [self.topColor.cgColor, self.bottomColor.cgColor]
self.layer.addSublayer(gradientLayer)
}
}
そしてIB上でGradationView
を配置し、IBOutletでVCに繋いで、setGradation()
が呼ばれるようにします。
制約は画面幅いっぱいになるようにつけています。高さは適当に固定しています。
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var gradationView: GradationView! {
willSet {
newValue.topColor = .red
newValue.bottomColor = .blue
newValue.setGradation()
}
}
...
}
これをまずはIB上のVCと同じ画面サイズの端末で実行してみます。
想定通り表示されました。
次に、IB上のVCより大きい画面サイズの端末で実行してみます。
グラデーションが途中で切れてしまいました!View Hierarchyを見てみるとビュー自体は幅が伸びていることがわかります。
なぜ起こるの?
タイトルの通りですが、addSublayerで追加されたCALayerはビューのサイズ変更に追従してくれないみたいです。
ビューはAutoLayoutによって端末サイズに合わせてサイズが変更されますが、CALayerはそのまま残ります。
対応策
ググってみると3通り対応策がありました。
CALayer And Auto Layout With Swift
詳細は上の記事を見てもらいたいのですが、ざっくりまとめると以下の通りです。
- AutoLayoutの制約が効いたあとでCALayerの
frame
を更新する - ビューの
bounds
をKVOで監視し、変更があったらCALayerのframe
を更新する - ビューのlayerをCAGradientLayerにしてしまう
今回私は#3の対応策を取ることにしました。
関心のあるCALayerが1つの場合はこれで対応できそうです。もし、複数ある場合は#1または#2で対応することになると思います。
import UIKit
class GradationView: UIView {
@IBInspectable var topColor: UIColor = .clear
@IBInspectable var bottomColor: UIColor = .white
override class var layerClass: AnyClass {
return CAGradientLayer.self
}
func setGradation() {
guard let gradientLayer = self.layer as? CAGradientLayer else {
return
}
gradientLayer.colors = [self.topColor.cgColor, self.bottomColor.cgColor]
}
}
これで、無事グラデーションがビューのサイズ変更に追従するようになってくれました。
まとめ
addSublayerで追加されたCALayerはビューのサイズ変更に追従してくれない。
関心のあるCALayerが1つだけの場合は、ビューのlayer自体を関心のあるCALayerに置き換えてしまうと良い。
関心のあるCALayerが複数の場合は、ビューのサイズ変更が起きた後のタイミングでCALayerのサイズ変更を行う。
終わりに
最初はなんで自動で変わってくれないんだよ、と思いましたが、よくよく考えてみればaddSubViewしたビューが親ビューにサイズ追従しないのと同じような話なんですかね
しかしframeをいじるしかサイズ変更できないというのはイカしてないと思うので、レイヤーにもAutoLayoutがつけられるようになったらいいんじゃないかなーと思います
なんだかAutoLayoutを使い始めた頃を思い出させる現象でした