swiftui - How to define and use a @Namespace var outside a View - Stack Overflow

Is it possible at all to define a @Namespace var outside a View and pass it in to views. For example, t

Is it possible at all to define a @Namespace var outside a View and pass it in to views. For example, to do this in the State class?

I have included a baseline code that has matched animations. I have tried a few things (like this) without success, meaning the animations are not matched. I have seen some solutions but the @Namespace var is always a View property which I cannot do.

import SwiftUI
import PlaygroundSupport

enum Flow {
    case initial
    case searching
}

class State: ObservableObject {
    @Published var flow: Flow = .initial
}

let state = State()

struct Container: View {
    @Namespace var namespace
    @ObservedObject var state: State

    var body: some View {
        VStack {
            SubView1(state: state, namespace: namespace)
            SubView2(state: state, namespace: namespace)
        }
        .onTapGesture {
            withAnimation(.spring()) {
                if state.flow == .initial {
                    state.flow = .searching
                } else {
                    state.flow = .initial
                }
            }
        }
        .frame(width: 300, height: 300)
    }
}

struct SubView1: View {
    @ObservedObject var state: State
    var namespace: Namespace.ID

    var body: some View {
        HStack {
            Text("Lorem")
            Spacer()

            if state.flow == .initial {
                Text("Ipsum")
                    .matchedGeometryEffect(id: "id", in: namespace)
                Text("|")
            }
            Image(systemName: "person.circle.fill")
        }
    }
}

struct SubView2: View {
    @ObservedObject var state: State
    var namespace: Namespace.ID

    var body: some View {
        VStack {
            Text("Headline")
            if state.flow == .searching {
                Text("Ipsum")
                    .matchedGeometryEffect(id: "id", in: namespace)
            }
        }
    }
}

PlaygroundPage.current.setLiveView(Container(state: state))

Is it possible at all to define a @Namespace var outside a View and pass it in to views. For example, to do this in the State class?

I have included a baseline code that has matched animations. I have tried a few things (like this) without success, meaning the animations are not matched. I have seen some solutions but the @Namespace var is always a View property which I cannot do.

import SwiftUI
import PlaygroundSupport

enum Flow {
    case initial
    case searching
}

class State: ObservableObject {
    @Published var flow: Flow = .initial
}

let state = State()

struct Container: View {
    @Namespace var namespace
    @ObservedObject var state: State

    var body: some View {
        VStack {
            SubView1(state: state, namespace: namespace)
            SubView2(state: state, namespace: namespace)
        }
        .onTapGesture {
            withAnimation(.spring()) {
                if state.flow == .initial {
                    state.flow = .searching
                } else {
                    state.flow = .initial
                }
            }
        }
        .frame(width: 300, height: 300)
    }
}

struct SubView1: View {
    @ObservedObject var state: State
    var namespace: Namespace.ID

    var body: some View {
        HStack {
            Text("Lorem")
            Spacer()

            if state.flow == .initial {
                Text("Ipsum")
                    .matchedGeometryEffect(id: "id", in: namespace)
                Text("|")
            }
            Image(systemName: "person.circle.fill")
        }
    }
}

struct SubView2: View {
    @ObservedObject var state: State
    var namespace: Namespace.ID

    var body: some View {
        VStack {
            Text("Headline")
            if state.flow == .searching {
                Text("Ipsum")
                    .matchedGeometryEffect(id: "id", in: namespace)
            }
        }
    }
}

PlaygroundPage.current.setLiveView(Container(state: state))

Share Improve this question edited Mar 13 at 20:59 fingia asked Mar 13 at 20:46 fingiafingia 6471 gold badge9 silver badges22 bronze badges 2
  • Why do you want to do this? This is an XY Problem? – Sweeper Commented Mar 13 at 21:04
  • Try @State and pass down let for read access. @Binding for read/write. No class needed. – malhal Commented Mar 14 at 10:41
Add a comment  | 

2 Answers 2

Reset to default 0

Namespace is a DynamicProperty and DynamicProperty only works in a View because they get their value when the body updates.

They cannot be declared in an additional class, struct or globally.

One possible solution is to inject the View-owned namespace into your "state" class. Then views can pass in this state's namespace property in their matchedGeometryEffect:

import SwiftUI
import PlaygroundSupport

enum Flow {
    case initial
    case searching
}

class AppState: ObservableObject {
    @Published var flow: Flow = .initial
    
    // We'll store SwiftUI's actual Namespace.ID here (reassigned later).
    // Published is necessary to emit updates.
    @Published var namespace: Namespace.ID

    init(namespace: Namespace.ID) {
        self.namespace = namespace
    }
}

struct Container: View {
    @Namespace private var realNamespace
    @StateObject private var state: AppState

    init() {
        // SwiftUI hasn't injected the "real" namespace yet, so use a placeholder.
        let placeholder = Namespace().wrappedValue
        _state = StateObject(wrappedValue: AppState(namespace: placeholder))
    }

    var body: some View {
        VStack {
            SubView1(state: state, namespace: state.namespace)
            SubView2(state: state, namespace: state.namespace)
        }
        .onTapGesture {
            withAnimation(.spring()) {
                if state.flow == .initial {
                    state.flow = .searching
                } else {
                    state.flow = .initial
                }
            }
        }
        .onAppear {
            if state.namespace != realNamespace {
                print(#function, "injecting real namespace")
                state.namespace = realNamespace
            }
        }
        .frame(width: 300, height: 300)
    }
}

// MARK: - SubView1
struct SubView1: View {
    @ObservedObject var state: AppState
    var namespace: Namespace.ID

    var body: some View {
        HStack {
            Text("Lorem")
            Spacer()
            if state.flow == .initial {
                Text("Ipsum")
                    .matchedGeometryEffect(id: "id", in: namespace)
                Text("|")
            }
            Image(systemName: "person.circle.fill")
        }
    }
}

// MARK: - SubView2
struct SubView2: View {
    @ObservedObject var state: AppState
    var namespace: Namespace.ID

    var body: some View {
        VStack {
            Text("Headline")
            if state.flow == .searching {
                Text("Ipsum")
                    .matchedGeometryEffect(id: "id", in: namespace)
            }
        }
    }
}

// MARK: - Playground Live View
PlaygroundPage.current.setLiveView(Container())

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744685995a4587919.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信