SwiftUI List navigation title font and padding update?

114 Views Asked by At

I'm trying to change the font and padding on the title ("My Title")? So i'm setting the .navigationTitle to have the nice List behavior where as you scroll past the title, it updates the navigation bar title.

Note: I also like to keep the this behavior of having the nav bar title change automagically

Here's my code:

#Preview() {
    NavigationView {
        List {
            ForEach(1..<30) { number in
                Text("\(number)")
            }.navigationTitle("My Title")
        }
    }
}

This is the text that i'd like to be able to modify the font and padding I also like to keep the this behavior of having the nav bar title change automagically

1

There are 1 best solutions below

0
MatBuompy On

This is a custom solution I came up with. It uses PreferenceKey and GeometryReader to determine the Scroll offset.

struct ContentView: View {
    
    @State var offsetValue: CGFloat = 1
    @State private var titlePlacement: Alignment = .topLeading
    
    var body: some View {
        NavigationStack {
            GeometryReader { proxy in
                let minY = proxy.frame(in: .scrollView(axis: .vertical)).minY
                ScrollView {
                    ZStack {
                        LazyVStack {
                            ForEach(0...100, id: \.self) { index in
                                Text("Row \(index)")
                            }
                        }
                        .padding(.top, 40)
                        GeometryReader { proxy in
                            let offset = proxy.frame(in: .named("scroll")).minY
                            Color.clear.preference(key: OffsetKey.self, value: offset)
                        }
                    } //: ZSTACK
                } //: SCROLL
                .coordinateSpace(name: "scroll")
                .onPreferenceChange(OffsetKey.self) { scrollValue in
                    let progress = -minY / scrollValue
                    
                    /// Cap it between 1-0
                    let cappedScrollValue = min(max(abs(progress), 0), 1)
                    print("Capped: \(cappedScrollValue)")
                    
                    withAnimation( /* YOUR ANIMATON HERE */) {
                        if offsetValue >= 0.8 {
                            titlePlacement = .topLeading
                        } else {
                            titlePlacement = .top
                        }
                    }
                    offsetValue = cappedScrollValue
                }
            } //: GEOMETRY
            .overlay(alignment: titlePlacement) {
                Text("My Title")
                    .font(titlePlacement == .topLeading ? .title.bold() : .title3.bold())
                    .frame(maxWidth: .infinity, alignment: titlePlacement)
                    .padding(.leading, 20) // <-- Padding here
                    .background(alignment: .top) {
                        if titlePlacement == .top {
                            Rectangle()
                                .fill(.ultraThinMaterial)
                                .frame(maxWidth: .infinity)
                                .frame(height: 100)
                                .offset(y: -60)
                        }
                    }
                    
            }
        } //: NAVIGATION
    }
    
    
}

struct OffsetKey: PreferenceKey {
    typealias Value = CGFloat
    static var defaultValue = CGFloat.zero
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value += nextValue()
    }
}

I keep track of two state variables: the offset, of course, and the placement of the title. I've tried to replicate as much as possible in the time I had the look and feel of standard NavigationBar. This solutin allows for deeper customisations as you can place any view you want in the overlay where the title is located. There is definitely room for improvement though. Result:

Custom Navbar and Title

Let me know if that works for you!