如何按顺序为 Swift 中的图像视图数组制作动画?
How do you sequentially animate an array of image views in Swift?
我想弄清楚这个问题已经有一段时间了,但无济于事。基本上,我有一个 UIImageViews 数组,我想按顺序逐一设置动画。现在,它们都正确但同时进行了动画处理。此外,我希望底部的代码仅在所有动画完成后执行,因为现在它似乎是同时执行的。我是 swift 的新手,所以我想我不完全理解 for 循环是如何工作的?在我看来,这段代码 应该 循环遍历数组中的所有图像视图,只有在它完成后才应该执行底部的代码。应在循环的每次迭代中调用 highlightCards 方法,但这似乎也没有发生。我知道随着移动正确的图像,阵列已正确启动。
代码如下:
playedCards 是一个带有卡片索引的整数数组
game.board是卡片对象数组(UIImageView)
highlightCards() 只是高亮当前可玩的牌
完成块中的代码仅用于后勤,我知道它工作正常
"set up new game"注释下的代码是所有动画完成后才应该执行的代码
for i in 0..<game.playedCards.count {
// update highlights
self.highlightCards()
// animate cards off screen
let endPoint: CGPoint = CGPoint(x: 1000, y: 250 )
UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 3, delay: 2, options: UIView.AnimationOptions.curveEaseInOut, animations: {
() -> Void in
self.game.board[self.game.playedCards[i]].center = endPoint
}, completion: {
(Bool) -> Void in
self.game.board[self.game.playedCards[i]].removed = true
self.game.board[self.game.playedCards[i]].removeFromSuperview()
self.currentCardCount = self.currentCardCount - 1
})
}
// set up next turn
game.blueTurn = !game.blueTurn
self.setBackground()
self.setSuitIndicators()
self.highlightCards()
你走对了!这里唯一的问题是动画函数 运行s asynchronously – 这意味着它不会等到前一个事物完成动画后再进入下一个循环。
您已经完成了使用 completion
闭包的部分过程。但是,如果您想在上一个 completion 之后等待对 next 项目进行动画处理,您基本上必须找到一种方法来运行 你的 属性 动画师再次从完成块内部。
最好的方法是提取对函数的 属性 动画调用。然后它使这变得简单!
// This is a function that contains what you've included in your code snippet.
func animateCards() {
// update highlights
highlightCards()
// animate cards off screen
let endPoint: CGPoint = CGPoint(x: 1000, y: 250)
recursivelyAnimateCard(at: game.playedCards.startIndex, to: endPoint) {
//set up next turn
self.game.blueTurn = !self.game.blueTurn
self.setBackground()
self.setSuitIndicators()
self.highlightCards()
}
}
// This function replicates your for-loop that animates everything.
func recursivelyAnimateCard(at index: Int, to endPoint: CGPoint, completion: () -> Void) {
// If this triggers, we've iterated through all of the cards. We can exit early and fire off the completion handler.
guard game.playedCards.indices.contains(index) else {
completion()
return
}
UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 3, delay: 2, options: UIView.AnimationOptions.curveEaseInOut,
animations: {
self.game.board[self.game.playedCards[index]].center = endPoint
},
completion: { _ in
self.game.board[self.game.playedCards[index]].removed = true
self.game.board[self.game.playedCards[index]].removeFromSuperview()
self.currentCardCount = self.currentCardCount - 1
// Call this function again on completion, but for the next card.
self.recursivelyAnimateCard(at: index + 1, to: endPoint, completion: completion)
})
}
编辑:我没有看到您需要 运行 "next turn" 代码 在 整个事情完成之后。我更新了上面的代码以包含一个完成处理程序,该处理程序在所有卡片都从屏幕上移除后触发。
我想弄清楚这个问题已经有一段时间了,但无济于事。基本上,我有一个 UIImageViews 数组,我想按顺序逐一设置动画。现在,它们都正确但同时进行了动画处理。此外,我希望底部的代码仅在所有动画完成后执行,因为现在它似乎是同时执行的。我是 swift 的新手,所以我想我不完全理解 for 循环是如何工作的?在我看来,这段代码 应该 循环遍历数组中的所有图像视图,只有在它完成后才应该执行底部的代码。应在循环的每次迭代中调用 highlightCards 方法,但这似乎也没有发生。我知道随着移动正确的图像,阵列已正确启动。
代码如下:
playedCards 是一个带有卡片索引的整数数组
game.board是卡片对象数组(UIImageView)
highlightCards() 只是高亮当前可玩的牌
完成块中的代码仅用于后勤,我知道它工作正常
"set up new game"注释下的代码是所有动画完成后才应该执行的代码
for i in 0..<game.playedCards.count {
// update highlights
self.highlightCards()
// animate cards off screen
let endPoint: CGPoint = CGPoint(x: 1000, y: 250 )
UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 3, delay: 2, options: UIView.AnimationOptions.curveEaseInOut, animations: {
() -> Void in
self.game.board[self.game.playedCards[i]].center = endPoint
}, completion: {
(Bool) -> Void in
self.game.board[self.game.playedCards[i]].removed = true
self.game.board[self.game.playedCards[i]].removeFromSuperview()
self.currentCardCount = self.currentCardCount - 1
})
}
// set up next turn
game.blueTurn = !game.blueTurn
self.setBackground()
self.setSuitIndicators()
self.highlightCards()
你走对了!这里唯一的问题是动画函数 运行s asynchronously – 这意味着它不会等到前一个事物完成动画后再进入下一个循环。
您已经完成了使用 completion
闭包的部分过程。但是,如果您想在上一个 completion 之后等待对 next 项目进行动画处理,您基本上必须找到一种方法来运行 你的 属性 动画师再次从完成块内部。
最好的方法是提取对函数的 属性 动画调用。然后它使这变得简单!
// This is a function that contains what you've included in your code snippet.
func animateCards() {
// update highlights
highlightCards()
// animate cards off screen
let endPoint: CGPoint = CGPoint(x: 1000, y: 250)
recursivelyAnimateCard(at: game.playedCards.startIndex, to: endPoint) {
//set up next turn
self.game.blueTurn = !self.game.blueTurn
self.setBackground()
self.setSuitIndicators()
self.highlightCards()
}
}
// This function replicates your for-loop that animates everything.
func recursivelyAnimateCard(at index: Int, to endPoint: CGPoint, completion: () -> Void) {
// If this triggers, we've iterated through all of the cards. We can exit early and fire off the completion handler.
guard game.playedCards.indices.contains(index) else {
completion()
return
}
UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 3, delay: 2, options: UIView.AnimationOptions.curveEaseInOut,
animations: {
self.game.board[self.game.playedCards[index]].center = endPoint
},
completion: { _ in
self.game.board[self.game.playedCards[index]].removed = true
self.game.board[self.game.playedCards[index]].removeFromSuperview()
self.currentCardCount = self.currentCardCount - 1
// Call this function again on completion, but for the next card.
self.recursivelyAnimateCard(at: index + 1, to: endPoint, completion: completion)
})
}
编辑:我没有看到您需要 运行 "next turn" 代码 在 整个事情完成之后。我更新了上面的代码以包含一个完成处理程序,该处理程序在所有卡片都从屏幕上移除后触发。