Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to have multiple NavigationLinks per row in a List SwiftUI?

Tags:

swiftui

I can't use multiple NavigationLinks in the same row of a List.

It looks like the navigation stack is totally messed up, because you tap once and it goes to multiple views and back erratically...

in TestList, I've tried adding the separate NavigationLinks in Sections, and I've tried moving the NavigationLinks two different places in the view hierarchy...

I've tried adding two NavigationViews for each row of the list, but then the navigationTitleBar don't go away when I need it to..


struct ContentView: View {
    var body: some View {

        NavigationView {
            TestList()
        }


    }
}


struct TestList: View {
    var body: some View {

        List  {
            ListCellView()

        }

    }
}


struct ListCellView: View {

    var body: some View {
        VStack {
            Spacer()

            NavigationLink(destination: TestDestination1())  {
                Text("Test Destination 1")
                    .frame(width: 140, height: 50)
                    .background(RoundedRectangle(cornerRadius: 7.0).strokeBorder(Color.green, lineWidth: 3.0))
            }

            Spacer()

            NavigationLink(destination: TestDestination2())  {
                Text("Test Destination 2")
                    .frame(width:140, height: 50)
                    .background(RoundedRectangle(cornerRadius: 7.0).strokeBorder(Color.purple, lineWidth: 3.0))
                Spacer()
            }
        }
    }
}


struct TestDestination1: View {
    var body: some View {
        Text("Test Destination 1")
    }
}

struct TestDestination2: View {
    var body: some View {
        Text("Test Destination 2")
    }
}


I expect that when you tap a NavigationLink, it will navigate to the destination view.

What happens is when two NavigationLinks are in the same row of a List and you tap in it, it will: 1. go to one of the views 2. After tapping 'back', it will take you back to the view AND THEN take you to the other destination view.

https://youtu.be/NCTnqjzJ4VE

like image 503
nSquid Avatar asked Sep 09 '19 16:09

nSquid


2 Answers

As the others mentioned, why 2 NavigationLinks in 1 cell. The issue is with multiple buttons and gesture in general for the Cell. I guess it is expected 1 Button/NavigationLink max per cell. As you noticed, on your video, you tap on a NavigationLink but your full Cell got the gesture (highlighted), which in return impact the other Buttons/NavigationLinks.

Anyhow, you can have it working, the 2 NavigationLinks in 1 cell, with a hack. Below I have created SGNavigationLink, which I use for my own app which solve your issue. It simply replace NavigationLink and based on TapGesture, so you will lose the highlight.

NB: I slightly modified your ListCellView, as the Spacer within my SGNavigationLink was creating an internal crash.

struct ListCellView: View {

    var body: some View {
        VStack {

            HStack{
                SGNavigationLink(destination: TestDestination1())  {
                    Text("Test Destination 1")
                        .frame(width: 140, height: 50)
                        .background(RoundedRectangle(cornerRadius: 7.0).strokeBorder(Color.green, lineWidth: 3.0))
                }

                Spacer()
            }

            HStack{
            SGNavigationLink(destination: TestDestination2())  {
                Text("Test Destination 2")
                    .frame(width:140, height: 50)
                    .background(RoundedRectangle(cornerRadius: 7.0).strokeBorder(Color.purple, lineWidth: 3.0))

            }
            Spacer()
            }
        }
    }
}



struct SGNavigationLink<Content, Destination>: View where Destination: View, Content: View {
    let destination:Destination?
    let content: () -> Content


    @State private var isLinkActive:Bool = false

    init(destination: Destination, title: String = "", @ViewBuilder content: @escaping () -> Content) {
        self.content = content
        self.destination = destination
    }

    var body: some View {
        return ZStack (alignment: .leading){
            if self.isLinkActive{
                NavigationLink(destination: destination, isActive: $isLinkActive){Color.clear}.frame(height:0)
            }
            content()
        }
        .onTapGesture {
            self.pushHiddenNavLink()
        }
    }

    func pushHiddenNavLink(){
        self.isLinkActive = true
    }
}
like image 97
Fabrice Leyne Avatar answered Jan 22 '23 19:01

Fabrice Leyne


I am not sure about why do you need multiple Navigationlinks (duplicate code). You can use a data source that will hold the required properties of the list [title, color, id etc] and based on the id, call the desired View. Reuse the same code. Here is an example.

struct TestList: View {
    var body: some View {
        List { // <- Use Data source 
            ForEach(0..<2) { index in
                 ListCellView(index: index)
            }
        }
    }
}

struct ListCellView: View {
    var index: Int
    var body: some View {
         return   NavigationLink(destination: ViewFactory.create(index))  {
                Text("Test Destination 1")
                    .frame(width: 140, height: 50)
                    .background(RoundedRectangle(cornerRadius: 7.0).strokeBorder(Color.green, lineWidth: 3.0))
            }
    }
}


class ViewFactory {
   static func create(_ index: Int) -> AnyView {
        switch index {
        case 0:
            return AnyView(TestDestination1())
        case 1:
            return AnyView(TestDestination2())
        default:
           return AnyView(EmptyView())
        }
    }
}

struct TestDestination1: View {
    var body: some View {
        Text("Test Destination 1")
    }
}

struct TestDestination2: View {
    var body: some View {
        Text("Test Destination 2")
    }
}
like image 22
Razib Mollick Avatar answered Jan 22 '23 20:01

Razib Mollick