Can’t make a button tappable parallel to navigationTitle

68 views Asked by At

enter image description here

The button is not tappable as I move it to the extended line of the title. If I move it to somewhere else, it works but I want to place it on the line of Title.

But whenever I do it, it does not tappable feels like its tappable area conflicts or masked by something. Is it SwiftUI limitation? If so, how this Health app made it work ? Don't say that it's because they're Apple :( Any help is appreciated in this matter!

Tried Moving the button to different places bottom of the navigationTitle line and it worked.

import SwiftUI

struct TestView: View {
    @State var isPresented = false
    var body: some View {
        
        NavigationStack {
            ScrollView {
                VStack {
                    Spacer()
                    HStack {
                        Spacer()
                        Button(action: {
                            print("tapped")
                        }) {
                            Text("XX") 
                                .foregroundColor(.black)
                                .font(.system(size: 20, weight: .bold))
                                .frame(width: 40, height: 40)
                                .background(Circle().strokeBorder(.blue, lineWidth: 4))
                                .background(Circle().foregroundColor(Color.white))
                                .padding()
                        }
                        .padding(.top, -80)
                        .background(
                            NavigationLink(destination: EmptyView(), isActive: $isPresented) {
                                EmptyView()
                            }
                        )
                    }
                }
                Text("Main Body")
            }.navigationBarTitle("Title", displayMode: .large)
        }
    }
}
1

There are 1 answers

2
Benzy Neez On

If you want the button to be permanently visible then you could make it a ToolbarItem with placement .topBarTrailing. However, it seems you want it to be adjacent to the navigation title and to scroll out of view with the title.

One solution would be to replace the native navigation title with your own header. This can contain any content you like and it can also be styled any way you like. The answer to Change NavigationStack title font, tint, and background in SwiftUI shows how it can be done (it was my answer).

I'm not quite clear why your button has a NavigationLink in the background. A NavigationLink is also a button. I would suggest using one or the other, but not both.

So here is how your example can be updated to use a self-styled header. An outer GeometryReader is not needed in your case, because the coordinate space of the ScrollView can be referenced directly (it was needed for the other answer I referenced above, because the main content there was a List):

struct TestView: View {

    @State var isPresented = false
    @State private var showingScrolledTitle = false

    private var scrollDetector: some View {
        GeometryReader { proxy in

            // pre iOS 17: the coordinate space of the ScrollView needs to be named
            let maxY = proxy.frame(in: .scrollView).maxY
            let isUnderToolbar = maxY < (proxy.size.height / 2)
            Color.clear
                // pre iOS 17: .onChange(of: isUnderToolbar) { newVal in
                .onChange(of: isUnderToolbar) { _, newVal in
                    showingScrolledTitle = newVal
                }
        }
    }

    var body: some View {
        NavigationStack {
            ScrollView {
                HStack {
                    Text("Title")
                        .font(.largeTitle)
                        .fontWeight(.bold)
                        .padding(.leading)
                    Spacer()

                    Button {
                        print("tapped")
                    } label: {
                        Text("XX")
                            .foregroundColor(.black)
                            .font(.system(size: 20, weight: .bold))
                            .frame(width: 40, height: 40)
                            .background(Circle().strokeBorder(.blue, lineWidth: 4))
                            .background(Circle().foregroundColor(Color.white))
                            .padding()
                    }
//                    .background(
//                        NavigationLink(destination: EmptyView(), isActive: $isPresented) {
//                            EmptyView()
//                        }
//                    )
                }
                .background { scrollDetector }

                Text("Main Body")
            }
            .navigationTitle("Title")
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .principal) {
                    Text("Title")
                        .font(.headline)
                        .opacity(showingScrolledTitle ? 1 : 0)
                        .animation(.easeInOut, value: showingScrolledTitle)
                }
            }
        }
    }
}

Animation