Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
4.5k views
in Technique[技术] by (71.8m points)

swift - How to create a NavigationLink or Button from a shape in SwiftUI that is only triggered by tapping on the shape but not its frame

I have a simple shape I want to use as a button but I would like to have the link/button only triggered when clicked on the shape itself but not on its frame. With the following code the link is also triggered when clicking inside the frame of the triangle:

struct Triangle: Shape {
    func path(in rect: CGRect)-> Path {
        var path = Path()

        path.move(to: CGPoint(x: rect.midX, y: rect.minY))
        path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
        path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
        path.addLine(to: CGPoint(x: rect.midX, y: rect.minY))

        return path
    }
}
struct ContentView: View {
    var body: some View {
        NavigationView {
            NavigationLink(
                destination: Text("DetailView")){
                Triangle()
                    .fill(Color.green)
                    .frame(width: 200, height: 200, alignment: .center)
            }.navigationBarTitle(Text("Triangle"))
        }
    }
}

How can I adjust the code to have only tapping the triangle but not the frame triggering the link?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can add a contentShape modifier to tell the system how to do hit testing for the NavigationLink:

NavigationLink(destination: Text("DetailView")){
    Triangle()
        .fill(Color.green)
        .frame(width: 100, height: 100, alignment: .center)
}
.contentShape(Triangle())

There's a caveat here, which is SwiftUI includes some slop in the hit testing, so this will still accept some clicks/touches that are close to the borders of the shape, but still outside. I don't have a great solution for getting rid of that slop. But, you can see that the same thing happens on the basic Rectangle that normally makes up the navigation view as well -- e.g. put a border around it and note that you can still tap slightly outside of that border.

A secondary way you could experiment with is render the Path on its own, attach an onTapGesture to it and turn on/off a boolean connected to the NavigationLink that you'd then want to move outside your hierarchy. Something like:

struct ContentView: View {
    @State var navigationLinkActive = false
    
    var body: some View {
        NavigationView {
            Group {
                if navigationLinkActive {
                    NavigationLink(destination: Text("Detail"), isActive: $navigationLinkActive) {
                        EmptyView()
                    }
                }
                Triangle()
                    .fill(Color.green)
                    .frame(width: 100, height: 100, alignment: .center)
                    .onTapGesture {
                        navigationLinkActive = true
                    }
            }.navigationBarTitle(Text("Triangle"))
            
            
            EmptyView()
            
        }
    }
}

Note that in this solution, you still have the hit slop issue, plus you lose the touch down/up highlighting that the Button has.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...