관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] Shimmer 애니메이션 (CABasicAnimation, CAGradientLayer) 본문

iOS 응용 (swift)

[iOS - swift] Shimmer 애니메이션 (CABasicAnimation, CAGradientLayer)

jake-kim 2022. 4. 4. 23:48

shimmer 애니메이션

CABasicAnimation

https://developer.apple.com/documentation/quartzcore/cabasicanimation

  • layer에 관련된 싱글-키프레임 애니메이션
  • 모든 뷰들이 가지고 있는 CALayer에 애니메이션을 줄 수 있는 방법이며, 뷰를 이동시키거나, 흐리게하거나 등의 다양한 기능 구현 가능

cf) CAKeyFrameAnimation도 위 종류들을 모두 사용할 수 있고, 레이어의 여러 프레임에 대한 작업에 사용되고 CABasicAnimation은 단일 키프레임에 대한 애니메이션 기능을 제공

  • 사용 방법
    • layer에 CABasicAnimation 인스턴스를 추가하여 사용
self.someView.layer.add(animation, forKey: animation.keyPath)

Shimmer 구현 방법 (CABasicAnimation 사용)

  • UIView을 extension하여 사용
extension UIView {
  func animateShimmer() {
  }
}
  • shimmer에 사용할 layer가 필요하고, 이 layer는 gradient되어 있으므로 CAGradientLayer 생성하여 layer에 삽입
// in animateShimmer()

let gradientLayer = CAGradientLayer()
let gradationColor = [UIColor.clear, .white.withAlphaComponent(0.3), .clear]
gradientLayer.frame = self.bounds
gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
gradientLayer.colors = gradationColor.map { $0.cgColor }
gradientLayer.locations = [0.0, 0.5, 1.0]
self.layer.addSublayer(gradientLayer)
  • CABasicAnimaion인스턴스를 만들어서 위 layer에 추가
    • 주의: repeat 사이의 delay는 줄 수 없기 때문에, 만약 delay를 주고 싶은 경우 repeatCount=1로 하고, 외부에서 timer를 돌릴것
// in animateShimmer()

// keyPath: locations 속성 이용 (opacity, borderColor 등 종류가 다양)
let animation = CABasicAnimation(keyPath: "locations")

// 보간 사용 (0.0 ~ 1.0이 원래 layer 색깔이고 멀어질수록 보간법 적용)
animation.fromValue = [-0.7, -0.5, 0.0]
animation.toValue = [1.0, 1.3, 1.7]

animation.repeatCount = .infinity
animation.duration = 1.5

// timingFunction을 통해 easeIn, easeOut, linear 등 설정 가능
animation.timingFunction = CAMediaTimingFunction(name: .easeIn)

// layer에 애니메이션 추가
gradientLayer.add(animation, forKey: animation.keyPath)
  • 전체 코드
extension UIView {
  func animateShimmer() {
    let gradientLayer = CAGradientLayer()
    let gradationColor = [UIColor.clear, .white.withAlphaComponent(0.3), .clear]
    gradientLayer.frame = self.bounds
    gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
    gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
    gradientLayer.colors = gradationColor.map { $0.cgColor }
    gradientLayer.locations = [0.0, 0.5, 1.0]
    self.layer.addSublayer(gradientLayer)
    
    let animation = CABasicAnimation(keyPath: "locations")
    animation.fromValue = [-0.7, -0.5, 0.0]
    animation.toValue = [1.0, 1.3, 1.7]
    animation.repeatCount = .infinity
    animation.duration = 1.5
    animation.timingFunction = CAMediaTimingFunction(name: .easeIn)
    gradientLayer.add(animation, forKey: animation.keyPath)
  }
}

// 사용하는 쪽
self.sampleView.animateShimmer()

CABasicAnimation응용 - UIImageView 이동

UIImageView를 오른쪽으로 이동 (반복 시, 1초 딜레이)

  • 이번엔 gradientLayer인스턴스를 따로 생성하지 않고, 이미 존재하는 UIImage 인스턴스를 위처럼 이동되게끔 설정
  • 파란색 sampleView와 화살표 이미지인 imageView 준비
private lazy var sampleView: UIView = {
  let view = UIView()
  view.backgroundColor = .systemBlue
  view.translatesAutoresizingMaskIntoConstraints = false
  view.layer.cornerRadius = 27
  view.layer.cornerCurve = .continuous
  view.clipsToBounds = true
  self.view.addSubview(view)
  return view
}()
private lazy var imageView: UIImageView = {
  let view = UIImageView()
  view.image = UIImage(named: "right")
  view.translatesAutoresizingMaskIntoConstraints = false
  self.sampleView.addSubview(view)
  return view
}()
  • viewDidAppear에서 animate()를 사용
    • 1초 딜레이를 주기 위해서 Timer 사용
  override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    
    self.animate()
    let delay = 1.0
    DispatchQueue.main.asyncAfter(deadline: .now() + self.duration) {
      Timer.scheduledTimer(withTimeInterval: self.duration + delay, repeats: true) { timer in
        self.animate()
      }
    }
}
  • animate() 구현
    • CABasicAnimation의 "position.x" 속성을 이용하여 imageView를 이동
private func animate() {
  self.imageView.layer.removeAnimation(forKey: "myAnimation")
  let animation = CABasicAnimation(keyPath: "position.x")
  animation.fromValue = 0
  animation.toValue = self.sampleView.bounds.width
  animation.duration = self.duration
  animation.repeatCount = 1
  animation.timingFunction = CAMediaTimingFunction(name: .easeIn)
  self.imageView.layer.add(animation, forKey: "myAnimation")
}

* 전체 코드: https://github.com/JK0369/ExShimmerAnimation

 

* 참고

https://stackoverflow.com/questions/13913101/cabasicanimation-keys

https://developer.apple.com/documentation/quartzcore/cabasicanimation

https://medium.com/theengineeringgecko/shimmer-animations-in-ios-4c720089eaec

Comments