Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- rxswift
- 리펙터링
- Protocol
- Refactoring
- clean architecture
- Observable
- Xcode
- uiscrollview
- 스위프트
- 리팩토링
- 애니메이션
- Clean Code
- MVVM
- ios
- ribs
- tableView
- UITextView
- UICollectionView
- SWIFT
- RxCocoa
- HIG
- map
- 리펙토링
- collectionview
- swiftUI
- swift documentation
- combine
- Human interface guide
- uitableview
- 클린 코드
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 2. long press gesture와 애니메이션 - 드래그할때 다른 뷰 줄어들고, 해당 뷰 크게하기 (UIView.animate, CGAffineTransform, concatenating) 본문
UI 컴포넌트 (swift)
[iOS - swift] 2. long press gesture와 애니메이션 - 드래그할때 다른 뷰 줄어들고, 해당 뷰 크게하기 (UIView.animate, CGAffineTransform, concatenating)
jake-kim 2023. 7. 19. 00:121. 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 구현)
4. long press gesture와 애니메이션 - gesture 도중 화면 끝으로 가면 자동으로 스크롤되는 기능 구현 (#Horizontal Scroll, #수평 스크롤)
뷰 준비
- 3개의 뷰
- 하나의 뷰를 drag할 때, 해당 뷰를 크게하고 나머지 두 개의 뷰를 줄어들게하여 drag & drop 애니메이션
- 코드
- long gresture 등록 후 began, changed, default에서 각 처리
class ViewController: UIViewController {
private let someView = UIView()
private let otherView = UIView()
private let anotherView = UIView()
override func viewDidLoad() {
...
let longPressGesture = UILongPressGestureRecognizer()
longPressGesture.minimumPressDuration = 0.3
longPressGesture.isEnabled = true
longPressGesture.delegate = self
longPressGesture.addTarget(self, action: #selector(handleLongPress))
someView.addGestureRecognizer(longPressGesture)
}
@objc private func handleLongPress(_ gesture: UILongPressGestureRecognizer) {
switch gesture.state {
case .began:
handleBegan(gesture)
case .changed:
handleChanged(gesture)
default:
// ended, canceled, failed
handleEnded(gesture)
}
}
- hnaldeBegan, handleChanged, handleEnded 코드
- 뷰를 이동시키는 코드 개념은 이전 포스팅 글 참고
private func handleBegan(_ gesture: UILongPressGestureRecognizer) {
originalPosition = gesture.location(in: view)
snapshotedView = someView.snapshotView(afterScreenUpdates: true)
snapshotedView?.frame = someView.frame
view.addSubview(snapshotedView!)
someView.alpha = 0
}
private func handleChanged(_ gesture: UILongPressGestureRecognizer) {
let newLocation = gesture.location(in: view)
let xOffset = newLocation.x - originalPosition.x
let yOffset = newLocation.y - originalPosition.y
let translation = CGAffineTransform(translationX: xOffset, y: yOffset)
snapshotedView?.transform = translation
}
private func handleEnded(_ gesture: UILongPressGestureRecognizer) {
someView.frame.origin = snapshotedView?.frame.origin ?? .zero
snapshotedView?.alpha = 0
snapshotedView?.removeFromSuperview()
someView.alpha = 1
}
애니메이션 적용 아이디어
- .began에서 이동시킬 뷰의 크기를 키우기 (UIView.animate, transform)
- .changed에서 이동시킬 뷰의 크기와 위치 동시에 바꾸어주기 (concatenating, transform)
구현
- UIView.animate를 사용하여 affine 변환이 자연스럽게 동작하도록 적절한 durration과 spring damping을 추가
- CGAffineTransform의 메소드인 concatenating()을 사용하면 동시에 여러가지 affine 변환이 가능
- 드래그 시작 시 드래그 시킬 뷰에 효과주기 (concatenating으로 합치기)
- 이동시킬 뷰의 스캐일을 키우기 (CGAffineTransform(scalex:y:))
- 이동시킬 뷰의 y 위치를 조금 내리기 (CGAffineTransform(translationX:y:))
- 나머지 뷰들의 크기를 줄이기 (CGAffineTransform(scaleX:y:))
private func handleBegan(_ gesture: UILongPressGestureRecognizer) {
originalPosition = gesture.location(in: view)
snapshotedView = someView.snapshotView(afterScreenUpdates: true)
snapshotedView?.frame = someView.frame
view.addSubview(snapshotedView!)
someView.alpha = 0
// Animation - 다른 뷰는 줄어들게하고, 이동시킬 뷰는 크게하기
UIView.animate(
withDuration: 0.4,
delay: 0,
usingSpringWithDamping: 0.8,
initialSpringVelocity: 0,
options: [.allowUserInteraction, .beginFromCurrentState],
animations: { self.prepareDragAnimation() },
completion: nil
)
}
private func prepareDragAnimation() {
let upScale = 1.2
let scaleTranasform = CGAffineTransform(scaleX: upScale, y: upScale)
let downYPosition = 10.0
let translationTransform = CGAffineTransform(translationX: 0, y: downYPosition)
snapshotedView?.transform = scaleTranasform.concatenating(translationTransform)
snapshotedView?.alpha = 0.9
let downScale = 0.8
[otherView, anotherView]
.forEach { subview in
subview.transform = CGAffineTransform(scaleX: downScale, y: downScale)
}
}
- .changed 될때 크기를 키운 snapshotedView가 다시 줄어들게 되므로 스캐일 업 하는 코드 추가
- 스캐일을 업하는 코드와 동시에 이동되어야 하므로 둘 다 affine 변환이기 때문에 concatenating 사용하여 동시에 적용되도록 구현
private func handleChanged(_ gesture: UILongPressGestureRecognizer) {
let newLocation = gesture.location(in: view)
let xOffset = newLocation.x - originalPosition.x
let yOffset = newLocation.y - originalPosition.y
let translationTransform = CGAffineTransform(translationX: xOffset, y: yOffset)
// Animation - 업스케일 된 이동 시킬 뷰를 계속 업스케일된 상태로 유지하기
let upScale = 1.2
let scaleTranasform = CGAffineTransform(scaleX: upScale, y: upScale)
snapshotedView?.transform = translationTransform.concatenating(scaleTranasform)
}
- .ended에서 원래의 뷰 보여지게하고, 나머지 뷰들은 다시 scale 1로 되돌리기
private func handleEnded(_ gesture: UILongPressGestureRecognizer) {
someView.frame.origin = snapshotedView?.frame.origin ?? .zero
snapshotedView?.alpha = 0
snapshotedView?.removeFromSuperview()
someView.alpha = 1
// Animation - drag 시작할때 적용한 애니메이션 되돌리기
UIView.animate(
withDuration: 0.4,
delay: 0,
usingSpringWithDamping: 0.8,
initialSpringVelocity: 0,
options: [.allowUserInteraction, .beginFromCurrentState],
animations: { self.animateEndDrop() },
completion: { _ in
// Hide the temporaryView, show the actualView
self.snapshotedView?.removeFromSuperview()
self.someView.alpha = 1
}
)
}
func animateEndDrop() {
snapshotedView?.transform = .identity
snapshotedView?.alpha = 1.0
[someView, otherView, anotherView]
.forEach { subview in
UIView.animate(
withDuration: 0.3,
animations: {
subview.transform = .identity
}
)
}
}
(완료)
* 보완하면 좋은 부분: drag 시작, drop 종료 할 때 뷰에 shadow 효과를 주고 radius도 변경해주면 더욱 자연스러운 애니메이션 효과가 가능 (다음 포스팅 글에서 계속)
* 전체 코드
'UI 컴포넌트 (swift)' 카테고리의 다른 글
Comments