UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Project 11 - Bookworm - issue with DetailView not displaying the genre ZStack for certain images.

Forums > 100 Days of SwiftUI

Hey all,

I'm working on the Bookworm project and I'm having a weird issue. Hoping y'all can give me some pointers on how to debug this.

I've built the project according to the book/videos, and everything is working EXCEPT... on the DetailView, the ZStack for the genre image/name does not work for Fantasy or Poetry. I get a scroll bar with an empty space. All the others: kids, horror, thriller, etc. are just fine.

I've checked the spelling of assets vs. my code and they match. And I can see by watching the videos that the Fantasy image works for Paul. So, if it's not the code, and it's not an issue with the asset names... what can I check for next? Since I know the code works, I'm not planning to lose too much sleep over it, and I'm going to attempt the challenges. But if it happened here in the classroom, it can happen out there in the real world, where I would lose sleep over it.

Also, if you could let me know how you create those windows with the code, I would really appreciate that. I see that on other forum posts and have no idea how to make those.

Thanks!

1      

@cameron wonders how to format Swift code in a forum message....

let me know how you create those windows with the code, I would really appreciate that

Here's a post from 2022 that I marked as a favorite.

See -> How to Create Those Windows with the Code

1      

Hi Obelix! Thanks for taking the time to look at my issue.

Here is the code for my DetailView. Let me know if anything pops out at you.

Lucas

struct DetailView: View {
    let book: Book
    @Environment(\.managedObjectContext) var moc
    @Environment(\.dismiss) var dismiss
    @State private var showingDeleteAlert = false

    func deleteBook() {
        moc.delete(book)

        try? moc.save()
        dismiss()
    }

    var body: some View {
        ScrollView {
            ZStack(alignment: .bottomTrailing) {
                Image(book.genre ?? "Fantasy")
                    .resizable()
                    .scaledToFit()

                Text(book.genre?.uppercased() ?? "FANTASY")
                    .font(.caption)
                    .fontWeight(.black)
                    .padding(8)
                    .foregroundStyle(.white)
                    .background(.black.opacity(0.75))
                    .clipShape(Capsule())
                    .offset(x: -5, y: -5)
            }

            Text(book.author ?? "Unknown author")
                .font(.title)
                .foregroundStyle(.secondary)

            Text(book.review ?? "No review")
                .padding()

            RatingView(rating: .constant(Int(book.rating)))
                .font(.largeTitle)

        }
        .navigationTitle(book.title ?? "Unknown Book")
        .navigationBarTitleDisplayMode(.inline)
        .toolbar {
            Button {
                showingDeleteAlert = true
            } label: {
                Label("Delete This Book", systemImage: "trash")
            }
        }
        .alert("Delete Book", isPresented: $showingDeleteAlert) {
            Button("Delete", role: .destructive, action: deleteBook)
            Button("Cancel", role: .cancel) {}
        } message: {
            Text("Are you sure?")
        }
    }
}

1      

Coding in SwiftUI isn't just a bunch of lessons where you learn proper syntax. Sometimes you have to step away from the proper coding, and take a close look at the logic.

Declarative Interface

SwiftUI is declarative. Read that part again. You declare what you want to see.

What is this code declaring?

var body: some View {

    ZStack(alignment: .bottomTrailing) {
            Image(book.genre ?? "Fantasy").resizable().scaledToFit()  // <- What is the genre?

            Text(book.genre?.uppercased() ?? "FANTASY") // <- Again? What is the genre?
                    .font(.caption)
            }
// .... snip .....
}

To me it looks like you declaring your intention to display a graphic if a book.genre exists. If it doesn't exist (is nil) in a Book object, you're declaring that you want to see the "Fantasy" image instead.

Also you're declaring that you want to display the book.genre as text (in uppercase letters) if it exists. Otherwise you're happy to categorize the book in the FANTASY genre.

Check Engine!

So if you state that DetailView isn't working for book with Fantasy or Poetry genres, it could be faulty graphics, or it could be your book isn't actually in the Fantasy, or Poetry genre.

So you need to add some debugging code! I call this process adding "dashboard lights" to your view. They remind me of the "check engine" lights you might see on your car's dashboard.

Try adding code like this:

var body: some View {
    VStack {
        // ------- CHECK ENGINE LIGHTS --------------
        Text( book.genre ) // <- will crash if nil. Otherwise show genre.
        Text( "Count: \(book.genre.count)") // <- How many characters in the genre?
        // ------ END OF CHECK ENGINE LIGHTS -----------

    ZStack(alignment: .bottomTrailing) {
            Image(book.genre ?? "Fantasy").resizable().scaledToFit()  // <- What is the genre?

            Text(book.genre?.uppercased() ?? "FANTASY") // <- Again? What is the genre?
                    .font(.caption)
            }
// .... snip .....
}

Keep Coding

Add a few and report back what you find for the Poetry and Fantasy books.

1      

Good morning @Obelix!

Finally had some time yesterday to sit down and implement the code you suggested. What I discovered was that for the poetry and fantasy genres, it had assigned an empty string to the genre variable for those categories.

I was able to troubleshoot fantasy quickly -- being the first entry in my string array, fantasy was the first choice on the picker. When I entered my book details, I didn't interact with the picker since fantasy was already selected. Of course, since I didn't use the picker, it didn't assign anything to my variable and it kept the empty string that I used to initialize the variable. I fixed this by changing my declaration to @State private var genre = "Fantasy" so I would have a default. This does not explain how my poetry entry ended up being assigned an empty string, but when I tried to replicate the problem, everything started working again.

Thank you for taking time to help me out. Learning how to wrap my head around these kinds of things is probably going to be my biggest challenge trying to make things work, especially when I get to where I'm trying to make my own projects. Thanks for hanging out on the forums and helping folks out.

All the Best, Lucas

1      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

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

Reply to this topic…

You need to create an account or log in to reply.

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.