如何在Swift中在子视图之间传递信息

huangapple go评论65阅读模式
英文:

How can I pass information between subviews in Swift

问题

我正在使用NavigationStack来构建我的应用。在NavigationStack中,我有多个“子”视图需要在彼此之间传递信息。例如,我的应用中有三个视图。View1负责所有视图之间的导航(由于SwiftUI的更新,这是我唯一知道如何做的方法)。在View2中,我需要将信息传递给View3(我可以使用@Binding,但由于视图更改是在View1中处理的,所以我不能这样做。是否有更好的结构可以使用?

以下是我的代码

enum Route {
    case view2
    case view3
}

struct View1: View {
    @State private var navigationPath: [Route] = []
    
    var body: some View {
        NavigationStack(path: $navigationPath) {
            Button("前往视图2") {
                navigationPath.append(.view2)
            }
            .background(Color.red)
            .navigationDestination(for: Route.self) { route in
                switch route {
                case .view2:
                    View2(navigationPath: $navigationPath)
                case .view3:
                    View3(navigationPath: $navigationPath)
                }
            }
        }
    }
}

struct View2: View {
    @Binding var navigationPath: [Route]
    
    var body: some View {
        Button("前往视图3") {
            navigationPath.append(.view3)
        }
        .background(Color.orange)
    }
}

struct View3: View {
    @Environment(\.dismiss) private var dismiss
    @Binding var navigationPath: [Route]
    
    var body: some View {
        Button("弹出视图") {
            navigationPath.removeLast()
            
            // 或者 - 调用 `dismiss()`
            // dismiss()
        }
        .background(Color.green)
    }
}
英文:

I am using a NavigationStack for my app. In the NavigationStack, I have multiple "sub" views that need to pass information between one another. For example, I have three views in my app. View1 handles all of the navigation between the views (this is the only way I know how to do this due to the SwiftUI update). In view2, I need to pass information to view3 ( I would use a @Binding, however, since the view change in handled in View1, I cannot do this. Is there a better structure I can use?

Here is my code

enum Route {
    case view2
    case view3
}

struct View1: View {
    @State private var navigationPath: [Route] = []
    
    var body: some View {
        NavigationStack(path: $navigationPath) {
            Button("Go to view 2") {
                navigationPath.append(.view2)
            }
            .background(Color.red)
            .navigationDestination(for: Route.self) { route in
                switch route {
                case .view2:
                    View2(navigationPath: $navigationPath)
                case .view3:
                    View3(navigationPath: $navigationPath)
                }
            }
        }
    }
}

struct View2: View {
    @Binding var navigationPath: [Route]
    
    var body: some View {
        Button("Go to view 3") {
            navigationPath.append(.view3)
        }
        .background(Color.orange)
    }
}

struct View3: View {
    @Environment(\.dismiss) private var dismiss
    @Binding var navigationPath: [Route]
    
    var body: some View {
        Button("Pop view") {
            navigationPath.removeLast()
            
            // or - call `dismiss()`
            // dismiss()
        }
        .background(Color.green)
    }
}

答案1

得分: 0

我认为这是使用 Navigation@Binding 的最佳结构。

import SwiftUI

struct View1: View {
    
    @State private var stackOverflow: String = "StackOverflow"

    //MARK: - Body
    var body: some View {
        NavigationStack {
            VStack {
                Text("Hello World!")
                
                NavigationLink(destination: {
                    View2(stackOverflow: $stackOverflow)
                }, label: {
                    Text(stackOverflow + "View 1")
                })
            }
        }
    }//END body
}//END struct

struct View2: View {
    
    @Binding var stackOverflow: String
    
    @State private var showNewView: Bool = false

    //MARK: - Body
    var body: some View {
        NavigationStack {
            VStack {
                Text("Hello World!")
                
                Button(action: {
                    showNewView.toggle()
                }, label: {
                    Text(stackOverflow + "View 2")
                })
            }
            .sheet(isPresented: $showNewView, content: {
                View3(stackOverflow: $stackOverflow)
            })
        }
    }//END body
}//END struct

struct View3: View {
    
    @Binding var stackOverflow: String

    //MARK: - Body
    var body: some View {
        NavigationStack {
            VStack {
                Text("Hello World!")
                
                Text(stackOverflow + "View 3")
            }
        }
    }//END body
}//END struct

对于 @Binding,你可以在此处查看更多。

对于 NavigationLink,你可以在此处查看更多。

不要忘记在你看了之后确认我的答案是否对你有帮助 如何在Swift中在子视图之间传递信息

英文:

I think this is the best struct to use Navigation and @Binding

import SwiftUI

struct View1: View {
    
    @State private var stackOverflow: String = "StackOverflow"

    //MARK: - Body
    var body: some View {
        NavigationStack {
            VStack {
                Text("Hello World!")
                
                NavigationLink(destination: {
                    View2(stackOverflow: $stackOverflow)
                }, label: {
                    Text(stackOverflow + "View 1")
                })
            }
        }
    }//END body
}//END struct

struct View2: View {
    
    @Binding var stackOverflow: String
    
    @State private var showNewView: Bool = false

    //MARK: - Body
    var body: some View {
        NavigationStack {
            VStack {
                Text("Hello World!")
                
                Button(action: {
                    showNewView.toggle()
                }, label: {
                    Text(stackOverflow + "View 2")
                })
            }
            .sheet(isPresented: $showNewView, content: {
                View3(stackOverflow: $stackOverflow)
            })
        }
    }//END body
}//END struct

struct View3: View {
    
    @Binding var stackOverflow: String

    //MARK: - Body
    var body: some View {
        NavigationStack {
            VStack {
                Text("Hello World!")
                
                Text(stackOverflow + "View 3")
            }
        }
    }//END body
}//END struct

For @Bindingyou can view more here

For NavigationLinkyou can view more here

Don't forget to validate my answer if it's right for you 如何在Swift中在子视图之间传递信息

答案2

得分: 0

我自己弄清楚了。我添加了一个AccountChangeSelector类和一个@ObservedObject var selection = AccountChangeSelector()

以下是完整的代码:

class AccountChangeSelector: ObservableObject {
    @Published var selection = ""
}

struct View1: View {
    @State private var path = NavigationPath()
    @ObservedObject var selection = AccountChangeSelector()

    var body: some View {
        NavigationStack(path: $path) {
            Button("转到视图2") {
                path.append("View2")
            }
            .background(Color.red)
            .navigationDestination(for: String.self) { route in
                switch route {
                case "View2":
                    View2(path: $path)
                case "View3":
                    View3(path: $path, selection: $selection)
                }
            }
        }
    }
}

struct View2: View {
    @Binding var path: NavigationPath
    @ObservedObject var selection: AccountChangeSelector

    var body: some View {
        Button("转到视图3") {
            selection.selection = "View3 选择"
            path.append("View3")
        }
        .background(Color.orange)
        //...
        //...
    }
}

struct View3: View {
    @Environment(\.dismiss) private var dismiss
    @Binding var path: NavigationPath
    var selection: String

    var body: some View {
        Button("弹出视图") {
            path.removeLast()
            
            // 或者 - 调用 `dismiss()`
            // dismiss()
        }
        .background(Color.green)
    }
}

我希望这能帮助某人,因为我为此头疼了好几天。作为一个一直使用NavigationViewNavigationLink的人,切换到这个应该很容易,但结果却很痛苦。

英文:

I figured it out myself. I added a AccountChangeSelector class and a @ObservedObject var selection = AccountChangeSelector().

Here is the full code:

class AccountChangeSelector: ObservableObject {
    @Published var selection = ""
}

struct View1: View {
    @State private var path = NavigationPath()
    @ObservedObject var selection = AccountChangeSelector()

    var body: some View {
        NavigationStack(path: $path) {
            Button("Go to view 2") {
                path.append("View2")
            }
            .background(Color.red)
            .navigationDestination(for: String.self) { route in
                switch route {
                case "View2":
                    View2(path: $path)
                case "View3":
                    View3(path: $path, selection: $selection)
                }
            }
        }
    }
}

struct View2: View {
    @Binding var path: NavigationPath
    @ObservedObject var selection: AccountChangeSelector

    var body: some View {
        Button("Go to view 3") {
            selection.selection = "View3 selection"
            path.append("View3")
        }
        .background(Color.orange)
        //...
        //...
    }
}

struct View3: View {
    @Environment(\.dismiss) private var dismiss
    @Binding var path: NavigationPath
    var selection: String

    var body: some View {
        Button("Pop view") {
            navigationPath.removeLast()
            
            // or - call `dismiss()`
            // dismiss()
        }
        .background(Color.green)
    }
}

I hope this helps someone as I had a headache over this for days. As someone who always used NavigationView & NavigationLink, switching to this was supposed to be easy, but turned out to be a pain.

答案3

得分: 0

View Model Solution (A)

这将允许数据来回移动,您可以使用viewModel.someView作为State变量

ViewModel

final class ViewModel: ObservableObject { 
   @Published var someVa: String = ""
   
   enum Route {
      case view2
      case view3
   }
}

View1

struct View1: View {
    @State private var navigationPath: [ViewModel.Route] = []
    @StateObject var viewModel = ViewModel()

    var body: some View {
        NavigationStack(path: $navigationPath) {
            Button("转到视图2") {
                navigationPath.append(.view2)
            }
            .background(Color.red)
            Text(viewModel.someVar)
            .navigationDestination(for: ViewModel.Route.self) { route in
                switch route {
                case .view2:
                    View2(navigationPath: $navigationPath, viewModel: viewModel)
                case .view3:
                    View3(navigationPath: $navigationPath, viewModel: viewModel)
                }
            }
        }
    }
}

View2

struct View2: View {
    @Binding var navigationPath: [ViewModel.Route]
    @ObservedObject var viewModel: ViewModel

    var body: some View {
        VStack {
           Button("转到视图3") {
              navigationPath.append(.view3)
           }
           .background(Color.orange)

           Text(viewModel.someVar)
        }
    }
}

View3

struct View3: View {
    @Environment(\.dismiss) private var dismiss
    @Binding var navigationPath: [ViewModel.Route]
    @ObservedObject var viewModel: ViewModel

    var body: some View {
        VStack {
           Button("弹出视图") {
              navigationPath.removeLast()
            
               // 或者 - 调用 `dismiss()`
               // dismiss()
           }
           .background(Color.green)

           Text(viewModel.someVar)
        }
    }
}

Enum Solution (B)

这只允许从父视图向前移动数据

Enum

enum Route {
   case view2(someVar: String)
   case view3(someVar: String)
}
英文:

You can use a ViewModel solution or pass variable through the enums.

View Model Solution (A)

This will allow movement of the data back and forth, you can use the viewModel.someView as a State var

ViewModel

final class ViewModel: ObservableObject { 
   @Published var someVa: String = ""

   enum Route {
      case view2
      case view3
   }
}

View1

struct View1: View {
    @State private var navigationPath: [ViewModel.Route] = []
    @StateObject var viewModel = ViewModel()

    var body: some View {
        NavigationStack(path: $navigationPath) {
            Button("Go to view 2") {
                navigationPath.append(.view2)
            }
            .background(Color.red)
            Text(viewModel.someVar)
            .navigationDestination(for: ViewModel.Route.self) { route in
                switch route {
                case .view2:
                    View2(navigationPath: $navigationPath, viewModel: viewModel)
                case .view3:
                    View3(navigationPath: $navigationPath, viewModel: viewModel)
                }
            }
        }
    }
}

View2

struct View2: View {
    @Binding var navigationPath: [ViewModel.Route]
    @ObservedObject var viewModel: ViewModel

    var body: some View {
        VStack {
           Button("Go to view 3") {
              navigationPath.append(.view3)
           }
           .background(Color.orange)

           Text(viewModel.someVar)
        }
    }
}

View3

struct View3: View {
    @Environment(\.dismiss) private var dismiss
    @Binding var navigationPath: [ViewModel.Route]
    @ObservedObject var viewModel: ViewModel

    var body: some View {
        VStack {
           Button("Pop view") {
              navigationPath.removeLast()
            
               // or - call `dismiss()`
               // dismiss()
           }
           .background(Color.green)

           Text(viewModel.someVar)
        }
    }
}

Enum Solution (B)

This will only allow a forward move of data from the parent view

Enum

enum Route {
   case view2(someVar: String)
   case view3(someVar: String)
}

huangapple
  • 本文由 发表于 2023年6月6日 04:26:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/76409783.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定