解决方案
您需要相对于屏幕大小缩放推送量,以便您的视图始终在同一个位置结束。为此,调整UIPushBehavior
'pushDirection
向量效果很好。在这种情况下,我将推动方向设置为与视图的边界成比例,并将其缩小一个常数因子。
let push = UIPushBehavior(items: [pushView], mode: .instantaneous)
let pushFactor: CGFloat = 0.01
push.pushDirection = CGVector(dx: -view.bounds.width * pushFactor, dy: -view.bounds.height * pushFactor)
animator.addBehavior(push)
您可能需要调整一些常量以获得所需的确切动画。您可以调整的常数是:
- 重力大小(目前为 0.3)
- 推动因子(目前为 0.01)
根据您的需要,您可能还需要根据屏幕大小按比例缩放重力大小。
注意:这些常量需要根据动画视图的大小进行更改,因为 UIKit Dynamics 将视图的大小视为其质量。如果您的视图需要动态调整大小,则需要根据动画视图的大小缩放常量。
编辑对原始问题的评论:
不同大小的视图:就像我在上面的注释中提到的那样,您需要应用一个额外的因素来解释视图的“质量”。类似的东西view.frame.height * view.frame.width * someConstant
应该很好用。
iPad 屏幕尺寸:目前pushFactor
适用于矢量的dx
和dy
组件。因为 iPad 具有不同的纵横比,您需要将其拆分为两个常数,也许xPushFactor
和yPushFactor
,这可以解释纵横比的差异。
例子
iPhone 8
iPhone SE
完整的游乐场源代码
将此代码复制并粘贴到 Swift Playground 中以查看它的运行情况。我已经包含了各种 iPhone 屏幕的尺寸,因此只需取消注释您想要在不同设备尺寸上轻松测试动画的尺寸。大多数有趣/相关的代码都在viewDidAppear
.
import UIKit
import PlaygroundSupport
class ViewController: UIViewController {
let pushView = UIView()
var animator: UIDynamicAnimator!
override func viewDidLoad() {
super.viewDidLoad()
view.frame = CGRect(x: 0, y: 0, width: 568, height: 320) // iPhone SE
// view.frame = CGRect(x: 0, y: 0, width: 667, height: 375) // iPhone 8
// view.frame = CGRect(x: 0, y: 0, width: 736, height: 414) // iPhone 8+
// view.frame = CGRect(x: 0, y: 0, width: 812, height: 375) // iPhone X
view.backgroundColor = .white
let pushViewSize = CGSize(width: 200, height: 150)
pushView.frame = CGRect(x: view.bounds.midX - pushViewSize.width / 2, y: view.bounds.midY - pushViewSize.height / 2, width: pushViewSize.width, height: pushViewSize.height)
pushView.backgroundColor = .red
view.addSubview(pushView)
animator = UIDynamicAnimator(referenceView: self.view)
let dynamic = UIDynamicItemBehavior()
dynamic.resistance = 1
animator.addBehavior(dynamic)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let gravity = UIGravityBehavior(items: [pushView])
gravity.magnitude = 0.3
animator.addBehavior(gravity)
let push = UIPushBehavior(items: [pushView], mode: .instantaneous)
let pushFactor: CGFloat = 0.01
push.pushDirection = CGVector(dx: -view.bounds.width * pushFactor, dy: -view.bounds.height * pushFactor)
animator.addBehavior(push)
}
}
let vc = ViewController()
PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = vc.view