简体   繁体   中英

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. 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)

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 . This will make adding and removing them much easier, because you can just append and removeLast to change how many are showing.

Here's what I did:

  1. Make a new struct that represents a Rectangle
  2. The added property will control whether the Rectangle is shown or not
  3. Define an array of RectangleInfo that will be shown to the user
  4. Use ForEach to loop over the rectangles
  5. Instead of a transition , you can use offset to have more control over the animation.

Once the UI is set up, it's time for the animations:

  1. First append a rectangle, to make space for it
  2. Once it's appended, set added to true so it slides in
  3. Set added to false to slide it out
  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:

矩形垂直堆叠,底部有添加和删除按钮。按添加首先腾出空间,然后将矩形滑入。按删除首先删除矩形,然后删除空间。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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