2

Is it possible to have both a NavigationView link coexist with a tap gesture (onLongPressGesture)!?

I can not make it work for the life of me...

ScrollView {   
                
    ForEach(self.itemStore.items) { p in
                    
        NavigationLink(destination: Details(p: p)) {

            CardDetector(p: p, position: self.position)
                    
        }
                    
    }

}

struct CardDetector: View {

    var p: ListData

    @State var position: CardPosition

    @Namespace var namespace

    var body: some View {
    
        Group {

        switch position {
            
            case .small:
                
                smallcardView(p: p, namespace: namespace)
                    .padding()
                    .frame(maxWidth: .infinity)
                    .frame(height: 120)
                    .background(BlurView(style: .regular))
                    .cornerRadius(10)
                    .padding(.vertical,6)
                    .onLongPressGesture {
                    
                        withAnimation {
                        
                            position = .big
                    
                        }
                
                    }
                    .padding(.horizontal)

This causes issues with scrolling... which they say the solution is to add a onTapGesture (according to the answer: Longpress and list scrolling) but then the NavigationLink wont work!?

ios coder
  • 1
  • 4
  • 31
  • 91
Learn2Code
  • 1,974
  • 5
  • 24
  • 46

2 Answers2

4

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 !!
            }
    }
}
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • can this be done with a scrollview as well or should list be used exclusively? I will try your solution soon... I hope it works for my use case! – Learn2Code Apr 15 '21 at 13:37
  • i get the following error `Missing argument for the parameter #1 in call - insert '<#LocalizedStringKey#>'` with the `.background(NavigationLink( destination: Text("Details \(number)"), isActive: $isActive) { EmptyView() })` – Learn2Code Apr 15 '21 at 15:43
  • So i just put a unique string to satisfy the error but your solution WORKED!!! Man you never disappoint!! – Learn2Code Apr 15 '21 at 16:04
2

It is a complicated project to explain, but I try: for having possibility to direct link to a View not just like a simple String or Text, I defined Content type which conform to View protocol in our Item struc. So you can link any View that you already designed.

For making tap actually be able open the Link we should work on custom Binding.

struct ContentView: View {
    
    @State private var items: [Item] = [Item(stringValue: "Link 0", destination: Text("Destination 0").foregroundColor(Color.red)),
                                        Item(stringValue: "Link 1", destination: Text("Destination 1").foregroundColor(Color.green)),
                                        Item(stringValue: "Link 2", destination: Text("Destination 2").foregroundColor(Color.blue))]
    
    var body: some View {
        
        NavigationView {
            
            Form {
                
                ForEach(items.indices, id: \.self ) { index in
                    
                    NavigationLink(destination: items[index].destination , isActive: Binding.init(get: { () -> Bool in return items[index].isActive },
                                                                                                  set: { (newValue) in items[index].isActive = newValue })) {
                        
                        Color.white.opacity(0.01)
                            .overlay(Text(items[index].stringValue), alignment: .leading)
                            .onTapGesture { items[index].isActive.toggle(); print("TapGesture! on Index:", index.description) }
                            .onLongPressGesture { print("LongPressGesture! on Index:", index.description) }
                        

                    }
                    
                }
                
            }
            
        }
        
    }

}

struct Item<Content: View>: Identifiable {
    let id: UUID = UUID()
    var stringValue: String
    var isActive: Bool = Bool()
    var destination: Content
}
ios coder
  • 1
  • 4
  • 31
  • 91