【问题标题】:How to pass @namespace to multiple Views in SwiftUI?如何将@namespace 传递给SwiftUI 中的多个视图?
【发布时间】:2020-11-17 16:33:09
【问题描述】:

我正在使用新的 Xcode 12 beta 和 SwiftUi 2.0。 .matchedGeometryEffect() 修饰符非常适合制作英雄动画。 SwiftUI 中引入了一个新属性 @Namespace。它超级酷。工作很棒。

我只是想知道是否有可能将命名空间变量传递给多个视图?

这是我正在研究的一个例子,

struct HomeView: View {
    @Namespace var namespace
    @State var isDisplay = true
    
    var body: some View {
        ZStack {
            if isDisplay {
                VStack {
                    Image("share sheet")
                        .resizable()
                        .frame(width: 150, height: 100)
                        .matchedGeometryEffect(id: "img", in: namespace)
                    Spacer()
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .background(Color.blue)
                .onTapGesture {
                    withAnimation {
                        self.isDisplay.toggle()
                    }
                }
            } else {
                VStack {
                    Spacer()
                    Image("share sheet")
                        .resizable()
                        .frame(width: 300, height: 200)
                        .matchedGeometryEffect(id: "img", in: namespace)
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .background(Color.red)
                .onTapGesture {
                    withAnimation {
                        self.isDisplay.toggle()
                    }
                }
            }
        }
    }
}

一切正常。

但是如果我想将Vstack提取为子视图,下图显示我已将第一个VStack提取到子视图中。

我得到了表扬Cannot find 'namespace' in scope

有没有办法跨多个视图传递命名空间?

【问题讨论】:

    标签: swiftui ios14 xcode12


    【解决方案1】:

    @NamespaceNamespace.ID 的包装器,您可以将Namespace.ID 作为参数传递给子视图。

    这是一个可能解决方案的演示。使用 Xcode 12 / iOS 14 测试

    struct HomeView: View {
        @Namespace var namespace
        @State var isDisplay = true
    
        var body: some View {
            ZStack {
                if isDisplay {
                    View1(namespace: namespace, isDisplay: $isDisplay)
                } else {
                    View2(namespace: namespace, isDisplay: $isDisplay)
                }
            }
        }
    }
    
    struct View1: View {
        let namespace: Namespace.ID
        @Binding var isDisplay: Bool
        var body: some View {
            VStack {
                Image("plant")
                    .resizable()
                    .frame(width: 150, height: 100)
                    .matchedGeometryEffect(id: "img", in: namespace)
                Spacer()
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(Color.blue)
            .onTapGesture {
                withAnimation {
                    self.isDisplay.toggle()
                }
            }
        }
    }
    
    struct View2: View {
        let namespace: Namespace.ID
        @Binding var isDisplay: Bool
        var body: some View {
            VStack {
                Spacer()
                Image("plant")
                    .resizable()
                    .frame(width: 300, height: 200)
                    .matchedGeometryEffect(id: "img", in: namespace)
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(Color.red)
            .onTapGesture {
                withAnimation {
                    self.isDisplay.toggle()
                }
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      虽然接受的答案有效,但在多个嵌套子视图之间共享命名空间会有点烦人,尤其是如果您希望初始化程序简洁明了。在这种情况下使用环境值可能会更好:

      struct NamespaceEnvironmentKey: EnvironmentKey {
          static var defaultValue: Namespace.ID = Namespace().wrappedValue
      }
      
      extension EnvironmentValues {
          var namespace: Namespace.ID {
              get { self[NamespaceEnvironmentKey.self] }
              set { self[NamespaceEnvironmentKey.self] = newValue }
          }
      }
      
      extension View {
          func namespace(_ value: Namespace.ID) -> some View {
              environment(\.namespace, value)
          }
      }
      

      现在您可以在任何视图中创建命名空间并允许其所有后代使用它:

      /// Main View
      struct PlaygroundView: View {
          @Namespace private var namespace
      
          var body: some View {
              ZStack {
                 SplashView()
      ...
              }
              .namespace(namespace)
          }
      }
      
      /// Subview
      struct SplashView: View {
          @Environment(\.namespace) var namespace
      
          var body: some View {
              ZStack(alignment: .center) {
                  Image("logo", bundle: .module)
                      .matchedGeometryEffect(id: "logo", in: namespace)
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-16
        • 2022-11-23
        相关资源
        最近更新 更多