(Swift) UIDynamic 예제

이전에 작성했던 UIDynamics 데모를 재작성했다.

  • swift3 문법을 적용했고,
  • 별도의 Xcode 프로젝트가 아니라 Playground 용으로 만들어서 바로 확인할 수 있게 했다.

UIDynamics를 적용하는 방법은 UIDynamicAnimator 객체를 만들고 여기에 애니메이션에 고려될 물리학적 요소 (중력이나 마찰등)를 behaivor로 설정해주면 된다. 애니메이터는 기준이 되는 레퍼런싱 뷰를 참조하면서 생성되고, 이후 해당 뷰 내의 뷰 계층 구조에 대해서 동역학을 계산하게 된다.




class PlayViewController: UIViewController {
    // 손을 끌고 다닐 뷰
    lazy var draggableView: UIView = {
        let v = UIView(frame: CGRect(x:40, y:40, width:50, height: 50))
        v.backgroundColor = UIColor.blue()
        return v
    }()

    // 이동가능한 뷰를 위한 제스쳐 인식
    lazy var pan: UIPanGestureRecognizer = { [unowned self] in 
        let _pan = UIPanGestureRecognizer(target: self, 
                                          action: #selector(self.panned(_:)))
        return _pan
    }()

    // UIDynamicAnimator는 느긋하게 초기화한다.
    lazy var animator: UIDynamicAnimator = { [unowned self] in
      let anim = UIDynamicAnimator(referencingView: self.view)
      return anim
    }()



    override func viewDidLoad() { 
        super.viewDidLoad()

        view.addSubview(draggableView)
        draggableView.addGestureRecognizer(pan)

        // 중력, 충돌 behavior 생성
        let gravity: UIGravityBehavior = { 
            let g = UIGravityBehavior(items: [draggableView])
            return g
        }()

        let collision: UICollisionBehavior = { 
            let c = UICollisionBehavior(items: [draggableView])
            c.translatesReferenceBoundsIntoBoundary = true
            c.collisionMode = .boundaries
            // 화면 바닥면에 
            let bottomCoord = view.bounds.size.height
            c.addBoundary(withIdentifier:"block", 
                          from: CGPoint(x:0, y:bottomCoord) 
                          to: CGPoint(x:view.bounds.size.width, y:bottomCoord))
            return c
        }()

        animator.addBehavior(gravity)
        animator.addBehavior(collision)
    }

    /// 패닝이 일어날 때마다 호출되는 핸들러 
    func panned(_ sender: UIPanGestureRecognizer) {
        // 터치가 끝날 때 애니메이터가 draggableView를 재계산에 포함하도록 한다.
        if (sender.state == .ended) {
            animator.updateItem(usingCurrentState: draggableView)
        }
        // 매 이동 순간의 offset을 계산하여 뷰의 위치를 바꾼다.
        let translation = sender.translation(in:view)
        if let sview = sender.view {
            sview.center = CGPoint(x: sview.center.x + translation.x,
                                   y: sview.center.y + translation.y)
            sender.setTranslation(CGPoint.zero)
        }
    }
}

let vc = PlayViewController()
PlaygroundPage.current.liveView = vc.view
PlaygroundPage.current.needsIndefiniteExecution = true