【Swift】UIBezierPathを使ってグラデーション付きの円弧を描く

![SS.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314373/081bfcd8-f196-8cef-afd5-1c809281de48.png)


UIBezierPathで描かれた図形にグラデーションを付ける方法が中々探しても見つからなかったので、備忘録も兼ねて投稿します。

##UIViewのサブクラスを作り、ストーリーボード上に設置してみる

```swift:GradientArcView.swift
@IBDesignable class GradientArcView: UIView {

}
```
`@IBDesignable`アノテーションを付与すると、ストーリーボードで作業する際にリアルタイムでレンダリングしてくれるので便利です。
当然ながら、動作は重たくなりますが……。

<img width="1040" alt="SS3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314373/9d599687-3a07-7507-0a26-d79de6d85dd0.png">

作成したカスタムViewにAspectRatio1:1の制約をかけて画面中央に配置しています。

##円弧を描く
UIViewのdraw(rect:)関数をオーバーライドして円弧を描きます。
この辺は詳しい記事がたくさんあるので簡単に。

```swift:GradientArcView.swift
override func draw(_ rect: CGRect) {
        let arcLayer = CAShapeLayer()
        let radius = rect.size.width / 2
        let lineWidth: CGFloat = 20
        let arcPath = UIBezierPath(arcCenter: CGPoint(x: rect.midX, y: rect.midY),
                               radius: radius - lineWidth,
                               startAngle: 0,
                               endAngle: CGFloat(Double.pi * 2),
                               clockwise: true)
        arcLayer.frame = rect
        arcLayer.path = arcPath.cgPath
        arcLayer.lineWidth = lineWidth
        arcLayer.fillColor = UIColor.clear.cgColor
        arcLayer.strokeColor = UIColor.red.cgColor
}
```
ここで`addSublayer()`すれば円弧が表示されますが、マスクしていくのでまだ追加しません

##円弧に重ねるためのグラデーションレイヤーを作る

```swift:GradientArcView.swift
        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = rect
        gradientLayer.colors = [
            UIColor.orange.cgColor,
            UIColor.red.cgColor,
            UIColor.purple.cgColor,
            UIColor.blue.cgColor
        ]
        gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
        gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
        self.layer.addSublayer(gradientLayer)

```

draw(rect:)関数の中に続けて書いていきます。
CAGradientLayerのstartPoint、endPointはこのような関係になっています
<img width="323" alt="SS4.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314373/c896a9dc-4c86-3bec-3add-a1f4d12ab39e.png">
左下が始点で右上が終点となるように書き換えてみます。

```swift:GradientArcView.swift
gradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.0)
```
![SS6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314373/19093e93-1cb4-5e35-4fd6-505aede2c59a.png)
色の重なり順が変わりました。

##グラデーションレイヤーでマスクする
```swift:.swift
gradientLayer.mask = arcLayer
```

<img width="926" alt="スクリーンショット 2019-06-18 18.00.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314373/a03923ce-58b2-c336-df65-c7a2fd7c44ef.png">

無事マスクされました。

##コード全文
```swift:GradientArcView.swift

import UIKit

@IBDesignable class GradientArcView: UIView {

    override func draw(_ rect: CGRect) {
        let arcLayer = CAShapeLayer()
        let radius = rect.size.width / 2
        let lineWidth: CGFloat = 20
        
        let arcPath = UIBezierPath(arcCenter: CGPoint(x: rect.midX, y: rect.midY),
                               radius: radius - lineWidth,
                               startAngle: 0,
                               endAngle: CGFloat(Double.pi * 2),
                               clockwise: true)
        arcLayer.frame = rect
        arcLayer.path = arcPath.cgPath
        arcLayer.lineWidth = lineWidth
        arcLayer.fillColor = UIColor.clear.cgColor
        arcLayer.strokeColor = UIColor.red.cgColor

        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = rect
        gradientLayer.colors = [
            UIColor.orange.cgColor,
            UIColor.red.cgColor,
            UIColor.purple.cgColor,
            UIColor.blue.cgColor
        ]
        gradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0)
        gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.0)
        self.layer.addSublayer(gradientLayer)
        
        gradientLayer.mask = arcLayer
    }
}
```

##参考
[Core Animation Framework Reference](https://developer.apple.com/documentation/quartzcore)