|
import UIKit |
|
import PlaygroundSupport |
|
|
|
class MyViewController: UIViewController { |
|
|
|
let button = ButtonWithShapeLayer() |
|
private var buttonHeightConstraint: NSLayoutConstraint! |
|
|
|
override func viewDidLoad() { |
|
super.viewDidLoad() |
|
view.backgroundColor = .lightGray |
|
setupButton() |
|
} |
|
|
|
private func setupButton() { |
|
button.backgroundColor = .yellow |
|
button.translatesAutoresizingMaskIntoConstraints = false |
|
button.addTarget(self, action: #selector(onButtonTap), for: .touchUpInside) |
|
view.addSubview(button) |
|
|
|
buttonHeightConstraint = button.heightAnchor.constraint(equalToConstant: 100.0) |
|
NSLayoutConstraint.activate([ |
|
button.centerYAnchor.constraint(equalTo: view.centerYAnchor), |
|
button.centerXAnchor.constraint(equalTo: view.centerXAnchor), |
|
buttonHeightConstraint, |
|
button.widthAnchor.constraint(equalTo: button.heightAnchor) |
|
]) |
|
} |
|
|
|
@objc private func onButtonTap() { |
|
let currentHeight = buttonHeightConstraint.constant |
|
buttonHeightConstraint.constant = currentHeight == 100.0 ? 200.0 : 100.0 |
|
UIView.animate(withDuration: 1.0) { |
|
self.view.layoutIfNeeded() |
|
} |
|
} |
|
} |
|
|
|
class ButtonWithShapeLayer: UIButton { |
|
|
|
private let shapeLayer = CAShapeLayer() |
|
private var isShapeLayerAdded = false |
|
private var oldFrame = CGRect.zero |
|
|
|
override func layoutSubviews() { |
|
super.layoutSubviews() |
|
if !isShapeLayerAdded { |
|
addShapeLayer() |
|
} else if oldFrame != frame { |
|
animateShapeLayer() |
|
} |
|
oldFrame = frame |
|
} |
|
|
|
private func animateShapeLayer() { |
|
let animation = CABasicAnimation(keyPath: "path") |
|
animation.fromValue = getBezierPath(forFrame: oldFrame) |
|
animation.toValue = getBezierPath(forFrame: frame) |
|
animation.duration = 1.0 |
|
animation.fillMode = kCAFillModeForwards |
|
animation.isRemovedOnCompletion = false |
|
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) |
|
|
|
shapeLayer.add(animation, forKey: "path") |
|
} |
|
|
|
private func getBezierPath(forFrame frame: CGRect) -> CGPath { |
|
let halfBeneathFill = UIBezierPath() |
|
halfBeneathFill.move(to: CGPoint(x: 0, y: frame.height / 2)) |
|
halfBeneathFill.addCurve( |
|
to: CGPoint(x: frame.width, y: frame.height / 2), |
|
controlPoint1: CGPoint(x: frame.width / 10, y: frame.height / 2 + frame.height / 10), |
|
controlPoint2: CGPoint(x: frame.width, y: frame.height / 2 + frame.height / 10) |
|
) |
|
halfBeneathFill.addLine(to: CGPoint(x: frame.width, y: frame.height)) |
|
halfBeneathFill.addLine(to: CGPoint(x: 0, y: frame.height)) |
|
halfBeneathFill.addLine(to: CGPoint(x: 0, y: frame.height / 2)) |
|
return halfBeneathFill.cgPath |
|
} |
|
|
|
private func addShapeLayer() { |
|
isShapeLayerAdded = true |
|
shapeLayer.path = getBezierPath(forFrame: frame) |
|
shapeLayer.fillColor = UIColor.red.cgColor |
|
shapeLayer.opacity = 0.3 |
|
layer.addSublayer(shapeLayer) |
|
} |
|
} |
|
|
|
PlaygroundPage.current.liveView = MyViewController() |