Skip to content

Instantly share code, notes, and snippets.

@TomaszPietrowski
Created January 13, 2018 16:25
Show Gist options
  • Save TomaszPietrowski/445517130b07ffa7f9202644c2260aec to your computer and use it in GitHub Desktop.
Save TomaszPietrowski/445517130b07ffa7f9202644c2260aec to your computer and use it in GitHub Desktop.
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()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment