简体   繁体   English

SwiftUI 动态添加子视图,但 animation 不起作用

[英]SwiftUI add subview dynamically but the animation doesn't work

I would like to create a view in SwiftUI that add a subview dynamically and with animation.我想在 SwiftUI 中创建一个动态添加子视图并使用 animation 的视图。

struct ContentView : View {
    @State private var isButtonVisible = false

    var body: some View {
        VStack {
            Toggle(isOn: $isButtonVisible.animation()) {
                Text("add view button")
            }

           if isButtonVisible {
                 AnyView(DetailView())
                      .transition(.move(edge: .trailing))
                      .animation(Animation.linear(duration: 2))
             }else{
                    AnyView(Text("test"))
            }
        }
    }
}

The above code works fine with the animation.上面的代码适用于 animation。 however when i move the view selection part into a function, the animation is not working anymore (since i want to add different views dynamically, therefore, I put the logic in a function.)但是,当我将视图选择部分移动到 function 中时,animation 不再工作(因为我想动态添加不同的视图,因此,我将逻辑放在 ZC1C425268E68385D14AB5074C17A9 中)。


struct ContentView : View {
    @State private var isButtonVisible = false
    var body: some View {
        VStack {
            Toggle(isOn: $isButtonVisible.animation()) {
                Text("add view button")
            }

            subView().transition(.move(edge: .trailing))
                     .animation(Animation.linear(duration: 2))   
    }

     func subView() -> some View {
         if isButtonVisible {
             return AnyView(DetailView())
         }else{
            return AnyView(Text("test"))
        }
    }
}

it looks totally the same to me, however, i don't understand why they have different result.它对我来说看起来完全一样,但是,我不明白为什么它们有不同的结果。 Could somebody explain me why?有人可以解释一下为什么吗? and any better solutions?以及任何更好的解决方案? thanks alot!多谢!

Here's your code, modified so that it works:这是您的代码,经过修改后可以正常工作:

struct ContentView : View {
    @State private var isButtonVisible = false

    var body: some View {
        VStack {
            Toggle(isOn: $isButtonVisible.animation()) {
                Text("add view button")
            }

            subView()
                .transition(.move(edge: .trailing))
                .animation(Animation.linear(duration: 2))
        }
    }

    func subView() -> some View {
        Group {
            if isButtonVisible {
                DetailView()
            } else {
                Text("test")
            }
        }
    }
}

Note two things:注意两点:

  1. Your two examples above are different, which is why you get different results.你上面的两个例子是不同的,这就是你得到不同结果的原因。 The first applies a transition and animation to a DetailView, then type-erases it with AnyView.首先将转换和 animation 应用到 DetailView,然后使用 AnyView 对其进行类型擦除。 The second type-erases a DetailView with AnyView, then applies a transition and animation.第二种类型——使用 AnyView 擦除 DetailView,然后应用转换和 animation。
  2. Rather that using AnyView and type-erasure, I prefer to encapsulate the conditional logic inside of a Group view.而不是使用 AnyView 和类型擦除,我更喜欢将条件逻辑封装在Group视图中。 Then the type you return is Group , which will animate properly.然后你返回的类型是Group ,它会正确地动画。
  3. If you wanted different animations on the two possibilities for your subview, you can now apply them directly to DetailView() or Text("test") .如果您希望子视图的两种可能性有不同的动画,您现在可以将它们直接应用于DetailView()Text("test")

Update更新

The Group method will only work with if , elseif , and else statements. Group方法仅适用于ifelseifelse语句。 If you want to use a switch, you will have to wrap each branch in AnyView() .如果要使用开关,则必须将每个分支包装在AnyView()中。 However, this breaks transitions/animations.但是,这会破坏过渡/动画。 Using switch and setting custom animations is currently not possible.目前无法使用switch设置自定义动画。

I was able to get it to work with a switch statement by wrapping the function that returns an AnyView in a VStack .通过包装在 VStack 中返回AnyViewVStack ,我能够让它与 switch 语句一起使用。 I also had to give the AnyView an .id so SwiftUI can know when it changes.我还必须给AnyView一个.id ,以便 SwiftUI 可以知道它何时发生变化。 This is on Xcode 11.3 and iOS 13.3这是在 Xcode 11.3 和 iOS 13.3 上

struct EnumView: View {

    @ObservedObject var viewModel: ViewModel

    var body: some View {
        VStack {
            view(for: viewModel.viewState)
                .id(viewModel.viewState)
                .transition(.opacity)
        }
    }

    func view(for viewState: ViewModel.ViewState) -> AnyView {
        switch viewState {
        case .loading:
            return AnyView(LoadingStateView(viewModel: self.viewModel))
        case .dataLoaded:
            return AnyView(LoadedStateView(viewModel: self.viewModel))
        case let .error(error):
            return AnyView(ErrorView(error: error, viewModel: viewModel))
        }
    }

}

Also for my example in the ViewModel I need to wrap the viewState changes in a withAnimation block同样对于我在ViewModel中的示例,我需要将viewState更改包装在withAnimation块中

withAnimation {
    self.viewState = .loading
}

In iOS 14 they added the possibility to use if let and switch statements in function builders.在 iOS 14 中,他们增加了在 function 构建器中使用if letswitch语句的可能性。 Maybe it helps for your issues:也许它对您的问题有帮助:

https://www.hackingwithswift.com/articles/221/whats-new-in-swiftui-for-ios-14 (at the article's bottom) https://www.hackingwithswift.com/articles/221/whats-new-in-swiftui-for-ios-14 (在文章底部)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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