@mickben and @Confused Vorlon both have good solutions. Here is an adaptaion for Swift 6 / iOS 26:
import SwiftUI
@Observable
final class SharedNamespace {
var id: Namespace.ID!
init(_ namespace: Namespace.ID? = nil) {
if let namespace = namespace {
self.id = namespace
}
}
}
struct SharedNamespaceEnvironmentKey: @MainActor EnvironmentKey {
@MainActor static let defaultValue: SharedNamespace = SharedNamespace()
}
extension EnvironmentValues {
@MainActor
var namespace: SharedNamespace {
get { self[SharedNamespaceEnvironmentKey.self] }
set { self[SharedNamespaceEnvironmentKey.self] = newValue }
}
}
extension View {
func namespace(_ value: Namespace.ID) -> some View {
environment(\.namespace, SharedNamespace(value))
}
}
Usage:
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)
}
}
.namespace(namespace)
}
}
struct View1: View {
@Environment(\.namespace) private var namespace
@Binding var isDisplay: Bool
var body: some View {
VStack {
Image("plant")
.resizable()
.frame(width: 150, height: 100)
.matchedGeometryEffect(id: "img", in: namespace.id)
Spacer()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.blue)
.onTapGesture {
withAnimation {
self.isDisplay.toggle()
}
}
}
}