Re-using same code for navigationbar items in SwiftUI

28 Views Asked by At

I'm setting up an SwiftUI app with TabView-navigation and individual NavigationStacks (to preserve individual navigation state per stack).

One thing that buggles my mind is that I want to have the same profile button in the top right navigationbar on every View. Adding the navigationbar item as below requires me to have the exact same code/addition in every View.

.navigationDestination(isPresented: $presentProfilePage) {
    ProfileView()
}
.toolbar {
    Button {
        presentProfilePage = true
    } label: {
        if let img = user.displayImg {
            Image(uiImage: img)
                .resizable()
                .frame(width: 48, height: 48)
        } else {
            Image.defaultProfileImage
        }
    }
}

Is there a way to set the navigationbar items once and then they are visible on all views?

1

There are 1 best solutions below

0
On BEST ANSWER

You can put this code in a ViewModifier. You can put presentProfilePage as a @State in there.

struct ProfileNavigation: ViewModifier {
    @State var presentProfilePage = false
    let image: UIImage?
    
    func body(content: Content) -> some View {
        content
            .navigationDestination(isPresented: $presentProfilePage) {
                ProfileView()
            }
            .toolbar {
                Button {
                    presentProfilePage = true
                } label: {
                    if let image {
                        Image(uiImage: image)
                            .resizable()
                            .frame(width: 48, height: 48)
                    } else {
                        Image.defaultProfileImage
                    }
                }
            }
    }
}

extension View {
    func profileNavigation(image: UIImage?) -> some View {
        modifier(ProfileNavigation(image: image))
    }
}

You'd just add .profileNavigation(...) to each view you want to have this button.

Alternatively, you can use a NavigationLink in the toolbar directly. You would just extract the NavigationLink as a View to reuse it:

struct ProfileNavigation: View {
    let image: UIImage?
    
    var body: some View {
        NavigationLink {
            ProfileView()
        } label: {
            if let image {
                Image(uiImage: image)
                    .resizable()
                    .frame(width: 48, height: 48)
            } else {
                Image.defaultProfileImage
            }
        }
    }
}

Usage:

.toolbar {
    ProfileNavigation(...)
}