Your Second iOS & SwiftUI App

Nov 4 2021 Swift 5.5, iOS 15, Xcode 13

Part 1: List View Fundamentals

8. Challenge: View Reuse

Lesson Complete

Play Next Lesson
Next
Save for later
About this episode
See versions

See course reviews

See forum comments
Cinema mode Mark as Complete Download course materials
Previous episode: 7. Navigation Next episode: 9. Conclusion

This video was last updated on Nov 4 2021

You’re now displaying what we’re calling a “detail view” for a book. But after you navigate to it, you’re actually seeing fewer details than in the list. That doesn’t make any sense!

We can at least show the author and the whole title! Can you can create another reusable view, that can be used by both BookRow, and DetailView to help you out with that?

Also, make the image a lot bigger, taking up as much screen space as possible (minus a bit of padding from the edges).

Here’s a hint for the image: right now, you’re setting the width and height for the image, using CGFloat constants with values of 80. But those values are optional.

And if you set them to nil, you’ll get the same result as having no frame modifier. Which means that the book image symbol will resize to fit its parent view.

Good luck with the challenge; see you soon!

Hi! Welcome back. Hopefully you had some success! Let’s compare our solutions. First, I isolated what I thought was going to make a good reusable view: this VStack in BookRow’s body.

        VStack(alignment: .leading) {

I extracted it into a new view, calling it TitleAndAuthorStack.

      HStack {
        Book.Image(title: book.title)
        TitleAndAuthorStack()
      }
    ContentView()
  }
}

struct TitleAndAuthorStack: View {

That gave me errors, because it didn’t have a book to work with. So I gave it one.

struct TitleAndAuthorStack: View {
  let book: Book

  var body: some View {

And, passed the correct book along.

TitleAndAuthorStack(book: book)

For the detail View, I needed to get rid of this line limit because I wanted to show the whole title and author name. I moved it back to its original place in BookRow

        TitleAndAuthorStack(book: book)
          .lineLimit(1)

Then I moved TitleAndAuthorStack to the top of the Book views file.

I put it here because I’m using it in more than one place in the project. It doesn’t belong in ContentView anymore.

import SwiftUI

struct TitleAndAuthorStack: View {
  let book: Book

  var body: some View {
    VStack(alignment: .leading) {
      Text(book.title)
        .font(.title2)
      Text(book.author)
        .font(.title3)
        .foregroundColor(.secondary)
    }
    .lineLimit(1)
  }
}

extension Book {

Then I was ready to try out the new view in DetailView.

I just added it to the top of the VStack, passing in the book.

    VStack {
      TitleAndAuthorStack(book: book)
      Book.Image(title: book.title)

Everything I needed was there, but it still needed some layout work. I needed a bigger image! So, I went to Book Image…

…and gave it a size variable, with a type of Optional CGFloat.

    let title: String
    var size: CGFloat?

    var body: some View {

This doesn’t actually need to be a variable, instead of a constant. But that’s the easiest way to provide a default value of nil, that’s over-writable. Then, I supplied the size to the frame modifier we had.

.frame(width: size, height: size)

…which gave me larger previews. That was nice.

It also gave me the larger detail image, as we needed. I put some padding on the VStack…

      Spacer()
    }
    .padding()
  }

…and set its alignment to leading, using control-option click.

VStack(alignment: .leading) {

Checking the original Title and Author Stack, in ContentView…

…I saw that I had to supply that original size of 80 points, as an argument now, to avoid this inconsistent sizing.

Book.Image(title: book.title, size: 80)

And that met the requirements of the challenge! So great job, if you came up with something similar! But there was one more difference I wanted to see, between the two Title & Author stacks: font size.

If you didn’t do this, that’s fine. I didn’t give you precise font specs. But here’s how I went about it.

I wanted a title font, and an author font…

struct TitleAndAuthorStack: View {
  let book: Book
  let titleFont: Font
  let authorFont: Font

And they wouldn’t always be title2 and title3.

        .font(titleFont)
      Text(book.author)
        .font(authorFont)

Those are what I wanted for BookRow, though.

        Book.Image(title: book.title, size: 80)

        TitleAndAuthorStack(
          book: book,
          titleFont: .title2,
          authorFont: .title3
        )
        .lineLimit(1)

But for DetailView…

…I wanted the next sizes up: title and title2.

    VStack(alignment: .leading) {
      TitleAndAuthorStack(
        book: book,
        titleFont: .title,
        authorFont: .title2
      )
      Book.Image(title: book.title)

And I copied that to a preview in Book views, using the default book.

    VStack {
      TitleAndAuthorStack(
        book: .init(),
        titleFont: .title,
        authorFont: .title2
      )
      Book.Image(title: Book().title)

To try the whole thing out, I went back to ContentView, and started Live Preview mode, and selected a few of the longer titles. That’s actually a little more detailed!