Custom back button for NavigationView's navigation bar in SwiftUI

后端 未结 9 633
隐瞒了意图╮
隐瞒了意图╮ 2020-11-29 23:57

I want to add a custom navigation button that will look somewhat like this:

Now, I\'ve written a custom BackButton view for this. When applying

相关标签:
9条回答
  • 2020-11-30 00:18

    I expect you want to use custom back button in all navigable screens, so I wrote custom wrapper based on @Ashish answer.

    struct NavigationItemContainer<Content>: View where Content: View {
        private let content: () -> Content
        @Environment(\.presentationMode) var presentationMode
    
        private var btnBack : some View { Button(action: {
            self.presentationMode.wrappedValue.dismiss()
        }) {
            HStack {
                Image("back_icon") // set image here
                    .aspectRatio(contentMode: .fit)
                    .foregroundColor(.black)
                Text("Go back")
            }
            }
        }
    
        var body: some View {
            content()
                .navigationBarBackButtonHidden(true)
                .navigationBarItems(leading: btnBack)
        }
    
        init(@ViewBuilder content: @escaping () -> Content) {
            self.content = content
        }
    }
    
    

    Wrap screen content in NavigationItemContainer:

    Usage:

    struct CreateAccountScreenView: View {
        var body: some View {
            NavigationItemContainer {
                VStack(spacing: 21) {
                    AppLogoView()
                    //...
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-30 00:24

    I found this: https://ryanashcraft.me/swiftui-programmatic-navigation/

    It does work, and it may lay the foundation for a state machine to control what is showing, but it is not a simple as it was before.

    import Combine
    import SwiftUI
    
    struct DetailView: View {
        var onDismiss: () -> Void
    
        var body: some View {
            Button(
                "Here are details. Tap to go back.",
                action: self.onDismiss
            )
        }
    }
    
    struct RootView: View {
        var link: NavigationDestinationLink<DetailView>
        var publisher: AnyPublisher<Void, Never>
    
        init() {
            let publisher = PassthroughSubject<Void, Never>()
            self.link = NavigationDestinationLink(
                DetailView(onDismiss: { publisher.send() }),
                isDetail: false
            )
            self.publisher = publisher.eraseToAnyPublisher()
        }
    
        var body: some View {
            VStack {
                Button("I am root. Tap for more details.", action: {
                    self.link.presented?.value = true
                })
            }
                .onReceive(publisher, perform: { _ in
                    self.link.presented?.value = false
                })
        }
    }
    
    struct ContentView: View {
        var body: some View {
            NavigationView {
                RootView()
            }
        }
    }
    
    If you want to hide the button then you can replace the DetailView with this:
    
    struct LocalDetailView: View {
        var onDismiss: () -> Void
    
        var body: some View {
            Button(
                "Here are details. Tap to go back.",
                action: self.onDismiss
            )
                .navigationBarItems(leading: Text(""))
        }
    }
    
    0 讨论(0)
  • 2020-11-30 00:29

    SwiftUI 1.0

    It looks like you can now combine the navigationBarBackButtonHidden and .navigationBarItems to get the effect you're trying to achieve.

    Code

    struct Navigation_CustomBackButton_Detail: View {
        @Environment(\.presentationMode) var presentationMode
        
        var body: some View {
            ZStack {
                Color("Theme3BackgroundColor")
                VStack(spacing: 25) {
                    Image(systemName: "globe").font(.largeTitle)
                    Text("NavigationView").font(.largeTitle)
                    Text("Custom Back Button").foregroundColor(.gray)
                    HStack {
                        Image("NavBarBackButtonHidden")
                        Image(systemName: "plus")
                        Image("NavBarItems")
                    }
                    Text("Hide the system back button and then use the navigation bar items modifier to add your own.")
                        .frame(maxWidth: .infinity)
                        .padding()
                        .background(Color("Theme3ForegroundColor"))
                        .foregroundColor(Color("Theme3BackgroundColor"))
                    
                    Spacer()
                }
                .font(.title)
                .padding(.top, 50)
            }
            .navigationBarTitle(Text("Detail View"), displayMode: .inline)
            .edgesIgnoringSafeArea(.bottom)
            // Hide the system back button
            .navigationBarBackButtonHidden(true)
            // Add your custom back button here
            .navigationBarItems(leading:
                Button(action: {
                    self.presentationMode.wrappedValue.dismiss()
                }) {
                    HStack {
                        Image(systemName: "arrow.left.circle")
                        Text("Go Back")
                    }
            })
        }
    }
    

    Example

    Here is what it looks like (excerpt from the "SwiftUI Views" book):

    0 讨论(0)
  • 2020-11-30 00:31

    Really simple method. Only two lines code

    0 讨论(0)
  • 2020-11-30 00:32

    This solution works for iPhone. However, for iPad it won't work because of the splitView.

    import SwiftUI
    
    struct NavigationBackButton: View {
      var title: Text?
      @Environment(\.presentationMode) private var presentationMode: Binding<PresentationMode>
    
      var body: some View {
        ZStack {
          VStack {
            ZStack {
              HStack {
                Button(action: {
                  self.presentationMode.wrappedValue.dismiss()
                }) {
                  Image(systemName: "chevron.left")
                    .font(.title)
                    .frame(width: 44, height: 44)
                  title
                }
                Spacer()
              }
            }
            Spacer()
          }
        }
        .zIndex(1)
        .navigationBarTitle("")
        .navigationBarHidden(true)
      }
    }
    
    struct NavigationBackButton_Previews: PreviewProvider {
      static var previews: some View {
        NavigationBackButton()
      }
    }
    
    0 讨论(0)
  • 2020-11-30 00:39

    Swiping is not disabled this way.

    Works for me. XCode 11.3.1

    Put this in your root View

    init() {
        UINavigationBar.appearance().isUserInteractionEnabled = false
        UINavigationBar.appearance().backgroundColor = .clear
        UINavigationBar.appearance().barTintColor = .clear
        UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default)
        UINavigationBar.appearance().shadowImage = UIImage()
        UINavigationBar.appearance().tintColor = .clear
    }
    

    And this in your child View

    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    
    Button(action: {self.presentationMode.wrappedValue.dismiss()}) {
        Image(systemName: "gobackward")
    }
    
    0 讨论(0)
提交回复
热议问题