How to Make a Navigationlink with Tapgesture and Longpressgesture Working Simultaneously Together in Swiftui

How can I make a NavigationLink with TapGesture and LongPressGesture working simultaneously together in SwiftUI?

The solution is to separate link with gestures, making link activated programmatically. In such cases actions, gestures, and scrolling do not conflict.

Here is a simplified demo of possible approach. Tested with Xcode 12.4 / iOS 14.4

demo

struct ContentView: View {
var body: some View {
NavigationView {
List(0..<10) {
LinkCard(number: $0 + 1)
}
}
}
}

struct LinkCard: View {
let number: Int
@State private var isActive = false
@State private var isBig = false

var body: some View {
RoundedRectangle(cornerRadius: 12).fill(Color.yellow)
.frame(height: isBig ? 400 : 100)
.overlay(Text("Card \(number)"))
.background(NavigationLink(
destination: Text("Details \(number)"),
isActive: $isActive) {
EmptyView()
})
.onTapGesture {
isActive.toggle() // << activate link !!
}
.onLongPressGesture {
isBig.toggle() // << alterante action !!
}
}
}

How to perform an action after NavigationLink is tapped?

Yes, NavigationLink does not allow such simultaneous gestures (might be as designed, might be due to issue, whatever).

The behavior that you expect might be implemented as follows (of course if you need some chevron in the list item, you will need to add it manually)

struct TestSimultaneousGesture: View {
@State var showPlusButton = false
@State var currentTag: Int?
var body: some View {

NavigationView {
List {
ForEach(0 ..< 12) { item in
VStack {
HStack(alignment: .top) {
Text("List item")
NavigationLink(destination: Text("Details"), tag: item, selection: self.$currentTag) {
EmptyView()
}
}
.padding(EdgeInsets(top: 5, leading: 10, bottom: 5, trailing: 10))
.foregroundColor(.black)
Divider()
}
.simultaneousGesture(TapGesture().onEnded{
print("Got Tap")
self.currentTag = item
self.showPlusButton = false
})
.simultaneousGesture(LongPressGesture().onEnded{_ in
print("Got Long Press")
self.currentTag = item
self.showPlusButton = false
})
.onAppear(){
self.showPlusButton = true
}
}
}
}
}
}

How to get a function to run when you push a Navigation Link in SwiftUI

I can think of a couple solutions. First is to use onAppear on your destination view. This is less code than the second solution, but I have seen issues with onAppear being fired at unexpected times or more than once.

NavigationLink {
Success()
.navigationBarBackButtonHidden(true)
.onAppear {
print("Sending to Storage")
}
} label: {
Text("Finish")
}

The other solution is to bind a variable to the isActive property of NavigationLink, and then use onChange(of:perform:) to watch for changes and trigger your function when the value changes to true.

struct ContentView: View {
@State private var navIsActive = false

var body: some View {
HStack {
Spacer()

NavigationLink(isActive: $navIsActive) {
Success()
.navigationBarBackButtonHidden(true)
} label: {
Text("Finish")
}

Spacer()
}
.onChange(of: navIsActive) { newValue in
if newValue {
print("Sending to Storage")
}
}
}
}

SwiftUI LongPressGesture takes too long to recognize when TapGesture also present

To having some multi gesture that fits every ones needs in projects, Apple has nothing offer than normal gesture, mixing them together to reach the wished gesture some times get tricky, here is a salvation, working without issue or bug!

Here a custom zero issue gesture called interactionReader, we can apply it to any View. for having LongPressGesture and TapGesture in the same time.


enter image description here



import SwiftUI

struct ContentView: View {

var body: some View {

Circle()
.fill(Color.yellow)
.frame(width: 150, height: 150)
.interactionReader(longPressSensitivity: 250, tapAction: tapAction, longPressAction: longPressAction, scaleEffect: true)
.animation(Animation.easeInOut(duration: 0.2))

}

func tapAction() { print("tap action!") }

func longPressAction() { print("longPress action!") }

}


struct InteractionReaderViewModifier: ViewModifier {

var longPressSensitivity: Int
var tapAction: () -> Void
var longPressAction: () -> Void
var scaleEffect: Bool = true

@State private var isPressing: Bool = Bool()
@State private var currentDismissId: DispatchTime = DispatchTime.now()
@State private var lastInteractionKind: String = String()

func body(content: Content) -> some View {

let processedContent = content
.gesture(gesture)
.onChange(of: isPressing) { newValue in

currentDismissId = DispatchTime.now() + .milliseconds(longPressSensitivity)
let dismissId: DispatchTime = currentDismissId

if isPressing {

DispatchQueue.main.asyncAfter(deadline: dismissId) {

if isPressing { if (dismissId == currentDismissId) { lastInteractionKind = "longPress"; longPressAction() } }

}

}
else {

if (lastInteractionKind != "longPress") { lastInteractionKind = "tap"; tapAction() }

DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(50)) {lastInteractionKind = "none"}


}

}

return Group {

if scaleEffect { processedContent.scaleEffect(lastInteractionKind == "longPress" ? 1.5: (lastInteractionKind == "tap" ? 0.8 : 1.0 )) }
else { processedContent }

}

}

var gesture: some Gesture {

DragGesture(minimumDistance: 0.0, coordinateSpace: .local)
.onChanged() { _ in if !isPressing { isPressing = true } }
.onEnded() { _ in isPressing = false }

}

}


extension View {

func interactionReader(longPressSensitivity: Int, tapAction: @escaping () -> Void, longPressAction: @escaping () -> Void, scaleEffect: Bool = true) -> some View {

return self.modifier(InteractionReaderViewModifier(longPressSensitivity: longPressSensitivity, tapAction: tapAction, longPressAction: longPressAction, scaleEffect: scaleEffect))

}

}

How to make whole List row a NavigationLink in SwiftUI

The problem comes with your 'longPressGesture' which wants to control the content of your row and listen for longPresses. To avoid this you can use 'onTapGesture' to control the activation of the NavigationLink and 'onLongPressGesture' to activate your actionSheet or Alert.

Here is a short code example that demonstrates the usage:
https://stackoverflow.com/a/67099257



Related Topics



Leave a reply



Submit