Here I would like to create navigation code, I'm able to do this Swift
UIKit
. I'm trying the same functionality in SwiftUI
, but I'm facing an issue with my code. How can we convert the SwiftUI
view to AnyView
.
Is there any other way to achieve the same functionality in SwiftUI
?
Your help would be greatly appreciated.!!
/// 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)
}
}
Here is another solution code working fine with a single destination, but I can't change destination runtime. Router.onLogin
should accept destination view.
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")
}
}
This wouldn't work because in Navigator
onTap return void and it will not push view on any view.
But you can do by this
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()
}
}
}
}
Usage:
struct ContentView: View {
@State private var isNextScreen: Bool = false
var body: some View {
NavigationView {
Button("Home") {
isNextScreen.toggle()
}.navigate(to: Text("SS"),when: $isNextScreen)
}
}
}
As you mention in a comment, you want multiple and dynamic destinations.
Then you can use it this way.
View extension for navigation
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()})
}
}
}
}
Create Navigator
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")
}
}
}
Usage Content 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)
}
}
}