dodo849 / DesignSystemBookApp

Expandable design system template for UIKit and SwiftUI
2 stars 0 forks source link

UIKit: Button animation #3

Open dodo849 opened 5 months ago

dodo849 commented 5 months ago

문제

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
  }

https://github.com/dodo849/DesignSystemBookApp/assets/71880682/fd443f00-ee9a-4054-8418-adea10f062b4

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()
    }
}

https://github.com/dodo849/DesignSystemBookApp/assets/71880682/49351c79-b2a3-44cf-81a3-e324d2108485

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
   })

[최종]

https://github.com/dodo849/DesignSystemBookApp/assets/71880682/4c3986b6-d493-4b56-a4c8-581925552afb

transition에서만 사용 가능한 .transitionCrossDissolve옵션을 이용했더니 자연스럽게 동작.

➕ endTracking에 curveEaseInOut을 사용한 이유는 scale이 커질땐 외부까지 겹쳐보이게됨. 따라서 컬러 변경은 포기하고 모양만 애니메이션 적용.

https://github.com/dodo849/DesignSystemBookApp/assets/71880682/ade8530c-b50f-4af2-8e1b-879d80dc34f1

결론

try3로 완성. 하지만 SwiftUI에 비해 부드러움이 아쉬운 것은 사실이다... spring같은 보간 애니메이션을 사용해야할듯..

[SwiftUI Button]

https://github.com/dodo849/DesignSystemBookApp/assets/71880682/fbe8cdc6-683e-453e-a1f5-f1f9ab89849d

dodo849 commented 4 months ago

배경 view를 따로 깔아서 해볼까..?