[英]Insertion/Removal animation in SwiftUI
I'm trying to achieve this animation for insertion of an element in a VStack
, all elements move down to create space for the new one and then the new one is inserted from right to left.我正在尝试实现此 animation 以在VStack
中插入元素,所有元素向下移动以为新元素创建空间,然后从右向左插入新元素。 The code bellow does the insertion as I described, but how to make it work for removal as well, the animation should be similar, the element is completely removed (to the right), then the other elements move up)下面的代码按照我的描述进行插入,但是如何使它也可以用于移除,animation 应该类似,元素被完全移除(向右),然后其他元素向上移动)
struct ContentView: View {
@State var show = false
var body: some View {
VStack {
show ? Rectangle().frame(width: 100, height: 100, alignment: .center)
.animation(Animation.easeIn(duration: 0.5).delay(1))
.transition(.move(edge: .trailing)): nil
Rectangle().frame(width: 100, height: 100, alignment: .center)
Rectangle().frame(width: 100, height: 100, alignment: .center)
Rectangle().frame(width: 100, height: 100, alignment: .center)
Rectangle().frame(width: 100, height: 100, alignment: .center)
Button("Add") {
show.toggle()
}
Spacer()
}.animation(Animation.easeIn(duration: 0.5))
}
}
I'm not sure what exactly your final outcome should be and my proposed solution is not really "generic", but it should at least give you a starting point.我不确定您的最终结果到底应该是什么,而且我提出的解决方案并不是真正的“通用”,但它至少应该为您提供一个起点。
struct ContentView: View {
@State var show = false
@State var showPlaceholder = false
// The total animation duration will be twice as long as this value
private var animationDuration: TimeInterval = 0.5
var body: some View {
VStack {
ZStack {
if showPlaceholder {
Color.white.opacity(0.0)
.zIndex(1)
.frame(width: 100, height: 100)
}
if show {
Rectangle()
.zIndex(2)
.frame(width: 100, height: 100, alignment: .center)
.animation(Animation.easeIn(duration: animationDuration))
.transition(.move(edge: .trailing))
}
}
Rectangle().frame(width: 100, height: 100, alignment: .center)
Rectangle().frame(width: 100, height: 100, alignment: .center)
Rectangle().frame(width: 100, height: 100, alignment: .center)
Rectangle().frame(width: 100, height: 100, alignment: .center)
Button("Add") {
if show {
showPlaceholder(for: animationDuration)
show = false
}
else {
showPlaceholder(for: animationDuration) {
show = true
}
}
}
Spacer()
}.animation(Animation.easeIn(duration: animationDuration))
}
private func showPlaceholder(for timeInterval: TimeInterval, completion: (() -> Void)? = nil) {
showPlaceholder = true
Timer.scheduledTimer(withTimeInterval: timeInterval, repeats: false) { (_) in
showPlaceholder = false
completion?()
}
}
}
If you have that many Rectangle()
s, you should use a ForEach
.如果你有那么多Rectangle()
,你应该使用ForEach
。 This will make adding and removing them much easier, because you can just append
and removeLast
to change how many are showing.这将使添加和删除它们变得更加容易,因为您只需append
和removeLast
即可更改显示的数量。
Here's what I did:这是我所做的:
Rectangle
创建一个表示Rectangle
的新结构added
property will control whether the Rectangle
is shown or not added
的属性将控制是否显示Rectangle
RectangleInfo
that will be shown to the user定义将显示给用户的RectangleInfo
数组ForEach
to loop over the rectangles
使用ForEach
遍历rectangles
transition
, you can use offset
to have more control over the animation.您可以使用offset
代替transition
来更好地控制 animation。Once the UI is set up, it's time for the animations:设置好 UI 后,就该制作动画了:
added
to true so it slides in added
后,设置为 true 以便它滑入added
to false to slide it out设置added
到 false 以将其滑出struct RectangleInfo: Identifiable { /// 1.
let id = UUID()
var added = true /// 2.
}
struct ContentView: View {
@State var rectangles = [ /// 3.
RectangleInfo(),
RectangleInfo(),
RectangleInfo()
]
let animationDuration = 0.5
var body: some View {
VStack {
ForEach(rectangles) { rectangle in /// 4.
Rectangle().frame(width: 100, height: 100, alignment: .center)
.offset(x: rectangle.added ? 0 : 200, y: 0) /// 5.
}
Button("Add") {
rectangles.append(RectangleInfo(added: false)) /// 6.
DispatchQueue.main.asyncAfter(deadline: .now() + animationDuration) {
rectangles[rectangles.count - 1].added = true /// 7.
}
}
Button("Remove") {
rectangles[rectangles.count - 1].added = false /// 8.
DispatchQueue.main.asyncAfter(deadline: .now() + animationDuration) {
rectangles.removeLast() /// 9.
}
}
Spacer()
}.animation(Animation.easeIn(duration: animationDuration))
}
}
Result:结果:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.