首页 > 解决方案 > 为什么当我在 UIHostingController 中使用 SwiftUI-transition 时,它不能按预期工作?

问题描述

我正在尝试为需要显示日期的视图获得一个很好的过渡。我为视图提供了一个 ID,以便 SwiftUI 知道它是一个新标签并通过过渡对其进行动画处理。这是没有格式化程序和样式的精简版本,并且持续时间较长,以实现更好的可视化:

struct ContentView: View {
    @State var date = Date()

    var body: some View {
        VStack {
            Text("\(date.description)")
                .id("DateLabel" + date.description)
                .transition(.slide)
                .animation(.easeInOut(duration: 5))

            Button(action: { date.addTimeInterval(24*60*60) }) {
                Text("Click")
            }
        }
    }
}

结果,它按预期工作,旧标签正在动画,新标签正在动画:

结果1

但是一旦我将它包装在 UIHostingController 中:

struct ContentView: View {
    @State var date = Date()

    var body: some View {
        AnyHostingView {
            VStack {
                Text("\(date.description)")
                    .id("DateLabel" + date.description)
                    .transition(.slide)
                    .animation(.easeInOut(duration: 5))

                Button(action: { date.addTimeInterval(24*60*60) }) {
                    Text("Click")
                }
            }
        }
    }
}

struct AnyHostingView<Content: View>: UIViewControllerRepresentable {
    typealias UIViewControllerType = UIHostingController<Content>
    let content: Content

    init(content: () -> Content) {
        self.content = content()
    }

    func makeUIViewController(context: Context) -> UIHostingController<Content> {
        let vc = UIHostingController(rootView: content)
        return vc
    }

    func updateUIViewController(_ uiViewController: UIHostingController<Content>, context: Context) {
        uiViewController.rootView = content
    }
}

结果,新标签没有动画,而只是插入到它的最终位置,而旧标签正在动画出来:

在此处输入图像描述

我有更复杂的托管控制器,但这说明了这个问题。我更新托管控制器视图的方式是否有问题,或者这是 SwiftUI 中的错误,还是其他什么?

标签: swiftuiswiftui-animation

解决方案


状态在不同的托管控制器之间运行不佳(尚不清楚这是限制还是错误,只是经验观察)。

解决方案是在托管视图中嵌入依赖状态。使用 Xcode 12.1 / iOS 14.1 测试。

struct ContentView: View {
    var body: some View {
        AnyHostingView {
            InternalView()
        }
    }
}

struct InternalView: View {
    @State private var date = Date()   // keep relative state inside
    var body: some View {
        VStack {
             Text("\(date.description)")
                  .id("DateLabel" + date.description)
                  .transition(.slide)
                  .animation(.easeInOut(duration: 5))

             Button(action: { date.addTimeInterval(24*60*60) }) {
                  Text("Click")
             }
        }
    }
}

注意:您还可以尝试ObservableObject/ObservedObject基于视图模型 - 该模式具有不同的生命周期。


推荐阅读