NEW: Join my free 100 Days of SwiftUI challenge today! >>

Showing book details

Paul Hudson    @twostraws   

When the user taps a book in ContentView we’re going to present a detail view with some more information – the genre of the book, their brief review, and more. We’re also going to reuse our new RatingView, and even customize it so you can see just how flexible SwiftUI is.

To make this screen more interesting, we’re going to add some artwork that represents each category in our app. I’ve picked out some artwork already from Unsplash, and placed it into the project11-files folder for this book – if you haven’t downloaded them, please do so now and then drag them into your asset catalog.

Unsplash has a license that allows us to use pictures commercially or non-commercially, with or without attribution, although attribution is appreciated. The pictures I’ve added are by Ryan Wallace, Eugene Triguba, Jamie Street, Alvaro Serrano, Joao Silas, David Dilbert, and Casey Horner – you can get the originals from https://unsplash.com if you want.

Next, create a new SwiftUI view called “DetailView”. This only needs one property, which is the book it should show, so please add that now:

let book: Book

Even just having that property is enough to break the preview code at the bottom of DetailView.swift. Previously this was easy to fix because we just sent in an example object, but with Core Data involved things are messier: creating a new book also means having a managed object context to create it inside.

To fix this, we can update our preview code to create a temporary managed object context, then use that to create our book. Once that’s done we can pass in some example data to make our preview look good, then use the test book to create a detail view preview.

Creating a managed object context means we need to start by importing Core Data. Add this line near the top of DetailView.swift, next to the existing import:

import CoreData

As for the previews code itself, replace whatever you have now with this:

struct DetailView_Previews: PreviewProvider {
    static let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)

    static var previews: some View {
        let book = Book(context: moc)
        book.title = "Test book"
        book.author = "Test author"
        book.genre = "Fantasy"
        book.rating = 4
        book.review = "This was a great book; I really enjoyed it."

        return NavigationView {
            DetailView(book: book)
        }
    }
}

As you can see, creating a managed object context involves telling the system what concurrency type we want to use. This is another way of saying “which thread do you plan to access your data using?” For our example, using the main queue – that’s the one the app was launched using – is perfectly fine.

With that done we can turn our attention to more interesting problems, namely designing the view itself. To start with, we’re going to place the category image and genre inside a ZStack, so we can put one on top of the other nicely. This in turn means going inside a GeometryReader, so we can make sure the image doesn’t take up too much space. I’ve picked out some styling that I think looks good, but you’re welcome to experiment with the styling all you want.

Replace the current body property with this:

GeometryReader { geometry in
    VStack {
        ZStack(alignment: .bottomTrailing) {
            Image(self.book.genre ?? "Fantasy")
                .frame(maxWidth: geometry.size.width)

            Text(self.book.genre?.uppercased() ?? "FANTASY")
                .font(.caption)
                .fontWeight(.black)
                .padding(8)
                .foregroundColor(.white)
                .background(Color.black.opacity(0.75))
                .clipShape(Capsule())
                .offset(x: -5, y: -5)
        }
    }
}
.navigationBarTitle(Text(book.title ?? "Unknown Book"), displayMode: .inline)

That places the genre name in the bottom-right corner of the ZStack, with a background color, bold font, and a little padding to help it stand out.

Below that Stack we’re going to add the author, review, and rating, plus a spacer so that everything gets pushed to the top of the view. We don’t want users to be able to adjust the rating here, so instead we can use another constant binding to turn this into a simple read-only view. Even better, because we used SF Symbols to create the rating image, we can scale them up seamlessly with a simple font() modifier, to make better use of all the space we have.

So, add these views directly below the previous ZStack:

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

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

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

Spacer()

That completes DetailView, so we can head back to ContentView.swift to change the navigation link so it points to the correct thing:

NavigationLink(destination: DetailView(book: book)) {

Now run the app again, because you should be able to tap any of the books you’ve entered to show them in our new detail view.

LEARN SWIFTUI FOR FREE I have a massive, free SwiftUI video collection on YouTube teaching you how to build complete apps with SwiftUI – check it out!

BUY OUR BOOKS
Buy Pro Swift Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift (Vapor Edition) Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Server-Side Swift (Kitura Edition) Buy Beyond Code

Was this page useful? Let us know!

Average rating: 5.0/5