【问题标题】:SwiftUI generic navigation link using block variable使用块变量的 SwiftUI 通用导航链接
【发布时间】:2021-06-20 13:28:37
【问题描述】:

我想在这里创建导航代码,我可以做到这一点SwiftUIKit。我正在SwiftUI 中尝试相同的功能,但我的代码遇到了问题。我们如何将SwiftUI 视图转换为AnyView

有没有其他方法可以在SwiftUI 中实现相同的功能?

非常感谢您的帮助。!!


/// Swift code
public struct Navigator {
    public var onLoginSuccess: (UINavigationController) -> Void = { navigationController in
        navigationController.pushViewController(UIViewController(), animated: true)
    }
}

/// Usage
var router = Navigator()
router.onLoginSuccess = { nav in
    nav.pushViewController(UIViewController(), animated: true)
}


/// SwiftUI  Code
struct Navigator {
    static var onTap: (AnyView) -> Void = { view in
        _ = view.navigate(to: Text("SS"))
    }
}

extension View {
    func navigate<SomeView: View>(to view: SomeView) -> some View {
        modifier(NavigateModifier(destination: view))
    }
}


fileprivate struct NavigateModifier<SomeView: View>: ViewModifier {
    fileprivate let destination: SomeView
    
    fileprivate func body(content: Content) -> some View {
        NavigationView {
            ZStack {
                content
                NavigationLink(destination: destination) {
                    EmptyView()
                }
            }
        }
    }
}

/// Usage 
NavigationView {
    Button("Home") {
        Navigator.onTap(self)
    }
}

这是另一个在单个目标上运行良好的解决方案代码,但我无法更改目标运行时。 Router.onLogin 应该接受目标视图。

struct ContentView: View {
    var body: some View {
        NavigationView {
            HStack {
                NavigationLink(destination: Router.onLogin) {
                    Text("HOME")
                }
            }
        }
    }
}


struct Router {
    @ViewBuilder
    static var onLogin: some View {
        Text("Hello")
    }
}

【问题讨论】:

  • 这行不通,因为您将 Void 返回到 Void。因为这个 _ = 。您必须以某种方式在 body 或 SwiftUI ViewModifier 中激活 NavigationLink。更改Buttonaction 中的某些内容以制作NavigationLink active

标签: swift swiftui swiftui-navigationlink


【解决方案1】:

这行不通,因为在Navigator onTap 中返回 void 并且它不会将视图推送到任何视图上。

但是你可以这样做

extension View {
    /// Navigate to a new view.
    /// - Parameters:
    ///   - view: View to navigate to.
    ///   - binding: Active binding
    func navigate<NewView: View>(to view: NewView, when binding: Binding<Bool>) -> some View {
        ZStack {
            self
            NavigationLink(
                destination: view,
                isActive: binding
            ) {
                EmptyView()
            }
        }
    }
}

用法:

struct ContentView: View {
    
    @State private var isNextScreen: Bool = false
    
    var body: some View {
        NavigationView {
            Button("Home") {
                isNextScreen.toggle()
            }.navigate(to: Text("SS"),when: $isNextScreen)
        }
    }
}

更新

正如您在评论中提到的,您需要多个动态目的地。

那你就可以这样用了。

用于导航的视图扩展

extension View {
    func navigate(to view: Binding<Navigator?>) -> some View {
        ZStack {
            self
            if let wrappedValue = view.wrappedValue {
                NavigationLink(
                    destination: wrappedValue.navigateView,
                    tag: wrappedValue,
                    selection: view,
                    label: {EmptyView()})
            }
        }
    }
}

创建导航器

enum Navigator: Identifiable {
    case onTap
    case onLogin
    
    var id: Navigator {
        return self
    }
    
    @ViewBuilder
    var navigateView: some View {
        switch self {
        case .onTap:
            Text("SS")
            
        case .onLogin:
            Text("Login View")
        }
    }
}

使用内容视图

struct ContentView: View {
    
    @State private var nextScreen: Navigator? = nil
    
    var body: some View {
        NavigationView {
            VStack{
                Button("Home") {
                    nextScreen = .onTap
                }
                
                Button("Login") {
                    nextScreen = .onLogin
                }
                
            }.navigate(to: $nextScreen)
        }
    }
}

【讨论】:

  • 感谢您的支持,有没有其他方法可以获取相同的Swift 功能?我的意思是,我并没有从我们的常规代码中获得太多好处。因为动作可能决定在运行时导航到哪里。我可以在需要时覆盖推送destination。我可以从我的 swift 代码中做到这一点,但不能在 SwiftUI 代码中。
  • @DRN 所以你的要求是:你的目标视图很多并且在按钮按下时决定视图吗?
  • 是的,它工作正常。谢谢。不过,我们必须维护enum 对吗?如果明天我们有NN 屏幕数量的案例数量,我们必须维护枚举。这就是我想用polymorphism 改进的地方。但是知道另一种方式真的很有帮助。
  • 是的。您需要维护enum。但是您必须在代码中的某处编写或添加目的地,因此最好在枚举中添加。
【解决方案2】:

我在这里找到了一个解决方案来解决这个问题,但不确定它是不是最好的。我相信我们仍然可以改进这一点。

struct LoginView: View {
    
    var loginAction = Router().onLogin(AnyView(Text("Actual View Wll This")))
    
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: loginAction) {
                    Text("Show View")
                }
                NavigationLink(destination: AnyView(Text("Detail"))) {
                    Text("Show Detail View")
                }
            }
        }
    }
}

extension LoginView {
    struct Router {
        var onLogin:(AnyView) -> AnyView = { links in
            return AnyView(links)
        }
    }
}


/// Now I can change the 'Destination' for 'loginAction' in a scene and where ever I want.

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    var contentView = LoginView()
    contentView.loginAction =  AnyView(Text("Changed Route to here"))
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: contentView)
        self.window = window
        window.makeKeyAndVisible()
    }
}

【讨论】:

    【解决方案3】:

    //经过一些改进

    struct LoginView: View {
        var loginAction = Router().onLogin(Text("Actual Detail View"))
    
        var body: some View {
            NavigationView {
                VStack {
                    NavigationLink(destination: loginAction) {
                        Text("Show View")
                    }
                }
            }
        }
    }
    
    extension LoginView {
        struct Router<Destination: View> {
            var onLogin:(Destination) -> Destination = { links in
                return links
            }
        }
    }
    
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
            
            var contentView = LoginView()
            contentView.loginAction =  LoginView.Router().onLogin(Text("Changed Route here .!!"))
            
            if let windowScene = scene as? UIWindowScene {
                let window = UIWindow(windowScene: windowScene)
                window.rootViewController = UIHostingController(rootView: contentView)
                self.window = window
                window.makeKeyAndVisible()
            }
        }
    

    【讨论】:

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