TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

NavigationLink in List pushes the wrong row

Forums > SwiftUI

I want to be able to edit a post that the user has created. I am showing the created posts in a List, and once the user taps on a row it should open a view where the user can edit it. I am pushing the isActive variable because i am going to pop to the root from another view.

Flow: Root (with the list of created posts) -> View to edit the post -> View to share the post (with button to go back to the root).

The issue is that every time i tap on a row it pushes the view but the wrong post is loaded.

    @Environment(\.managedObjectContext) var moc
    @State private var showEditPost = false
    var body: some View { 
        NavigationView {
                    VStack(spacing: 40) {
                        if posts.count > 0 {
                            List {
                                ForEach(posts, id: \.self) { post in
                                    NavigationLink(
                                        destination: CreateNewPostView(postCoreData: post, isEditPostActive: $showEditPost),
                                        isActive: $showEditPost,
                                        label: {
                                            HStack {
                                                post.profileImage
                                                    .resizable()
                                                    .scaledToFit()
                                                    .frame(width: 54, height: 54)
                                                    .cornerRadius(50)
                                                VStack(alignment: .leading, spacing: 8) {
                                                    Text(post.name ?? "nil name")
                                                        .font(.headline)
                                                    HStack {
                                                        if let postContent = post.postContent {
                                                            let firstChars = String(postContent.prefix(50))
                                                            Text("\(firstChars) \(postContent.count > 50 ? "..." : "")")
                                                                .font(.body)
                                                        } else {
                                                            post.postImage
                                                                .resizable()
                                                                .scaledToFit()
                                                                .frame(height: 20)
                                                        }
                                                        post.reaction
                                                            .resizable()
                                                            .scaledToFit()
                                                            .frame(height: 20)
                                                        Text(post.likeCount)
                                                            .font(.caption)
                                                            .foregroundColor(Color.secondary)
                                                    }
                                                }
                                            }
                                            .padding(.vertical, 8)
                                        })
                                }.onDelete(perform: { indexSet in
                                    deletePost(indexSet: indexSet)
                                })
                            }
                            .listStyle(InsetGroupedListStyle())
                        } else {

Any idea what I'm doing wrong?

2      

It's not clear from the code you shared.

1- Your first condition is better being if posts.isEmpty == false

2- What is in your posts array? How is that populated. That might be where the issue lies.

3- For clarity purposes you might want to consider the NavigationLink line to be slightly different

NavigationLink(
      destination: CreateNewPostView(postCoreData: post, isEditPostActive: $showEditPost),                       
      isActive: $showEditPost) {
          HStack { ... }
          ...
      }

4- You would also be better off having a separate View, or computed property or function to create each row. For simplicity I would use a function that returns some View

I realize this does not directly answer your question, but the more information and clarity you provide to readers of your code, the better they can help! 😉

2      

I agree with @MarcusKay you have ForEach(posts, id: \.self) and if self is not unique then it will pick the best fit which might not be the correct one. You better if the posts has let id = UUID() and then you can do ForEach(posts, id: \.id)

PS if it a struct you need to make itIdentifiable

2      

Thank you for the replies:

From the code below you can see how's it's loaded and it should be more clear:

    @FetchRequest(entity: Post.entity(), sortDescriptors: []) private var posts: FetchedResults<Post>
    @State private var showEditPost = false

    var body: some View {

        NavigationView {
            VStack(spacing: 40) {
                if posts.isEmpty == false {
                    List {
                        ForEach(posts, id: \.id) { post in
                            NavigationLink(
                                destination: CreateNewPostView(postCoreData: post, isEditPostActive: $showEditPost),
                                isActive: $showEditPost)
                                {
                                    HStack {
                                        post.profileImage
                                        VStack(alignment: .leading, spacing: 8) {
                                            Text(post.name ?? "nil name")
                                                .font(.headline)
                                        }
                                    }
                                }
                        }

                  .
                  .
                  .
    }

public class Post: NSManagedObject, Identifiable {

    @NSManaged public var id: UUID?
    @NSManaged public var createdAt: Date
    @NSManaged public var profileImageData: Data?
    @NSManaged public var name: String!
    .
    .
    .
}

The posts are loaded correctly in the List. The issue is that the navigationLink loads the wrong "post".

2      

Could it be that CreateNewPostView is where the problem is?

It might have to do with something there!

Also, which post gets shown? As in, what is the relationship between the post you click and the one that is shown. Is it the next one down the list? or is it some random post? In which case, you might want to try using a sort descriptor and seeing if that fixes the issue.

2      

Thank you for the reply. I think I found the issue, but I don't know how to fix it. I Will post my code again because I made some changes and it will make it easier for you:

@FetchRequest(entity: Post.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Post.createdAt, ascending: true)]) var posts: FetchedResults<Post>
    @State private var showEditPost = false
    var body: some View {
        NavigationView {
                    List{
                        ForEach(posts, id: \.id) { post in
                            let poststruct = PostStruct(postCoreData: post)
                            NavigationLink( destination: CreateNewPostView(post: poststruct, isEditPostActive: $showEditPost),
                                isActive: $showEditPost) {
                                Text(poststruct.name)
                            }
                        }
                    }
                    .
                    .
                    .
            }

If I remove the line:

                                isActive: $showEditPost) {

This solves the issue but obviously I'm not able to dismiss the ViewC to go back to the rootView where this code is. This is the flow i need: rootView (where the list is), tap on a row -> ViewB, tap on a button -> ViewC, tap on a button -> pop to rootView

2      

I might be able to provide you with a link to that issue from an article I remember reading, I just need to sift through a bit to find it. Will update you as soon as I find it.

2      

Did you try this solution on this forum? It's a few months old, but might help. I couldn't find my link, but a quick search showed this:

https://www.hackingwithswift.com/forums/swiftui/pop-to-parent-view-via-bar-button/1295

2      

Thank you but I can't apply that to a row of the list.

I tried the solution with NavigationLink(destination, tag, selection). I can't find a way to go back to the root.

I created a sample project:

import SwiftUI

struct ContentView: View {
    @State var selectedView: Int? = nil
    var colors: [String] = ["blue", "yellow", "green", "red", "black"]

    var body: some View {
        NavigationView {
            List{
                ForEach(colors.indices) { index in
                    NavigationLink(
                        destination: ColorDetail(selectedView: self.$selectedView, color: colors[index]),
                        tag: index,
                        selection: self.$selectedView,
                        label: {
                            Text(colors[index])
                        })
                }
            }
        }
    }
}

struct ColorDetail: View {

    @Binding var selectedView: Int?
    var color: String

    var body: some View {
        VStack {
            Text(color)
            Text("SelectedView: \(selectedView ?? 99)")
            Button("set SelectedView to nil and go back") {
                self.selectedView = nil
            }
        }
    }
}

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

I would like to go back to the root (ContentView) using the button in the ColorDetail view but setting the selectedView to nil doesn't work

2      

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.