为啥当我在 UIHostingController 中使用 SwiftUI-transition 时,它不能按预期工作?

Posted

技术标签:

【中文标题】为啥当我在 UIHostingController 中使用 SwiftUI-transition 时,它不能按预期工作?【英文标题】:Why SwiftUI-transition does not work as expected when I use it in UIHostingController?为什么当我在 UIHostingController 中使用 SwiftUI-transition 时,它不能按预期工作? 【发布时间】:2020-11-28 14:37:03 【问题描述】:

我正在尝试为需要显示日期的视图进行良好的过渡。我为视图提供了一个 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")
            
        
    

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

但是一旦我将它包装在 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 中的错误,还是其他什么?

【问题讨论】:

【参考方案1】:

不同主机控制器之间的状态不能很好地运行(不清楚这是限制还是错误,只是经验观察)。

解决方案是在托管视图中嵌入依赖状态。使用 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 的视图模型 - 该模式具有不同的生命周期。

【讨论】:

这是正确的!但是对于我来说,将状态保留在视图中并不是一种选择,因为它来自外部,不像示例中那样。我会玩 ObservableObject 寿!谢谢!

以上是关于为啥当我在 UIHostingController 中使用 SwiftUI-transition 时,它不能按预期工作?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在 SwiftUI Lifecycle 中将 rootViewController 设置为 UIHostingController 时会出错?

SwiftUI UIHostingController 导航标题动画坏了

在构建 iOS 应用程序时获取在范围错误中找不到类型“UIHostingController”

无法将类型“UIHostingController<AnyView>”的值转换为关闭结果类型“UIHostingController<Page>”

将变量从 UIHostingController 传递到 SWIFTUI 视图

嵌入在 UIViewController 中的 UIHostingController - 如何从外部更新 @State?