为什么我的SwiftUI过渡没有动画?

时间:2020-10-08 16:15:06

标签: swift swiftui swiftui-animation

我有一个基于一些状态变量的视图。这些状态由API调用的完成块设置。 bookings显示为ForEach列表,我希望它们以slide过渡出现。

slide过渡效果很好。由于我添加了!isLoading检查,因此不再显示这些动画。现在出现的所有动画都是ProgressView的淡入/淡出。取消!isLoading检查后,交错的幻灯片动画又回来了,但是我想保留此检查,以便我能够正确处理加载状态。

下面是一个独立的代码来重现该问题:

import SwiftUI

struct RoomView: View {
    
    @State private var room: String?
    @State private var bookings: [String]?
    @State private var isLoading: Bool = false
    
    var body: some View {
        VStack(spacing: 0) {
            if /* This is the problematic condition !isLoading, */ let room = self.room {
                ScrollView {
                    LazyVStack(alignment: .leading) {
                        Text(room)
                        
                        if let bookings = bookings {
                            ForEach(bookings.indices) { (index) in
                                let booking = bookings[index]
                                VStack(alignment: .leading) {
                                    Text(booking)
                                    Divider()
                                }
                                .transition(.slide)
                                // Stagger the animations when displaying the list
                                .animation(Animation.spring().delay(0.04 * Double(index)))
                            }
                        }
                    }
                    .padding()
                }
            } else {
                // If we're loading
                ProgressView("Loading Room")
            }
        }
        .transition(.opacity)
        .animation(.easeInOut)
        .onAppear {
            self.isLoading = true
            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                self.room = "Demo room"
                DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                    withAnimation {
                        self.bookings = ["Booking 1", "Booking 2", "Booking 3", "Booking 4", "Booking 5"]
                        self.isLoading = false
                    }
                }
            }
        }
    }
}

struct RoomView_Previews: PreviewProvider {
    static var previews: some View {
        RoomView()
    }
}

SwiftUI动画是否存在一个已知问题,其中具有多个条件可能导致过渡不出现?如何解决此问题,以便保持ProgressView过渡的交错动画和淡入淡出状态而无需取消!isLoading检查?

1 个答案:

答案 0 :(得分:0)

这是可能的解决方案-使视图独立。在Xcode 12 / iOS 14上进行了测试。

demo

struct RoomView: View {
    
    @State private var room: String?
    @State private var bookings: [String]?
    @State private var isLoading: Bool = false
    
    var body: some View {
        ZStack {
            if isLoading {
                // If we're loading
                ProgressView("Loading Room")
            }
            if let room = self.room {
                ScrollView {
                    LazyVStack(alignment: .leading) {
                        Text(room)
                        
                        if let bookings = bookings {
                            ForEach(bookings.indices) { (index) in
                                let booking = bookings[index]
                                VStack(alignment: .leading) {
                                    Text(booking)
                                    Divider()
                                }
                                .transition(.slide)
                                // Stagger the animations when displaying the list
                                .animation(Animation.spring().delay(0.04 * Double(index)))
                            }
                        }
                    }
                    .padding()
                }
            }
        }
        .transition(.opacity)
        .animation(.easeInOut)
        .onAppear {
            self.isLoading = true
            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                self.room = "Demo room"
                DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                    withAnimation {
                        self.bookings = ["Booking 1", "Booking 2", "Booking 3", "Booking 4", "Booking 5"]
                        self.isLoading = false
                    }
                }
            }
        }
    }
}