Chapters

Hide chapters

SwiftUI Apprentice

Second Edition · iOS 16 · Swift 5.7 · Xcode 14.2

Section I: Your First App: HIITFit

Section 1: 12 chapters
Show chapters Hide chapters

Section II: Your Second App: Cards

Section 2: 9 chapters
Show chapters Hide chapters

13. Outlining a Photo Collage App
Written by Caroline Begbie

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

Congratulations, you’ve written your first app! HIITFit uses standard iOS user interaction with lists and swipeable page views. Now you’ll get your teeth into something a bit more complex with custom gestures and custom views.

Photo collage apps are very popular, and in this section, you’ll build your own collaging app in which you’ll create cards to share. You’ll be able to add images — from your photos or from the internet — and add text and stickers too. This app will be real-world with real-world problems to match.

In this chapter, you’ll take a look at a sketch outline of the app idea and create a view hierarchy that will be the skeleton of your app.

At the end of Section 2, your finished app will look like this:

Final app
Final app

Initial App Idea

The first step to creating a new app is having the idea. Before writing any code, you should do research as to whether your app is going to be a hit or a miss. Work out who your target audience is and talk to some people who might use your app. Find out what your competition is in the App Store and explore how your app can offer something new and different.

Once you’ve decided that you have a hit on your hands, sketch your app out and work out feasibility and where technical difficulties may lie.

Your photo collaging app will have a primary view — where you list all the cards — and a detail view for the selected card — where you can add photos and text. This might be the back-of-the-napkin sketch:

Back of the napkin sketch
Back of the napkin sketch

In future chapters, you’ll set up the data model and data storage, but for now, examine the design and think about possible implementation difficulties that you’ll need to overcome. Always take a modular approach and test each aspect of the app as separately from the main app as possible.

SwiftUI is great for this, because you can construct views and controls independently using SwiftUI’s live preview. When you’re happy with how a view works, add it to your app.

Creating the Project

In the previous section, you began with a starter app containing all the assets you needed to create HIITFit. In this section, you’ll start with a new app, and you’ll find out how to add assets as you move through the next few chapters.

Initial view
Ibituuw ziuy

Creating the First View for Your Project

Skills you’ll learn in this section: ScrollView

Creating a List of Cards

Instead of cards, for the moment, you’ll show a placeholder list of rounded rectangles.

var body: some View {
  ScrollView {
    VStack {
      ForEach(0..<10) { _ in
        RoundedRectangle(cornerRadius: 15)
          .foregroundColor(.gray)
          .frame(width: 150, height: 250)
      }
    }
  }
}
Placeholder thumbnails
Lkoqacozlef znagsqaojf

Show canvas
Rqay kihcoc

ScrollView(showsIndicators: false) {
With and without the scroll bar
Bays uhb xohdook npe lxfizh yox

Refactoring the View

Skills you’ll learn in this section: refactoring views

Extracted View
Ihhgocsij Coax

Refactor the view
Ruzimpil fxu quin

struct CardThumbnail: View {
  var body: some View {
    RoundedRectangle(cornerRadius: 15)
      .foregroundColor(.gray)
      .frame(width: 150, height: 250)
  }
}

Setting Up the Single Card View

A card will have a colored background to which you’ll add photos, stickers and text.

var body: some View {
  Color.yellow
}
A yellow card
A cufsid nepc

Transitioning From List to Card

Skills you’ll learn in this section: full screen modal

Creating a Full Screen Modal View

➤ In CardsListView, create a new property to track the modal presentation:

@State private var isPresented = false
.fullScreenCover(isPresented: $isPresented) {
  SingleCardView()
}
.onTapGesture {
  isPresented = true
}
Transition from thumbnail to card
Yfulrodioj lkes crenxmeuw da kitq

CardsListView()

The Navigation Toolbar

Skills you’ll learn in this section: toolbars; navigation bar; NavigationStack; tuples

@Environment(\.dismiss) var dismiss
.toolbar {
  ToolbarItem(placement: .navigationBarTrailing) {
    Button("Done") {
      dismiss()
    }
  }
}
No Done button
Ma Mula kukbum

NavigationStack

Notice that the button doesn’t show up. This is because ToolbarItem(placement:) is using navigationBarTrailing, so any item will only show up if the view is inside a NavigationStack.

Navigation bar Done button
Qetizineaz tiq Cabu jekjon

Navigation with Done button
Xamopineap hizp Yuve sodces

The Bottom Toolbar

The single card view will have four buttons at the bottom, allowing you to add elements to your card:

enum ToolbarSelection {
  case photoModal, frameModal, stickerModal, textModal
}
struct ToolbarButton: View {
  var body: some View {
    VStack {
      Image(systemName: "heart.circle")
        .font(.largeTitle)
      Text("Stickers")
    }
    .padding(.top)
  }
}
@Binding var modal: ToolbarSelection?
var body: some View {
  HStack {
    Button {
      modal = .stickerModal
    } label: {
      ToolbarButton()
    }
  }
}
struct BottomToolbar_Previews: PreviewProvider {
  static var previews: some View {
    BottomToolbar(modal: .constant(.stickerModal))
      .padding()
  }
}
Stickers button
Kmobduwf qevdar

Adding the Bottom Toolbar

➤ Open SingleCardView.swift and add a new property to SingleCardView:

@State private var currentModal: ToolbarSelection?
ToolbarItem(placement: .bottomBar) {
  BottomToolbar(modal: $currentModal)
}
Bottom toolbar
Wajwoq caihtas

Adding the Other Buttons

➤ Open BottomToolbar.swift and add a new property to ToolbarButton:

let modal: ToolbarSelection
private let modalButton: [
    ToolbarSelection: (text: String, imageName: String)
  ] = [
    .photoModal: ("Photos", "photo"),
    .frameModal: ("Frames", "square.on.circle"),
    .stickerModal: ("Stickers", "heart.circle"),
    .textModal: ("Text", "textformat")
  ]

Tuples

A tuple is a group of values. For example, you could initialize a tuple with three elements like this:

let button = ("Stickers", "heart.circle", 1)
let text = button.0
let number = button.2
var body: some View {
  if let text = modalButton[modal]?.text,
    let imageName = modalButton[modal]?.imageName {
  VStack {
    Image(systemName: imageName)
      .font(.largeTitle)
    Text(text)
  }
  .padding(.top)
  }
}
enum ToolbarSelection: CaseIterable {
HStack {
  ForEach(ToolbarSelection.allCases, id: \.self) { selection in
    Button {
      modal = selection
    } label: {
      ToolbarButton(modal: selection)
    }
  }
}
Button Preview
Tamwel Cbozoaf

Adding Modal Views

Skills you’ll learn in this section: multiple modal sheets; Identifiable enumerations; Hashable

.sheet(item: $currentModal) { item in
  switch item {
  default:
    Text(String(describing: item))
  }
}

Making an Enumeration Identifiable

➤ Open ToolbarSelection.swift and conform ToolbarSelection to Identifiable:

enum ToolbarSelection: CaseIterable, Identifiable {
var id = UUID()

Making an Object Hashable

You need a value that uniquely identifies an object. That describes a hash value. Hashing algorithms calculate values from any data to provide a digital fingerprint that identifies an object.

var id: Int {
  hashValue
}
A modal view
I kepoy boex

Cleaning Up

➤ Open BottomToolbar.swift, and in BottomToolbar, remove , id: \.self from the ForEach loop.

ForEach(ToolbarSelection.allCases) { selection in
The app outline
Sgu exk uixlata

Challenge

Make it a habit to regularly tidy up the code and files in your app.

Challenge 1: Tidy up Files

Look down the list of files and see which ones you can group together. Command-click each file that you want to group together, then Control-click the selected files and choose New Group from Selection. Name the group. If you miss any files, just drag them into the group later.

Challenge 2: Refactor Code

You don’t always have to create new structures for views. Sometimes, if it’s a simple view and you’re only using it once, it’s easier to keep track of views as properties or methods.

Key Points

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.

Unlock now