[英]How to make this floating views animation
How to make this floating views animation in swift uikit with uiviews如何使用 uiviews 在 swift uikit 中制作此浮动视图 animation
I have tried with this code and calling push function several times我已经尝试使用此代码并多次调用 push function
import UIKit进口 UIKit
lazy var collision: UICollisionBehavior = {
let collision = UICollisionBehavior()
collision.translatesReferenceBoundsIntoBoundary = true
collision.collisionDelegate = self
collision.collisionMode = .everything
return collision
}()
lazy var itemBehaviour: UIDynamicItemBehavior = {
let behaviou = UIDynamicItemBehavior()
behaviou.allowsRotation = false
return behaviou
}()
func addingPushBehaviour(onView view: UIDynamicItem ,animationAdded: Bool = false) {
let push = UIPushBehavior(items: [view], mode: .instantaneous)
push.angle = CGFloat(arc4random())
push.magnitude = 0.005
push.action = { [weak self] in
self?.removeChildBehavior(push)
}
addChildBehavior(push)
}
func addItem(withItem item: UIDynamicItem) {
collision.addItem(item)
itemBehaviour.addItem(item)
addingPushBehaviour(onView: item)
}
override init() {
super.init()
addChildBehavior(collision)
addChildBehavior(itemBehaviour)
}
var mainView: UIView?
convenience init(animator: UIDynamicAnimator , onView: UIView) {
self.init()
self.mainView = onView
animator.addBehavior(self)
}
Set up your views as square, using backroundColor and cornerRadius = height/2 to get the circular colored view look you show in your example.将您的视图设置为正方形,使用 backroundColor 和cornerRadius = height/2 以获得您在示例中显示的圆形彩色视图外观。
Create UIView animations using UIViewPropertyAnimator
with a several second duration, where you animate each of your views to a location that is a random distance from it's "anchor" location by < 16 pixels in each dimension.使用具有几秒钟持续时间的UIViewPropertyAnimator
创建 UIView 动画,其中您将每个视图动画到一个位置,该位置距其“锚”位置的随机距离在每个维度中 < 16 个像素。 (Google creating animations using UIViewPropertyAnimator
. You should be able to find plenty of examples online.) (谷歌使用UIViewPropertyAnimator
创建动画。你应该可以在网上找到很多例子。)
Correct me if I'm wrong, but it sounds like there are two tasks here: 1) randomly populate the screen with non-overlapping balls, and 2) let those balls float around such that they bounce off each other on collision.如果我错了,请纠正我,但听起来这里有两个任务:1)用不重叠的球随机填充屏幕,2)让这些球漂浮在周围,以便它们在碰撞时相互反弹。
If the problem is that the animations are jerky, then why not abandon UIDynamicAnimator
and write the physics from scratch?如果问题是动画很生涩,那么为什么不放弃UIDynamicAnimator
并从头开始编写物理呢? Fiddling with janky features in Apple's libraries can waste countless hours, so just take the sure-fire route.摆弄 Apple 库中的 janky 功能可能会浪费无数时间,所以请采取可靠的方法。 The math is simple enough, plus you can have direct control over frame rate.数学很简单,而且您可以直接控制帧速率。
Keep a list of the balls and their velocities:保留球及其速度的列表:
var balls = [UIView]()
var xvel = [CGFloat]()
var yvel = [CGFloat]()
let fps = 60.0
When creating a ball, randomly generate a position that does not overlap with any other ball:创建球时,随机生成一个不与任何其他球重叠的 position:
for _ in 0 ..< 6 {
let ball = UIView(frame: CGRect(x: 0, y: 0, width: ballSize, height: ballSize))
ball.layer.cornerRadius = ballSize / 2
ball.backgroundColor = .green
// Try random positions until we find something valid
while true {
ball.frame.origin = CGPoint(x: .random(in: 0 ... view.frame.width - ballSize),
y: .random(in: 0 ... view.frame.height - ballSize))
// Check for collisions
if balls.allSatisfy({ !doesCollide($0, ball) }) { break }
}
view.addSubview(ball)
balls.append(ball)
// Randomly generate a direction
let theta = CGFloat.random(in: -.pi ..< .pi)
let speed: CGFloat = 20 // Pixels per second
xvel.append(cos(theta) * speed)
yvel.append(sin(theta) * speed)
}
Then run a while
loop on a background thread that updates the positions however often you want:然后在后台线程上运行一个while
循环,以您想要的频率更新位置:
let timer = Timer(fire: .init(), interval: 1 / fps, repeats: true, block: { _ in
// Apply movement
for i in 0 ..< self.balls.count {
self.move(ball: i)
}
// Check for collisions
for i in 0 ..< self.balls.count {
for j in 0 ..< self.balls.count {
if i != j && self.doesCollide(self.balls[i], self.balls[j]) {
// Calculate the normal vector between the two balls
let nx = self.balls[j].center.x - self.balls[i].center.x
let ny = self.balls[j].center.y - self.balls[i].center.y
// Reflect both balls
self.reflect(ball: i, nx: nx, ny: ny)
self.reflect(ball: j, nx: -nx, ny: -ny)
// Move both balls out of each other's hitboxes
self.move(ball: i)
self.move(ball: j)
}
}
}
// Check for boundary collision
for (i, ball) in self.balls.enumerated() {
if ball.frame.minX < 0 { self.balls[i].frame.origin.x = 0; self.xvel[i] *= -1 }
if ball.frame.maxX > self.view.frame.width { self.balls[i].frame.origin.x = self.view.frame.width - ball.frame.width; self.xvel[i] *= -1 }
if ball.frame.minY < 0 { self.balls[i].frame.origin.y = 0; self.yvel[i] *= -1 }
if ball.frame.maxY > self.view.frame.height { self.balls[i].frame.origin.y = self.view.frame.height - ball.frame.height; self.yvel[i] *= -1 }
}
})
RunLoop.current.add(timer, forMode: .common)
Here are the helper functions:以下是辅助函数:
func move(ball i: Int) {
balls[i].frame = balls[i].frame.offsetBy(dx: self.xvel[i] / CGFloat(fps), dy: self.yvel[i] / CGFloat(fps))
}
func reflect(ball: Int, nx: CGFloat, ny: CGFloat) {
// Normalize the normal
let normalMagnitude = sqrt(nx * nx + ny * ny)
let nx = nx / normalMagnitude
let ny = ny / normalMagnitude
// Calculate the dot product of the ball's velocity with the normal
let dot = xvel[ball] * nx + yvel[ball] * ny
// Use formula to calculate the reflection. Explanation: https://chicio.medium.com/how-to-calculate-the-reflection-vector-7f8cab12dc42
let rx = -(2 * dot * nx - xvel[ball])
let ry = -(2 * dot * ny - yvel[ball])
// Only apply the reflection if the ball is heading in the same direction as the normal
if xvel[ball] * nx + yvel[ball] * ny >= 0 {
xvel[ball] = rx
yvel[ball] = ry
}
}
func doesCollide(_ a: UIView, _ b: UIView) -> Bool {
let radius = a.frame.width / 2
let distance = sqrt(pow(a.center.x - b.center.x, 2) + pow(a.center.y - b.center.y, 2))
return distance <= 2 * radius
}
https://imgur.com/a/SoJ0mcL https://imgur.com/a/SoJ0mcL
Let me know if anything goes wrong, I'm more than happy to help.如果出现任何问题,请告诉我,我非常乐意提供帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.