Created
June 14, 2019 01:13
-
-
Save p-larson/ca23d9596f2979effd9cc8fa9e73c893 to your computer and use it in GitHub Desktop.
OverlayLayer.swift Fix
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
// Bug Recreation Playground | |
// Peter Larson | |
// Util | |
extension UIColor { | |
func lighter(by percentage: CGFloat = 25.0) -> UIColor { | |
return self.adjust(by: abs(percentage) ) ?? self | |
} | |
func darker(by percentage: CGFloat = 25.0) -> UIColor { | |
return self.adjust(by: -abs(percentage)) ?? self | |
} | |
func adjust(by percentage: CGFloat = 30.0) -> UIColor? { | |
var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0 | |
if self.getRed(&red, green: &green, blue: &blue, alpha: &alpha) { | |
return UIColor( | |
red: min(red + percentage/100, 1.0), | |
green: min(green + percentage/100, 1.0), | |
blue: min(blue + percentage/100, 1.0), | |
alpha: alpha) | |
} else { | |
return nil | |
} | |
} | |
} | |
final public class OverlayLayer: CALayer { | |
@objc var color: UIColor = #colorLiteral(red: 0.9607843161, green: 0.7058823705, blue: 0.200000003, alpha: 1) { | |
didSet { | |
self.setNeedsDisplay() | |
} | |
} | |
@objc public var defaultShadowHeight: CGFloat = 12.0 | |
@objc public var shadowHeight: CGFloat = 12.0 | |
init(frame: CGRect, color: UIColor) { | |
super.init() | |
self.frame = frame | |
self.color = color | |
self.common() | |
} | |
public override init(layer: Any) { | |
super.init(layer: layer) | |
self.common() | |
guard let overlay = layer as? OverlayLayer else { | |
return | |
} | |
self.color = overlay.color | |
} | |
public required init?(coder aDecoder: NSCoder) { | |
super.init(coder: aDecoder) | |
self.common() | |
} | |
public override init() { | |
super.init() | |
self.common() | |
} | |
private func common() { | |
self.drawsAsynchronously = true | |
self.setNeedsDisplay() | |
} | |
public func animate(pressed: Bool, duration: TimeInterval = 0.5) { | |
let animation = CABasicAnimation(keyPath: #keyPath(OverlayLayer.shadowHeight)) | |
animation.toValue = pressed ? 0.0 : defaultShadowHeight | |
animation.fromValue = pressed ? shadowHeight : 0.0 | |
animation.timingFunction = CAMediaTimingFunction(name: .easeOut) | |
animation.duration = duration | |
self.shadowHeight = animation.toValue as! CGFloat | |
self.add(animation, forKey: "press") | |
} | |
override public class func needsDisplay(forKey key: String) -> Bool { | |
if key == #keyPath(OverlayLayer.shadowHeight) { | |
return true | |
} | |
return super.needsDisplay(forKey: key) | |
} | |
public override func draw(in ctx: CGContext) { | |
let shadowBackground = CGRect.init(origin: .zero, size: .init(width: frame.width, height: frame.height)) | |
let fillBackground = CGRect.init(origin: .zero, size: .init(width: frame.width, height: frame.height - shadowHeight)) | |
ctx.setFillColor(self.color.darker().cgColor) | |
ctx.addPath(UIBezierPath(roundedRect: shadowBackground, cornerRadius: cornerRadius).cgPath) | |
ctx.fillPath() | |
ctx.setFillColor(self.color.cgColor) | |
ctx.closePath() | |
ctx.beginPath() | |
ctx.addPath(UIBezierPath(roundedRect: fillBackground, cornerRadius: cornerRadius).cgPath) | |
ctx.fillPath() | |
super.draw(in: ctx) | |
} | |
} | |
final public class TestView: UIView { | |
public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { | |
overlayLayer?.animate(pressed: true) | |
} | |
public override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { | |
overlayLayer?.animate(pressed: false) | |
} | |
public required init?(coder aDecoder: NSCoder) { | |
super.init(coder: aDecoder) | |
} | |
public override init(frame: CGRect) { | |
super.init(frame: frame) | |
} | |
override public class var layerClass: AnyClass { | |
return OverlayLayer.self | |
} | |
public var overlayLayer: OverlayLayer? { | |
return layer as? OverlayLayer | |
} | |
public init(frame: CGRect, color: UIColor) { | |
super.init(frame: frame) | |
// color not being set in layer? | |
overlayLayer?.color = color | |
} | |
} | |
let view = TestView(frame: CGRect(origin: .zero, size: .init(width: 250, height: 250)), color: #colorLiteral(red: 0.3411764801, green: 0.6235294342, blue: 0.1686274558, alpha: 1)) | |
// The first display uses the correct color, but when the animation is run after being triggered by user interaction, | |
// it uses the default value instead of what should be the current value. | |
import PlaygroundSupport | |
PlaygroundPage.current.liveView = view |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment