如何将@namespace 传递给 SwiftUI 中的多个视图?

Azh*_*mil 15 swiftui ios14 xcode12

我正在使用新的 Xcode 12 beta 和 SwiftUi 2.0。.matchedGeometryEffect()修改器非常适合做英雄动画。@NamespaceSwiftUI 中引入了一个新属性。它超级酷。工作真棒。

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

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

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()
                    }
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它工作正常。

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

在此处输入图片说明

我得到了赞美 Cannot find 'namespace' in scope

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

Asp*_*eri 24

The@Namespace是 的包装器Namespace.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()
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


mic*_*ben 11

Namespace将 注入的一种无警告方法Environment是创建一个ObservableObject名为 的,以在创建后NamespaceWrapper保存。Namespace这可能看起来像:

class NamespaceWrapper: ObservableObject {
    var namespace: Namespace.ID

    init(_ namespace: Namespace.ID) {
        self.namespace = namespace
    }
}
Run Code Online (Sandbox Code Playgroud)

然后您将创建并传递Namespace类似的内容:

struct ContentView: View {
    @Namespace var someNamespace

    var body: some View {
        Foo()
            .environmentObject(NamespaceWrapper(someNamespace))
    }
}

struct Foo: View {
    @EnvironmentObject var namespaceWrapper: NamespaceWrapper
    
    var body: some View {
        Text("Hey you guys!")
            .matchedGeometryEffect(id: "textView", in: namespaceWrapper.namespace)
    }
}
Run Code Online (Sandbox Code Playgroud)


Val*_*adu 6

虽然接受的答案有效,但在多个嵌套子视图之间共享名称空间会有点烦人,特别是如果您希望初始化器干净且切中要点。在这种情况下,使用环境值可能会更好:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

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

/// 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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @mickben,确实,*如果*您使用默认值:想法是设置`.namespace`。或者,可以使用`@EnvironmentObject`,但也会遇到同样的问题,如果你不先设置它,你会得到一个致命错误。 (2认同)