UIkit Dynamics 投掷效果

UIkit Dynamics 投掷效果_第1张图片
图片来源于网络

前言:上章UIKit Dynamics 置身真实世界介绍了基本用法,下面我们继续深入学习——手势跟Dynamics结合的用法

一、触摸处理

1、在ViewController.swift添加以下属性,并在Main.storyboard结合这些属性,在Main.storyboard添加一个imageView,以及扮演redSquareblueSquare的俩个view

    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var redSquare: UIView!
    private var originalBounds = CGRect.zero
    private var orignalCenter = CGPoint.zero
    private var animator: UIDynamicAnimator!
    private var attachmentBehavior: UIAttachmentBehavior!
    private var pushBehavior: UIPushBehavior!
    private var itemBehavior: UIDynamicItemBehavior!

blueSquare将简单地表示您的触摸开始的位置,即您的手指首先与屏幕接触。redSquare会在您的手指移动时跟踪您的手指。
另外,在view添加一个手势识别器(Pan Gesture Recognizer),打开ViewController.swift并将此新方法添加到该文件中:

    @IBAction func handleAttachmentGesture(_ sender: UIPanGestureRcodeecognizer) {
        let location = sender.location(in: view)
        let boxLocation = sender.location(in: imageView)
        
        switch sender.state {
        case .began:
            print("Your touch start position is \(location)")
            print("Start location in image is \(boxLocation)")
        case .ended:
            print("Your touch end position is \(location)")
            print("End location in image is \(boxLocation)")
        default:
            break
        }

    }

在屏幕上滑动或者拖动下,会打印如下

Your touch start position is (73.0, 363.5)
Start location in image is (40.0, 226.5)
Your touch end position is (72.5, 363.0)
End location in image is (39.5, 226.0)

二、UIDynamicAnimator和UIAttachmentBehavior

设置完简单的UI,现在加上Dynamics,使其动态化
首先,我们得让imageView跟随我们的拖动而移动,用到Dynamics中的一个类--UIAttachmentBehavior

打开ViewController.swift并将以下代码放在viewDidLoad()下面

animator = UIDynamicAnimator(referenceView: view)
originalBounds = imageView.bounds
orignalCenter = imageView.center

上面的代码设置了一个UIDynamicAnimator——基于物理动画的UIKit引擎,将视图控制器的视图作为参考视图来定义animator的坐标系。
我们可以添加行为到animator,它允许你做很多事情例如:附着view,推动view,使他们受重力的影响,等等。

请在handleAttachmentGesture(sender:)中的case .began:两个print语句下方添加以下代码

    //1
    animator.removeAllBehaviors()
    //2
    let centerOffset = UIOffset(horizontal: boxLocation.x - imageView.bounds.midX, vertical: boxLocation.y - imageView.bounds.midY)
    attachmentBehavior = UIAttachmentBehavior(item: imageView, offsetFromCenter: centerOffset, attachedToAnchor: location)
    //3
    redSquare.center = attachmentBehavior.anchorPoint
    blueSquare.center = location
    //4
    animator.addBehavior(attachmentBehavior)

我们先来看看上面代码:

1、首先删除可能存在的任何现有的动画行为。
2、接下来,您创建一个UIAttachmentBehaviorimageView的点附加到用户点击锚点(恰好相同点)的位置。稍后,您将更改锚点,这将导致imageView移动。
将锚点连接到视图就像安装一个不可见的杆,将锚点连接到视图上的固定附件位置。
3、更新红色方块以指示锚点,蓝色方块表示imageView中附加的点。当手势开始时,这些将是相同的点。
4、将此行为添加到animator,使其生效。

接下来你需要告诉锚点本身跟随你的手指.将下列代码替换defaultbreak语句

  attachmentBehavior.anchorPoint = sender.location(in: view)
  redSquare.center = attachmentBehavior.anchorPoint

拖拽完之后,最好imageView可以回到初始位置,所以我们写一个方法func resetPosintion()

func resetPosintion() {
    animator.removeAllBehaviors()
    
    UIView.animate(withDuration: 0.45) { 
        self.imageView.bounds = self.originalBounds
        self.imageView.center = self.orignalCenter
        self.imageView.transform = CGAffineTransform.identity
    }
}

接着把 resetPosintion()放入handleAttachmentGesturecase .ended:print语句下面
效果如下:

UIkit Dynamics 投掷效果_第2张图片

但是很明显,我们一放开拖动, imageView马上回到原始位置,显然我们更希望手拖动后,存在惯性,还可以移动一段距离,为了解决这个问题,继续下面的学习

三、UIPushBehavior

在停止拖动时分离视图,并赋予动量,使其在运动时释放时可以继续其轨迹
首先,添加两个常量到顶部:

let ThrowingThreshold: CGFloat = 1000
let ThrowingVelocityPadding: CGFloat = 35

ThrowingThreshhold指示视图必须移动多快以使视图继续移动(而不是立即返回到原始位置)。ThrowingVelocityPadding是一个魔法常数,影响运动多快或者多慢(这是通过反复试验选择的)。

替换上面的case.end :resetPosintion()

// 1
let velocity = sender.velocity(in: view)
let magnitude = sqrt((velocity.x * velocity.x) + (velocity.y * velocity.y))

if magnitude > ThrowingThreshold {
    // 2
    let pushBehavior = UIPushBehavior (items: [imageView], mode: .instantaneous)
    pushBehavior.pushDirection = CGVector(dx: velocity.x / 10, dy: velocity.y / 10)
    pushBehavior.magnitude = magnitude / ThrowingVelocityPadding
    
    self.pushBehavior = pushBehavior
    animator.addBehavior(pushBehavior)
    
    // 3
    let angle = Int(arc4random_uniform(20)) - 10
    
    itemBehavior = UIDynamicItemBehavior(items: [imageView])
    itemBehavior.friction = 0.2
    itemBehavior.allowsRotation = true
    itemBehavior.addAngularVelocity(CGFloat(angle), for: imageView)
    animator.addBehavior(itemBehavior)
    
    // 4
    let timeOffset = Int64(0.4 * Double(NSEC_PER_SEC))
    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(timeOffset) / Double(NSEC_PER_SEC)) {
        self.resetPosition()
    }
} else {
  resetPosition()
}

我们先来看看这一节:

1、询问手势的拖动速度。
使用速度和你的老朋友毕达哥拉斯定理,你可以计算速度的大小 - 这是由x方向速度和y方向速度形成的三角形的斜边。

2、假设手势幅度超过为动作设置的最小阈值,则设置推送行为。
推动行为对指定的项目施加力。
在这种情况下,它是对图像的瞬时力量。
期望的方向由转换为给出方向部分的向量的x和y速度组成。
一旦设置了推动行为,就将其添加到动画序列中。

4、在指定的时间间隔之后,动画会通过将图像发送回目的地重置,因此它会拉出并返回屏幕 - 就像一个球从墙上弹起!
效果如下:

你可能感兴趣的:(UIkit Dynamics 投掷效果)