Auto Layoutで配置したUIViewにCAGradientLayerを使う時の注意点


- 開発環境：Xcode Ver8.0（macOS Sierra 10.12.1）
- 動作対象OS：iOS10.0
- 言語：swift3
- 前提条件：Auto Layout使用

Core AnimationのCAGradientLayerクラスを使って
StoryBoardにてAuto Layoutで配置したボタンを一色の表示から
2色のグラデーション表示にしたいのだけど
ボタンの表示が大きくなってしまうという問題が発生した
つまり

①　これを
![1.png](https://qiita-image-store.s3.amazonaws.com/0/143342/98463863-6d44-fed4-b35f-c0584c7dbf41.png "1.png")

②　こうしたい
![3.png](https://qiita-image-store.s3.amazonaws.com/0/143342/03c6cf49-c591-b0ae-05b8-19111693163f.png "3.png")

③　けどこうなった
![2.png](https://qiita-image-store.s3.amazonaws.com/0/143342/7face601-179a-7a7a-79f1-905bdad0e887.png "2.png")

③のように問題が発生するソースはこんな感じ
（以下、戻るボタンのCALayerに関するコードのみ記載、決定ボタンやその他のコードは省略）

```swift
    // 戻るボタン
    // →ボタンそのものの配置やサイズの指定はStoryBoardにてAuto Layoutで指定してある）
    // → StoryBoard側ではボタンの背景色はclearを指定してある
    @IBOutlet weak var cancelButton: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()

        // 戻るボタンのCALayerによる描画設定

        // 枠線や影の定義
        self.cancelButton.layer.cornerRadius = 8.0
        self.cancelButton.layer.borderWidth = 1.0
        self.cancelButton.layer.borderColor = UIColor.init(red: 1.000, green: 0.000, blue: 0.000, alpha: 1.0).cgColor
        self.cancelButton.layer.shadowOpacity = 0.5
        self.cancelButton.layer.shadowOffset = CGSize.init(width:2,height:2)

        // グラデーション（CAGradientLayer）の定義
        let cancelgradient = CAGradientLayer.init()
        cancelgradient.frame = self.cancelButton.bounds
        cancelgradient.cornerRadius = 8.0
        cancelgradient.borderWidth = 1.0
        cancelgradient.colors = [UIColor.init(red: 1.000, green: 0.000, blue: 0.000, alpha: 1.0).cgColor,
                                 UIColor.init(red: 0.859, green: 0.439, blue: 0.576, alpha: 1.0).cgColor]

        // CALayerにCAGradientLayerを載せる
        self.cancelButton.layer.insertSublayer(cancelgradient, at: 0)

        }
    }
```

下記コードで一色の時は①のようにサイズが変わりません

```swift
    override func viewDidLoad() {
        super.viewDidLoad()

        self.cancelButton.backgroundColor = UIColor.init(red: 0.859, green: 0.439, blue: 0.576, alpha: 1.0)

    }
```

なぜこのような問題が発生するのか最初はわからなかったけど
ある仮説をたてて下記のように書き換えたらうまく表示するようになった

```swift

    // 画面描画時の一瞬に表示される時の色としてグラデーションのうちの一色を通常のbackgroundColorとして指定しておく
    override func viewDidLoad() {
        super.viewDidLoad()

        self.cancelButton.backgroundColor = UIColor.init(red: 0.859, green: 0.439, blue: 0.576, alpha: 1.0)
    }

    // CALayerの描画をviewDidLoadではなくviewDidAppearで行う
    override func viewDidAppear(_ animated: Bool) {

        super.viewDidAppear(animated)

        self.cancelButton.layer.cornerRadius = 8.0
        self.cancelButton.layer.borderWidth = 1.0
        self.cancelButton.layer.borderColor = UIColor.init(red: 1.000, green: 0.000, blue: 0.000, alpha: 1.0).cgColor
        self.cancelButton.layer.shadowOpacity = 0.5
        self.cancelButton.layer.shadowOffset = CGSize.init(width:2,height:2)

        let cancelgradient = CAGradientLayer.init()
        cancelgradient.frame = self.cancelButton.bounds
        cancelgradient.cornerRadius = 8.0
        cancelgradient.borderWidth = 1.0
        cancelgradient.colors = [UIColor.init(red: 1.000, green: 0.000, blue: 0.000, alpha: 1.0).cgColor,
                                 UIColor.init(red: 0.859, green: 0.439, blue: 0.576, alpha: 1.0).cgColor]
        self.cancelButton.layer.insertSublayer(cancelgradient, at: 0)

    }
```

ViewControllerのライフサイクルとして
Auto Layoutで指定したボタンの描画の大きさの決定が

・viewDidLoadのタイミングでは
　　・CALayerに重ねるCAGradientLayerではなされておらず
　　・通常の背景色ではされている
　　
・viewDidAppearのタイミングでは
　　・CALayerに重ねるCAGradientLayerでされており
　　・通常の背景色の指定の時点でもされている

ということのようです。

なのでviewDidAppearでグラデーションの指定をすれば
ボタンは②のように適切なサイズでグラデーション表記できるになりました。

・・・が画面の描画が始まる一瞬だけボタンが表示されないので違和感が発生してしまうので
viewDidLoadでグラデーションの片方の色を通常のbackgroundColorで指定する事で
ほぼ違和感なくグラデーションのボタンを表示するようにしました。