まず始めに
Pathをアニメーションで描画するのではなく、パーセンテージで描画する方法について書こうと思います。
パーセンテージ指定でPathの描画をさせると、以下のGIFアニメーションのようにスライダーと連動して描画できるようになったりもします。
どのように実装しているのか
実装する上でのポイントは以下のようになります。
-
CAShapeLayer
のインスタンスをUIView
のdrawRect
で描画する -
CAShapeLayer
のstrokeEnd
を使ってパーセンテージ指定できるようにする
class PathStokeAdjustableView: UIView {
private let shapeLayer = CAShapeLayer()
private var _stroke: CGFloat = 0 {
didSet {
shapeLayer.strokeEnd = _stroke
}
}
var stroke: CGFloat {
set {
_stroke = max(0, min(1, newValue))
}
get {
return _stroke
}
}
//MARK: - XibやStroyboardから呼ばれた時
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
layer.addSublayer(shapeLayer)
shapeLayer.strokeEnd = 0
layoutIfNeeded()
}
override func layoutSubviews() {
super.layoutSubviews()
setNeedsDisplay()
}
//MARK: - コードで生成された時
init() {
super.init(frame: .zero)
layer.addSublayer(shapeLayer)
shapeLayer.strokeEnd = 0
}
override init(frame: CGRect) {
super.init(frame: frame)
layer.addSublayer(shapeLayer)
shapeLayer.strokeEnd = 0
}
//MARK: - Pathの描画
override func drawRect(rect: CGRect) {
UIColor.whiteColor().setFill()
shapeLayer.lineWidth = 10
shapeLayer.strokeColor = UIColor.blackColor().CGColor
shapeLayer.fillColor = UIColor.whiteColor().CGColor
shapeLayer.path = path().CGPath
}
}
extension PathStokeAdjustableView {
func path() -> UIBezierPath {
let path = UIBezierPath()
path.moveToPoint(CGPoint(x: CGRectGetMinX(bounds), y: CGRectGetMinY(bounds)))
path.addLineToPoint(CGPoint(x: CGRectGetMaxX(bounds), y: CGRectGetMinY(bounds)))
path.addLineToPoint(CGPoint(x: CGRectGetMaxX(bounds), y: CGRectGetMaxY(bounds)))
path.addLineToPoint(CGPoint(x: CGRectGetMinX(bounds), y: CGRectGetMaxY(bounds)))
path.addLineToPoint(CGPoint(x: CGRectGetMinX(bounds), y: CGRectGetMinY(bounds)-5))
return path
}
}
Storyboard
にはUIView
とUISlider
を適当に追加してください。
この後、PathStokeAdjustableView
をStoryboard
の中で指定するか、ViewController
の中でaddSubView
するかによって書き方が少し変わってきます。
Storyboard
で指定する場合
以下のスクリーンショットのように、追加したUIView
のCustom ClassをPathStokeAdjustableView
に指定してください。
ViewController
自体の実装はSlider
の値を受け取ったとき、PathStokeAdjustableView
のインスタンスにstroke
の値として受け取ったものを渡すだけです。
class ViewController: UIViewController {
@IBOutlet weak var pathView: PathStokeAdjustableView!
@IBAction func sliderValueChanged(sender: AnyObject) {
guard let slider = sender as? UISlider else {
return
}
pathView.stroke = CGFloat(slider.value)
}
}
ViewController
で生成する場合
-
Storyboard
で追加したUIView
のCustom Classは変更せず、UIView
のままにしてください。 - 上記の
UIView
をpathContainerView
として使用します。 -
PathStokeAdjustableView
のインスタンスを生成して、AutoLayout
などを使用しpathContainerView
にaddSubview
してください。 -
stroke
へ値を渡す実装に違いはありません。
class ViewController: UIViewController {
@IBOutlet weak var pathContainerView: UIView!
private let pathView = PathStokeAdjustableView()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
pathView.translatesAutoresizingMaskIntoConstraints = false
pathContainerView.addSubview(pathView)
pathContainerView.addConstraints([
NSLayoutConstraint(item: pathView, attribute: .Top, relatedBy: .Equal, toItem: pathContainerView, attribute: .Top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: pathView, attribute: .Right, relatedBy: .Equal, toItem: pathContainerView, attribute: .Right, multiplier: 1, constant: 0),
NSLayoutConstraint(item: pathView, attribute: .Left, relatedBy: .Equal, toItem: pathContainerView, attribute: .Left, multiplier: 1, constant: 0),
NSLayoutConstraint(item: pathView, attribute: .Bottom, relatedBy: .Equal, toItem: pathContainerView, attribute: .Bottom, multiplier: 1, constant: 0)
])
}
@IBAction func sliderValueChanged(sender: AnyObject) {
guard let slider = sender as? UISlider else {
return
}
pathView.stroke = CGFloat(slider.value)
}
}
注意点
Pathのstroke
には対応していますが、fill
には対応していません。
最後に
Pathをパーセンテージ指定して描画できるようにすることによって、UISlider
の値に合わせるだけでなく、UITableView
などのスクロールに合わせて描画したりもできるようになります。