0

so I'm having a bit of an issue here I'm hoping is easy to fix, just can't figure it out at the moment. I'm running a loop through some CoreData info (posts) and returning a grid of images, I want to be able to click these images and open up a fullScreenCover of the DetailView with the correct info in it. With the current code, the DetailView always shows the data from the first post. If I change it from a Button to a NavigationLink NavigationLink(destination: DetailView(post: post)), as commented out in the code, it works perfectly, but doesn't give me the fullScreenCover behaviour I would like. What am I doing wrong here? Thanks in advance!

@FetchRequest(entity: Post.entity(), sortDescriptors: []) var posts: FetchedResults<Post>

enum ActiveSheet: Identifiable {
        case detail, addNew
        
        var id: Int {
            hashValue
        }
    }
@State var activeSheet: ActiveSheet?

var body: some View {

ForEach(posts.reversed(), id: \.self) { post in
    
    VStack {
    Button(action: { activeSheet = .detail }){
    //NavigationLink(destination: DetailView(post: post)){
    ZStack {
    Image(uiImage: UIImage(data: post.mainImage ?? self.image)!)
        VStack {
        Text("\(post.title)")
        Text("\(post.desc)")
        }
    }
}
.fullScreenCover(item: $activeSheet) { item in
            switch item {
            case .detail:
                DetailView(post: post)
            case .addNew:
                AddNewView()
            }
        }
}
}
}
SeanForReal
  • 131
  • 8
  • It's best practice to avoid any conditional logic within the .sheet and .fullScreenCover closure. Solutions here: https://stackoverflow.com/questions/65416673/multiple-bottom-sheets-the-content-doesnt-load-swiftui/65416999#65416999 – nicksarno Feb 08 '21 at 18:25

1 Answers1

2

I've made the array of posts static for now instead of coming from Core Data and mocked the objects/structs so that I could test easily, but the principal should stay the same:

struct ContentView : View {
    //@FetchRequest(entity: Post.entity(), sortDescriptors: []) var posts: FetchedResults<Post>
    var posts : [Post] = [Post(title: "1", desc: "desc1"),
                          Post(title: "2", desc: "desc2"),
                          Post(title: "3", desc: "desc3")]
    
    enum ActiveSheet: Identifiable {
        case detail(post: Post)
        case addNew
        
        var id: UUID {
            switch self {
            case .detail(let post):
                return post.id
            default:
                return UUID()
            }
        }
    }
    @State var activeSheet: ActiveSheet?
    
    var body: some View {
        
        ForEach(posts.reversed(), id: \.self) { post in
            
            VStack {
                Button(action: { activeSheet = .detail(post: post) }){
                    ZStack {
                        //Image(uiImage: UIImage(data: post.mainImage ?? self.image)!)
                        VStack {
                            Text("\(post.title)")
                            Text("\(post.desc)")
                        }
                    }
                }
            }
            .fullScreenCover(item: $activeSheet) { item in
                switch item {
                case .detail(let post):
                    DetailView(post: post)
                case .addNew:
                    AddNewView()
                }
            }
        }
    }
}

struct DetailView : View {
    var post: Post
    
    var body : some View {
        Text("Detail \(post.id)")
    }
}

struct AddNewView : View {
    var body : some View {
        Text("add")
    }
}

struct Post : Hashable {
    var id = UUID()
    var title : String
    var desc : String
}

The basic idea is that instead of creating the fullScreenCover on first render, you should create it in based on the activeSheet so that it gets created dynamically. You were on the right track using item: and activeSheet already -- the problem was it wasn't tied to the actual post, since you were just using the button to set activeSheet = .detail.

I've added an associated property to case detail that allows you to actually tie a post to it. Then, in fullScreenCover you can see that I use that associated value when creating the DetailView.

You may have to make slight adjustments to fit your Post model, but the concept will remain the same.

jnpdx
  • 45,847
  • 6
  • 64
  • 94