관리 메뉴

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

[iOS - swift] 3. long press gesture와 애니메이션 - 드래그와 cornerRadius, shadow 효과 (CABasicAnimation) 본문

UI 컴포넌트 (swift)

[iOS - swift] 3. long press gesture와 애니메이션 - 드래그와 cornerRadius, shadow 효과 (CABasicAnimation)

jake-kim 2023. 7. 20. 00:50

1. long press gesture와 애니메이션 - 드래그 구현 방법 (snapshotView, CGAffineTransform)

2. long press gesture와 애니메이션 - 드래그할때 다른  줄어들고, 해당  크게하기 (UIView.animate, CGAffineTransform, concatenating)

3. long press gesture와 애니메이션 - 드래그와 cornerRadius, shadow 효과 (CABasicAnimation)

4. long press gesture와 애니메이션 - UIStackView에 DragDrop 적용 (DragDropStackView 구현)

5. long press gesture와 애니메이션 - gesture 도중 화면 끝으로 가면 자동으로 스크롤되는 기능 구현 (#Horizontal Scroll, #수평 스크롤)

 

뷰 준비

지금까지 구현된 부분 내용은 이전 포스팅 글 참고

  • long press gesture를 사용하여 began, changed, ended 상태에따라 애니메이션 처리
  • began 시점에 다른 뷰들은 Affine 변환을 통해 작게하고, 이동시킬 뷰는 크게하여 애니메이션 효과 적용
  • 뷰 이동은 Affine 변환 사용

drag 시작과 종료 시 뷰 사이즈 줄어들고 크게 효과 적용

drag 시작 시, 다른 뷰들에 shadow opacity 효과주기

  • 구현 아이디어
    • gesture의 began에서 CABasicAnimation(keyPath: "shadowOpacity")를 사용하여 그림자 효과를 적용
    • gesture의 ended에서 애니메이션 제거 (layer.removeAllAnimation())

아래 코드에서 prepareDragAnimation() 부분 아래에서 구현)

@objc private func handleLongPress(_ gesture: UILongPressGestureRecognizer) {
    switch gesture.state {
    case .began:
        handleBegan(gesture)
    case .changed:
        handleChanged(gesture)
    default:
        // ended, canceled, failed
        handleEnded(gesture)
    }
}

private func handleBegan(_ gesture: UILongPressGestureRecognizer) {
    ...
    
    UIView.animate(
        withDuration: 0.4,
        delay: 0,
        usingSpringWithDamping: 0.8,
        initialSpringVelocity: 0,
        options: [.allowUserInteraction, .beginFromCurrentState],
        animations: { self.prepareDragAnimation() },
        completion: nil
    )
}

private func prepareDragAnimation() {
    ...
    
    // TODO: shadow animation
}
  • CABasicAnimation(keyPath: "shadowOpacity")를 사용
    • opacity 0부터 (fromValue), 0.8 그림자가 생기도록 적용
// shadow animation
let shadowOpacityAnimation = CABasicAnimation(keyPath: "shadowOpacity")
shadowOpacityAnimation.fromValue = 0
shadowOpacityAnimation.toValue = 0.8
shadowOpacityAnimation.isRemovedOnCompletion = false
shadowOpacityAnimation.fillMode = .forwards

[otherView, anotherView]
    .forEach { subview in
        subview.layer.add(shadowOpacityAnimation, forKey: "animation_shadow_opacity")
    }
  • gesture의 ended 상태에서는 애니메이션을 제거
// shadow animation
[otherView, anotherView]
    .forEach { subview in
        UIView.animate(
            withDuration: 0.3,
            animations: {
                subview.layer.removeAllAnimations()
            }
        )
    }

shadow 효과 여러개 한꺼번에 주기

  • 위에까지 구현하면 shadow offset y 값이 상단으로 향해 있으므로, 자연스럽게 아래로 내려가도록 구현이 필요

  • CABasicAnimation(keyPath: "shadowOffset.height")를 사용
    • layer.add에 shadowOffset.height 애니메이션도 같이 삽입하면 완성
// shadow animation
let shadowOpacityAnimation = CABasicAnimation(keyPath: "shadowOpacity")
...

let shadowOffsetHeightAnimation = CABasicAnimation(keyPath: "shadowOffset.height")
shadowOffsetHeightAnimation.fromValue = 0
shadowOffsetHeightAnimation.toValue = 3
shadowOffsetHeightAnimation.isRemovedOnCompletion = false
shadowOffsetHeightAnimation.fillMode = .forwards

[otherView, anotherView]
    .forEach { subview in
        subview.layer.add(shadowOpacityAnimation, forKey: "animation_shadow_opacity")
        subview.layer.add(shadowOffsetHeightAnimation, forKey: "animation_shadow_offset_height")
    }
  • CABasicAnimation(keyPath: "shadowRadius")를 사용
    • 마찬가지로 layer.add에 shadowOffset.height 애니메이션도 같이 사용
// shadow animation
let shadowOpacityAnimation = CABasicAnimation(keyPath: "shadowOpacity")
...

let shadowOffsetHeightAnimation = CABasicAnimation(keyPath: "shadowOffset.height")
...

let shadowRadiusAnimation = CABasicAnimation(keyPath: "shadowRadius")
shadowRadiusAnimation.fromValue = 0
shadowRadiusAnimation.toValue = 8
shadowRadiusAnimation.isRemovedOnCompletion = false
shadowRadiusAnimation.fillMode = .forwards

[otherView, anotherView]
    .forEach { subview in
        subview.layer.add(shadowOpacityAnimation, forKey: "animation_shadow_opacity")
        subview.layer.add(shadowOffsetHeightAnimation, forKey: "animation_shadow_offset_height")
        subview.layer.add(shadowRadiusAnimation, forKey: "animation_shadow_radius")
    }

(완성)

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

Comments