简体   繁体   English

在 SwiftUI 中插入/移除 animation

[英]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.这将使添加和删除它们变得更加容易,因为您只需appendremoveLast即可更改显示的数量。

Here's what I did:这是我所做的:

  1. Make a new struct that represents a Rectangle创建一个表示Rectangle的新结构
  2. The added property will control whether the Rectangle is shown or not added的属性将控制是否显示Rectangle
  3. Define an array of RectangleInfo that will be shown to the user定义将显示给用户的RectangleInfo数组
  4. Use ForEach to loop over the rectangles使用ForEach遍历rectangles
  5. Instead of a 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 后,就该制作动画了:

  1. First append a rectangle, to make space for it首先 append 一个矩形,为它腾出空间
  2. Once it's appended, set added to true so it slides in added后,设置为 true 以便它滑入
  3. Set added to false to slide it out设置added到 false 以将其滑出
  4. Once it slid out, remove it from the array and also remove the space.一旦滑出,将其从阵列中移除并移除空间。
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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM