iosswiftuiswiftui-navigationview

SwiftUI How to change page from different list cell?


I listened by @sonle to using NavigationStack. I think something is going wrong. It does navigate but not like my expecting to destination to Travel Detail. It looks like this.

enter image description here

NavigationLink(destination: {
TravelDetail(TourID: item.id)
}, label: {
TourItemView(tableItem: item)
})

------------THE QUESTION ------------

I have a ScrollView whose content is generated by ForEach parsing the elements in the struct Array. I hope that clicking on different cells will lead to a Detail page and load the information of the corresponding objects. How can I achieve this?

I wrap the style of each cell in a button and plan to perform the page change action through the button click event. At the same time, I retrieve the id of the element in the list page and load the content into the Detail page after jumping to the page. I try to use NavigationView is used to jump, but the placement of NavigationLink confuses me. I want to know if this is a normal method.

I think this is a basic question, but I am stumped. As you can see, I am a beginner, so if you have any reference methods, please provide it to me. Thank you very much!

And if is there a need to provide other parts of the code, please let me know.

List Page:

enter image description here

import SwiftUI


struct ContentView: View {
    var tableItems: [TableItem]
    
   

        
    init() {
        var items = [TableItem]()
        for index in 0..<tour.count {
            let tourItem = [tour[index]]
            let userItem = [user[index]]
            let tableItem = TableItem(tourItem: tourItem, user: userItem)
            items.append(tableItem)
        }
        self.tableItems = items
        
    }
    
    var body: some View {
        NavigationView {
            VStack {
                            ScrollView(.vertical){
                                VStack(spacing: 8){
                                    ForEach(tableItems, id: \.id) { item in
                                        Button(action: {
                                           
                                        }) {
                                            TourItemView(tableItem: item)
                                        }//ButtonStyle
                                        
                                        
                                        
                                    }//ForEach
                                    
                                }//VStack
                            }//ScrollView
                            
                        }//VStack
                        .padding()

        }//NavigationView
            
        }//view
    
}//ContentView

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


Detail Page:

enter image description here

import SwiftUI




struct TravelDetail: View {
    
    //let DetailItem: TravelDetailConstant
    
    var ItemID: String
    var result: TravelDetailConstant?
    
    init(TourID: String) {
        self.ItemID = TourID
        
        result = initializersTourItem(id: ItemID)
        
    }
    
    func initializersTourItem(id: String)-> TravelDetailConstant {
        guard let register = ContentView().tableItems.first(where: { $0.id == id }),
                  let initTour = tour.first(where: { $0.id == id }),
                  let initUser = user.first(where: { $0.id == initTour.mainKeeper })
        else {
            return TravelDetailConstant(
                        imagetext: "",
                        title: "",
                        startTime: "",
                        finishTime: "",
                        place: "",
                        peopleNum: "",
                        detailLocaltion: "",
                        meetingPlace: "",
                        tag: nil,
                        detailContent: "",
                        userImage: "",
                        userNikename: "",
                        userGender: "",
                        userBadge: []
                    )
        }
        
        let detail = TravelDetailConstant(imagetext: register.tourImage, title: register.title, startTime: register.startTime, finishTime: initTour.finishTime, place: register.location, peopleNum: register.peopeleNum, detailLocaltion: initTour.detailLocaltion, meetingPlace: initTour.meetingPlace, tag: register.tag, detailContent: initTour.detailContent, userImage: register.userImage, userNikename: register.userName, userGender: initUser.userGender, userBadge: initUser.userBadge)
        
        return detail
        
    }
    
    var body: some View {
        VStack{
            Spacer()
                .frame(height: 20)
            HStack{
                Image((result?.userImage)!)
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .frame(width: 75, height: 75)
                    .clipShape(Circle())
                    .background(
                                    Circle()
                                        .padding(-2)
                                        .foregroundColor(Color("DesignLightMutedOrange"))
                                )
                    .padding(.leading, 15)
                VStack{
                    HStack{
                        Text((result?.userNikename)!)
                            .frame(width: 186, height: 28)
                            .padding(.trailing, 12)
                        Image((result?.userGender)!)
                            .resizable()
                            .aspectRatio(contentMode: .fill)
                            .frame(width: 24, height: 24)
                    }
                    .padding(.bottom, 4)
                    ScrollView(.horizontal) {
                        
                        Button(action: {}) {
                                                Image("")
                                            }
                    }
                    .frame(width: 186, height: 28)
                }
                Button(action: { }) {
                    Image("heart")
                        .resizable()
                        .aspectRatio(contentMode: .fill)
                        .frame(width: 24, height: 24)
                        .padding(.trailing, 15)
                }
            }
            Spacer().frame(height: 20)
                    HStack{
                        Button(action: {
                            
                        }){
                            Text("Detail")
                        }
                        .frame(width: 150, height: 52, alignment: .leading)
                        .background(Color .red)
                        .padding(.leading, 0)
                        Button(action: {
                            
                        }){
                            Text("Q&A")
                        }
                        .frame(width: 150, height: 52, alignment: .leading)
                        .background(Color .gray)
                        .listRowInsets(EdgeInsets())
                        Spacer()
                    }
            Spacer()
            }
        
    }
}

struct TravelDetail_Previews: PreviewProvider {
    static var previews: some View {
        TravelDetail(TourID: "")
    }
}

Solution

  • Since NavigationView was deprecated, I suggest using NavigationStack instead. Something like:

    struct ContentView: View {
        @State private var path = NavigationPath()
        var body: some View {
            NavigationStack(path: $path) {
                VStack {
                    ....
                    //Other stuff
                    Button(action: {
                        path.append(item)
                    }, label: {
                        TourItemView(tableItem: item)          
                    })
    
                    //OR
                    //NavigationLink(value: item) {
                    //    TourItemView(tableItem: item)
                    //}
                }
                .navigationDestination(for: TableItem.self) { item in
                    TourItemView(tableItem: item)
                }
            }
        }
    }
    

    Your TableItem model needs to fully conform to Hashable protocol.

    struct TableItem: Hashable {
        ...
    }