Как создать NavigationLink или Button из фигуры в SwiftUI, которая запускается только при нажатии на фигуру, но не на ее рамку

У меня есть простая форма, которую я хочу использовать в качестве кнопки, но я бы хотел, чтобы ссылка / кнопка срабатывала только при нажатии на саму форму, но не на ее рамку. В следующем коде ссылка также активируется при нажатии внутри рамки треугольника:

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"))
        }
    }
}

Как я могу настроить код так, чтобы касался только треугольника, но не кадра, запускающего ссылку?


person BeSCoNeZ    schedule 19.01.2021    source источник


Ответы (1)


Вы можете добавить модификатор contentShape, чтобы сообщить системе, как выполнять проверку попадания для NavigationLink:

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

Здесь есть предостережение: SwiftUI включает некоторую задержку при проверке нажатия, поэтому он все равно будет принимать некоторые щелчки / касания, которые близки к границам фигуры, но все еще находятся за ее пределами. У меня нет отличного решения, чтобы избавиться от этих помоев. Но вы можете видеть, что то же самое происходит с базовым Rectangle, который обычно также составляет представление навигации - например, обведите его border и обратите внимание, что вы все еще можете слегка касаться за пределами этой границы.

Второй способ, с которым вы можете поэкспериментировать, - это визуализировать Path самостоятельно, присоединить к нему onTapGesture и включить / выключить логическое значение, связанное с NavigationLink, которое вы затем захотите переместить за пределы своей иерархии. Что-то вроде:

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()
            
        }
    }
}

Обратите внимание, что в этом решении у вас по-прежнему есть проблема с задержкой попадания, плюс вы теряете подсветку касания вниз / вверх, как у Button.

person jnpdx    schedule 19.01.2021
comment
Большое спасибо! Модификатор .contentShape - это то, что я искал. В первых тестах у меня все получилось. - person BeSCoNeZ; 20.01.2021