How can I add a badge to a leading navigationBarItems in SwiftUI and iOS 14?

5k views Asked by At

How can I add a badge to navigationBarItems in SwiftUI and iOS 14?

I cannot find anything on the net...

I want for example add a badge over the leading navigationBarItems:

var body: some View {
    NavigationView {
        ZStack {
            VStack(spacing: 0) {
                Text("Peanut")
                    .padding(-10)
                    .navigationBarTitle(Text("HomeTitle"), displayMode: .inline)
                    .navigationBarItems(leading:
                        HStack {
                            NavigationLink(destination: Notifications()) {
                                Image(systemName: "bell")
                                    .font(.system(size: 20))
                            }.foregroundColor(.white)
                        }, trailing:
                        HStack {
                            NavigationLink(destination: Settings()) {
                                Image(systemName: "gearshape")
                                    .font(.system(size: 20))
                            }.foregroundColor(.white)
                        })
            }
        }
    }
}
2

There are 2 answers

0
pawello2222 On BEST ANSWER

You can create a custom Badge view:

struct Badge: View {
    let count: Int

    var body: some View {
        ZStack(alignment: .topTrailing) {
            Color.clear
            Text(String(count))
                .font(.system(size: 16))
                .padding(5)
                .background(Color.red)
                .clipShape(Circle())
                // custom positioning in the top-right corner
                .alignmentGuide(.top) { $0[.bottom] }
                .alignmentGuide(.trailing) { $0[.trailing] - $0.width * 0.25 }
        }
    }
}

and use it as an overlay:

struct ContentView: View {
    var body: some View {
        NavigationView {
            ZStack {
                VStack(spacing: 0) {
                    Text("Peanut")
                        .padding(-10)
                        .navigationBarTitle(Text("HomeTitle"), displayMode: .inline)
                        .navigationBarItems(leading: leadingBarItems)
                }
            }
        }
    }

    var leadingBarItems: some View {
        NavigationLink(destination: Text("Notifications")) {
            Image(systemName: "bell")
                .font(.system(size: 20))
        }
        .foregroundColor(.primary)
        .padding(5)
        .overlay(Badge(count: 3))
    }
}

enter image description here

Note

The badge view uses alignment guides for positioning. For more information see:

0
mdonati On

Here's another example of custom badge

struct BadgeViewModifier: ViewModifier {
    let text: String?
    
    func body(content: Content) -> some View {
        content
            .overlay(alignment: .topTrailing) {
                text.map { value in
                    Text(value)
                        .fixedSize(horizontal: true, vertical: false)
                        .font(.system(size: 14, weight: .semibold))
                        .foregroundColor(DS.Colors.white)
                        .padding(.horizontal, value.count == 1 ? 2 : 6)
                        .padding(.vertical, 2)
                        .background(
                            Capsule()
                                .fill(DS.Colors.red)
                                .if(value.count == 1) { $0.aspectRatio(1, contentMode: .fill) }
                        )
                }
            }
    }
}

extension View {
    func badge(value: String?) -> some View {
        modifier(BadgeViewModifier(text: value))
    }
    
    @ViewBuilder func `if`<Result: View>(_ condition: Bool, closure: @escaping (Self) -> Result) -> some View {
        if condition {
            closure(self)
        } else {
            self
        }
    }

}