Notice
Recent Posts
Recent Comments
Link
관리 메뉴

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

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

UI 컴포넌트 (swift)

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

jake-kim 2023. 7. 18. 01:52

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 후 드래그 앤 드롭

드래그 구현 아이디어

  • UILongPressGestureRecognizer를 뷰에 붙이기
  • began, changed, ended에서 드래그 앤 드롭 처리
  • 이동 시킬 뷰를 CGAffineTransform를 통해 바로 이동시키면, 실제로 frame이 이동되는게 아니기 때문에 이동 시킬 뷰와 동일한 뷰를 snapshotView를 통해 복사하여, 이 뷰를 CGAffineTransform를 통해 이동
    • began: snapshotView를 사용하여 이동시킬 뷰 캡쳐, 기존 뷰 alpha = 0 설정
    • changed: snapshotView를 CGAffineTransform으로 이동
    • ended: 기존 뷰 alpha = 1 설정, 기존 뷰의 frame을 snapshotView와 동일하게 설정

구현

  • 필요한 프로퍼티 선언
    • someView: 롱 프레스하여 이동시킬 뷰
    • snapshotedView: 복제할 뷰를 저장할 프로퍼티
    • isDragging: 현재 드래깅 되고 있는지 여부 프로퍼티
    • originalPosition: 롱 프레스하여 이동시킬 뷰의 드래그 하기 전의 위치
import UIKit

class ViewController: UIViewController {
    private let someView = UIView()
    private var snapshotedView: UIView?
    private var isDragging = false
    private var originalPosition = CGPoint.zero
}
  • 기본 뷰 레이아웃 설정
    override func viewDidLoad() {
        super.viewDidLoad()
        
        someView.backgroundColor = .green
        view.addSubview(someView)
        someView.frame = .init(x: 120, y: 120, width: 200, height: 200)
         
        // TODO: long press 붙이기
    }
  • long press 붙이기
    • 주의) delegate = self로 설정한 다음 gestureRecognizerShouldBegin 델리게이트 추가
    • 의미: 롱 프레스 후 드래그할 때 다른 제스쳐를 막기 위함
        let longPressGesture = UILongPressGestureRecognizer()
        longPressGesture.minimumPressDuration = 0.3
        longPressGesture.isEnabled = true
        longPressGesture.delegate = self
        longPressGesture.addTarget(self, action: #selector(handleLongPress))
        someView.addGestureRecognizer(longPressGesture)
        
...

extension ViewController: UIGestureRecognizerDelegate {
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        !isDragging
    }
}
  • long press 헨들러 추가
@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) {
    // TODO
}

private func handleChanged(_ gesture: UILongPressGestureRecognizer) {
    // TODO
}

private func handleEnded(_ gesture: UILongPressGestureRecognizer) {
    // TODO
}
  • handleBegan 구현
    • originalPosition 저장
    • snapshotView(afterScreenUpdates:)로 기존 뷰 캡쳐
    • 기존 뷰 가리기
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
}
  • handleChanged 구현
    • 위에서 저장한 originalPosition 값을 이용하여 이동된 offset을 구한 후 CGAffineTransform을 통해 좌표 이동
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
}
  • handleEnded 구현
    • 캡쳐된 뷰를 다시 지우고, 원본 뷰의 프레임 업데이트
private func handleEnded(_ gesture: UILongPressGestureRecognizer) {
    someView.frame = snapshotedView?.frame ?? .zero
    snapshotedView?.alpha = 0
    snapshotedView?.removeFromSuperview()
    someView.alpha = 1
}

(완료)

구현한 long press 후 드래그 앤 드롭

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

Comments