Search code examples
swiftuilazyvgridswiftui-sheet

Sheet from Lazyvgrid.. again


I know this has been asked before, but I just can't figure out why it isn't working for me. I'm pretty new to coding, so any help would be appreciated.

Trying to have BookSheetView open as a sheet after selecting a cell in my lazyvgrid.

struct LibraryView: View {
    @State private var showingSheet = false
    
    let book: Book
    
    let spacing: CGFloat = 10
    
    var gridItems: [GridItem] {
        [GridItem(.adaptive(minimum: 180, maximum: 180))]
    }
    
    var body: some View {
        Text("Text to come")
            .multilineTextAlignment(.leading)
            .navigationTitle("Library")        
        
        ScrollView {
            LazyVGrid(columns: gridItems,
                      spacing: spacing
            )
            { ForEach(books, id: \.self) { book in
                
                Button {
                    showingSheet = true
                } label: {
                    BookTileModel(book: book)
                }
                
                //                NavigationLink(destination: BookSheetView(book:book),
                //                               label: {BookTileModel(book: book)})
                
            }
            }
        }
        //  Start of sheet
        .sheet(isPresented: $showingSheet) {
            BookSheetView(book: book)
        }
        // End of sheet
    }
    
}

struct LibraryView_Previews: PreviewProvider {
    static var previews: some View {
        LibraryView(book: books[1])
    }
}

If I use a button, I can get the sheet to open to the right view, but it's not passing the information to the sheet, and if I use a NavigationLink, I get the right information, but as a full page, not a sheet.

I've looked at a bunch of similar posts and watched some tutorials, but I just can't quite figure it out :(

UPDATED CODE

import Foundation
import SwiftUI

extension String: Identifiable {
    public var id: String { self }
}

struct GridView: View {

    @State private var selected: String? = nil

    let book: Book

    var gridItems: [GridItem] {
        [GridItem(.adaptive(minimum: 180, maximum: 180))]
    }

    var body: some View {

        ScrollView (showsIndicators: false) {
                        LazyVGrid(columns: gridItems) {
                            ForEach(book, id: \.self) { item in   //ERROR Generic struct 'ForEach' requires that 'Book' conform to 'RandomAccessCollection'

                                Button(action: {
                                    selected = item
                                }) {
                                    BookTileModel(book: book)
                                }
                            }
                        }
                    }.sheet(item: $selected) { item in
                        BookSheetView(book: item)  // ERROR Cannot convert the value of type 'String' to expected argument type 'Book'
                    }
                 
                }
            }

Solution

  • Here is a working example, notice the comments:

    struct ContentView: View {
        
        let books: [Book] = [
            Book(title: "Anna Karenina", author: "Tolstoi"),
            Book(title: "To Kill a Mockingbird", author: "Harper Lee"),
            Book(title: "The Great Gatsby", author: "F. Scott Fitzgerald"),
            Book(title: "One Hundred Years of Solitude", author: "Gabriel García Márquez "),
            Book(title: "Mrs. Dalloway", author: "Virginia Woolf’"),
            Book(title: "Jane Eyre", author: "Charlotte Brontë’"),
        ]
        
        @State private var selected: Book? = nil // selection has to be an optional of the Type you use in the ForEach, here Book?
        
        var gridItems: [GridItem] {
            [GridItem(.adaptive(minimum: 180, maximum: 180))]
        }
        
        var body: some View {
            
            ScrollView (showsIndicators: false) {
                LazyVGrid(columns: gridItems) {
                    ForEach(books) { item in   // you cant ForEach over one book, it has to be an array of books = [Book]
                        
                        Button(action: {
                            selected = item
                        }) {
                            BookTileModel(book: item)
                        }
                    }
                }
            }.sheet(item: $selected) { item in
                BookSheetView(book: item)  // now that selected is of Type Book, this should work
            }
        }
    }