-
Notifications
You must be signed in to change notification settings - Fork 0
Description
문제
UIKit에서 버튼(DSButton) press시 color, scale 애니메이션 적용
UIKit에서 애니메이션을 다룰 수 있는 방법은 3개 UIView.animation, UIView.transition, CALayer-CABasicAnimiation.
이중 UIView.animation, UIView.transition은 하이레벨 CALayer-CABasicAnimiation은 로우레벨로, CALayer 사용시 애니메이션 지정이 복잡하다.
Try 1 - UIView.animation
UIView.animate(
withDuration: 0.35,
delay: 0,
usingSpringWithDamping: 0.8,
initialSpringVelocity: 0.8,
options: []
) {
self.transform = .init(scaleX: 0.9, y: 0.9)
self.backgroundColor = colorTheme.backgroundColor(state: .pressed).uiColor
}Simulator.Screen.Recording.-.iPhone.15.-.2024-06-18.at.15.01.10.mp4
scale애니메이션은 적용되나 컬러 애니메이션은 적용되지 않아 색상 변경이 끊어져서 진행됨.
Try2 - CALayer-CABasicAnimation
let caLayer = CALayer()
caLayer.backgroundColor = self.backgroundColor?.cgColor
caLayer.frame = self.bounds
self.layer.addSublayer(caLayer)
let colorAnimation = CABasicAnimation(keyPath: "backgroundColor")
colorAnimation.duration = 0.35
colorAnimation.fromValue = fromColor
colorAnimation.toValue = toColor
colorAnimation.isRemovedOnCompletion = false
colorAnimation.fillMode = .forwards
colorAnimation.repeatCount = 1
colorAnimation.delegate = LayerRemover(for: caLayer)
caLayer.add(colorAnimation, forKey: "backgroundColorChange")
// - support
class LayerRemover: NSObject, CAAnimationDelegate {
private weak var layer: CALayer?
init(for layer: CALayer) {
self.layer = layer
super.init()
}
func animationDidStart(_ anim: CAAnimation) {
print("start")
}
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
print("stop")
layer?.removeFromSuperlayer()
}
}Simulator.Screen.Recording.-.iPhone.15.-.2024-06-18.at.15.04.26.mp4
CALayer로 추가 시 컬러 애니메이션은 잘 되나, label의 텍스트 색상까지 변경시키는 문제 발생.
이에 더해서 translucent의 경우 opacity 컬러를 사용하기 때문에 CALayer가 계속 추가되면서 색상이 진해짐.
따라서 애니메이션 종료 후 레이어를 지워주기 위해 LayerRemover를 도입했으나 아직 press중인 상태임에도 애니메이션 완료로 calayer가 해제되면서 깜빡임 발생.
이후 포기..press와 animation complete시점을 어떻게 조정해야할지 모르겠음. 타이머 넣어서 하면 어떻게 될 것 같긴한데 너무 복잡.
Try3 - UIView.transition
// - beginTracking
UIView.transition(with: self, duration: 0.2, options: [.transitionCrossDissolve], animations: {
self.backgroundColor = colorTheme.backgroundColor(state: .pressed).uiColor
self.transform = .init(scaleX: 0.9, y: 0.9)
}) { (finish) in
}
// - endTracking
UIView.transition(with: self, duration: 0.2, options: [.curveEaseInOut], animations: {
self.backgroundColor = colorTheme.backgroundColor(state: .enabled).uiColor
self.transform = .identity
})[최종]
Simulator.Screen.Recording.-.iPhone.15.-.2024-06-18.at.15.33.00.mp4
transition에서만 사용 가능한 .transitionCrossDissolve옵션을 이용했더니 자연스럽게 동작.
➕
endTracking에 curveEaseInOut을 사용한 이유는 scale이 커질땐 외부까지 겹쳐보이게됨. 따라서 컬러 변경은 포기하고 모양만 애니메이션 적용.
Simulator.Screen.Recording.-.iPhone.15.-.2024-06-18.at.15.22.59.mp4
결론
try3로 완성.
하지만 SwiftUI에 비해 부드러움이 아쉬운 것은 사실이다... spring같은 보간 애니메이션을 사용해야할듯..
[SwiftUI Button]