上拉和放下 UIView 并改变 x 位置
Pull up and drop UIView with a change in the x position
编辑
这是我想要可视化的(忽略丑陋的红线,它只是表示 UIView 的移动):
我想要一个在屏幕中间初始化的 UIView。在那之后,我想向上推它,重力将它拉下来,直到它离开屏幕。我的老问题适用于 UIPushBehaviour、UIDynamicBehaviour 和 UIGravityBehaviour(见下文)。 Matt 指出 UIPushBehaviour 可能不是正确的选择,因为它在 iOS.
上可用的每个屏幕尺寸上都不能很好地工作
我可以用 UIView.animate 函数做到这一点,但它确实是静态的,看起来不自然。使用 UIPushBehaviour、UIDynamicBehaviour 和 UIGravityBehaviour,它看起来非常好,但是 UIPushBehaviour 的大小不能在每个屏幕尺寸上计算,以给出 UIView 的 x 和 y 位置的相同终点。
问题
如何在屏幕中间初始化一个 UIView,'pull up' 那个 UIView(x 位置有一些变化)并让重力(或其他东西)将它拉下来直到它关闭屏幕?重要的是 x 和 y 位置的变化在每个屏幕尺寸上都是相同的。
下面是我的老问题
我有一个 UIPushBehaviour
和 instantaneous
作为我推动一些 UIView
的模式。屏幕尺寸越大,推送越少。
我还有一个 UIDynamicItemBehavior
,resistance
设置为 1
,我认为这是每个屏幕尺寸不同的主要原因之一(如果我错了请纠正我)。
我想要一个函数,它将 UIView
推到相同的终点,无论屏幕大小如何,速度、持续时间和终点都相同。
我试着做了一个相对星等,但运气不佳:
对于 iPhone 5S,假设 magnitude
of 0.5
会从中间到顶部接触 UIView
。我想像这样计算所有设备的幅度:
let y = 0.5 / 520 // 5S screen height
magnitude = self.view.frame.height * y
对于 iPhone 8,它的输出非常不同并且无法正常工作。读the docs的时候,我以为我会明白的。我以为1个星等代表100个像素,但显然不是这样。
有什么方法可以计算幅度,例如,将 UIView
从中间向右移动?
我做了a project here。有一个黑色 UIView
在 iPhone 5 上被推到边缘,但在 iPhone 8.
上没有
解决方案
您需要根据屏幕大小缩放推送量,以便您的视图始终在同一个位置结束。为此,调整 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 将视图的大小视为其质量。如果您的视图需要动态调整大小,则需要根据动画视图的大小缩放常量。
编辑对原始问题的评论:
不同大小的视图:就像我在上面的注释中提到的,您需要应用一个额外的因素来考虑 "mass" 的视图。 view.frame.height * view.frame.width * someConstant
之类的应该可以正常工作。
iPad 屏幕尺寸:目前 pushFactor
应用于向量的 dx
和 dy
分量。因为 iPad 有不同的纵横比,你需要把它分成两个常量,可能是 xPushFactor
和 yPushFactor
,这可以解释纵横比的差异。
例子
iPhone 8
iPhone SE
完整的游乐场源代码
将此代码复制并粘贴到 Swift 游乐场中以查看实际效果。我已经包含了各种 iPhone 屏幕的尺寸,因此只需取消注释您想要在不同设备尺寸上轻松测试动画的尺寸。大多数 interesting/relevant 代码在 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
编辑
这是我想要可视化的(忽略丑陋的红线,它只是表示 UIView 的移动):
我想要一个在屏幕中间初始化的 UIView。在那之后,我想向上推它,重力将它拉下来,直到它离开屏幕。我的老问题适用于 UIPushBehaviour、UIDynamicBehaviour 和 UIGravityBehaviour(见下文)。 Matt 指出 UIPushBehaviour 可能不是正确的选择,因为它在 iOS.
上可用的每个屏幕尺寸上都不能很好地工作我可以用 UIView.animate 函数做到这一点,但它确实是静态的,看起来不自然。使用 UIPushBehaviour、UIDynamicBehaviour 和 UIGravityBehaviour,它看起来非常好,但是 UIPushBehaviour 的大小不能在每个屏幕尺寸上计算,以给出 UIView 的 x 和 y 位置的相同终点。
问题
如何在屏幕中间初始化一个 UIView,'pull up' 那个 UIView(x 位置有一些变化)并让重力(或其他东西)将它拉下来直到它关闭屏幕?重要的是 x 和 y 位置的变化在每个屏幕尺寸上都是相同的。
下面是我的老问题
我有一个 UIPushBehaviour
和 instantaneous
作为我推动一些 UIView
的模式。屏幕尺寸越大,推送越少。
我还有一个 UIDynamicItemBehavior
,resistance
设置为 1
,我认为这是每个屏幕尺寸不同的主要原因之一(如果我错了请纠正我)。
我想要一个函数,它将 UIView
推到相同的终点,无论屏幕大小如何,速度、持续时间和终点都相同。
我试着做了一个相对星等,但运气不佳:
对于 iPhone 5S,假设 magnitude
of 0.5
会从中间到顶部接触 UIView
。我想像这样计算所有设备的幅度:
let y = 0.5 / 520 // 5S screen height
magnitude = self.view.frame.height * y
对于 iPhone 8,它的输出非常不同并且无法正常工作。读the docs的时候,我以为我会明白的。我以为1个星等代表100个像素,但显然不是这样。
有什么方法可以计算幅度,例如,将 UIView
从中间向右移动?
我做了a project here。有一个黑色 UIView
在 iPhone 5 上被推到边缘,但在 iPhone 8.
解决方案
您需要根据屏幕大小缩放推送量,以便您的视图始终在同一个位置结束。为此,调整 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 将视图的大小视为其质量。如果您的视图需要动态调整大小,则需要根据动画视图的大小缩放常量。
编辑对原始问题的评论:
不同大小的视图:就像我在上面的注释中提到的,您需要应用一个额外的因素来考虑 "mass" 的视图。
view.frame.height * view.frame.width * someConstant
之类的应该可以正常工作。iPad 屏幕尺寸:目前
pushFactor
应用于向量的dx
和dy
分量。因为 iPad 有不同的纵横比,你需要把它分成两个常量,可能是xPushFactor
和yPushFactor
,这可以解释纵横比的差异。
例子
iPhone 8
iPhone SE
完整的游乐场源代码
将此代码复制并粘贴到 Swift 游乐场中以查看实际效果。我已经包含了各种 iPhone 屏幕的尺寸,因此只需取消注释您想要在不同设备尺寸上轻松测试动画的尺寸。大多数 interesting/relevant 代码在 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