Chapters

Hide chapters

SwiftUI Apprentice

First Edition · iOS 14 · Swift 5.4 · Xcode 12.5

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

16. Adding Assets to Your 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.

Initially, in this chapter, you’ll learn about managing assets held in an asset catalog and you’ll create that all-important app icon. However, the most important part of your app is decorating your cards with photos, stickers and text, so you’ll then focus on how to manage and import sticker images supplied with your app.

At the end of this chapter, you’ll be able to create a card loaded with stickers.

Make a fun picture!
Make a fun picture!

The starter project

➤ Open the starter project for this chapter.

The starter project is exactly the same as the project from the previous chapter’s challenge folder.

Asset catalog

Skills you’ll learn in this section: managing images in asset catalogs; app icons; screen resolution; vector vs bitmap

Adding the app icon

➤ Click the project name Cards at the top of the Project navigator. Choose the target Cards. On the General tab, find App Icons and Launch Images and click the App Icons Source drop-down:

Source for app icons
Veirqu yan ugm omeqv

Waiting for icons
Vuozivs cug ememk

A Figma design file
A Tokno cowagt wora

Device resolutions and image scale

Early iPhone screens had a 1:1 pixel density which means that a 100x100 pixel image on screen took up 100x100 points. iPhone 4 introduced the retina screen. Retina is simply an Apple marketing term for displays with a higher pixel density. On the iPhone 4 screen, where you can barely see the pixels, a 100x100 pixel image would take up 50x50 points on screen, having a scale factor of 2. iPhone 6s Plus came along, introducing a 3:1 pixel density. For an image to take up 100x100 points on screen, you’d have to scale it to 300x300 pixels.

App icon files
Imm opay niziy

App icons
Ozf ijibc

App icon in use
Orh ozib aj upo

Vector vs bitmap

You imported bitmap PNG images for the icons. For other assets, you can use vector formats, such as PDF or SVG. When possible, it’s always better to use vector formats. These are made up of lines, curves and fills. For a vector line, you can set a start point and an end point. When you scale the line, the vector resizes without losing any of its resolution. With a bitmap line, you must stretch or compress pixels.

Bitmap vs vector
Qefdob rr hivmif

Adding a vector image

Later, your app will need a placeholder image to show whether there are any errors in loading an image.

Error image
Uyqaj etixo

Single scale
Jerdxa pcuwi

Launch screen

Skills you’ll learn in this section: launch screen; size classes

Info.plist
Atra.jmohn

LaunchImage
LaunchColor
Launch Image
Voiyvm Arepe

Size classes

Size classes represent the content area available using horizontal and vertical traits. These two traits can be either regular or compact. All devices have either a regular or compact width size class and either a regular or compact height size class. You can find a list of these size classes in Apple’s Human Interface Guidelines at https://apple.co/348lVx0 under the section Size Classes.

Size classes
Yole jdizpaf

Any & Compact height size class
Esp & Wughomm houvnt loha ggowp

Launch screen in landscape
Duektk xsqoud uk bacdjrepi

Adding sticker images to your app

Skills you’ll learn in this section: present multiple modals; hashing

Camping stickers
Tebsimm fpuyhebz

Adding the stickers modal view

Earlier in this section, you set up four buttons to present four different modals. You’ll now create the modal view that will appear when you tap the Stickers button and show all the stickers in your reference folder. The user will then pick one, which will appear as an image element on the card.

.sheet(item: $currentModal) { item in
  switch item {
  case .stickerPicker:
    EmptyView()
  default: 
    EmptyView()
  }
}
enum CardModal: Identifiable {
  var id = UUID()
  case photoPicker, framePicker, stickerPicker, textPicker
}

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. Fortuitously, enumerations automatically conform to Hashable which provides a hash value.

var id: Int {
  hashValue
}
New modal view for stickers
Meh zucuv saaf sac dlabquvv

Reference folders

Skills you’ll learn in this section: groups; reference folders; loading images from files; lazy loading

Identity and Type for group
Akokcadf igm Jdqu hox hkauh

Absolute location
Oyhalata zucehuel

Groups
Ttoeqs

Add a reference folder
Ilb i tacepance renxep

Reference folder
Piloqevha wirmav

Reference folder with new sub-folder
Noluzewdu vimbat venv fir soz-pucxam

Loading files from reference folders

Now, you’ll create a Sticker view that loads images from the Stickers folder.

StickerPicker()
New Sticker Picker modal
Var Ksibgir Wekrof cuwec

var body: some View {
  // 1
  Group {
    // 2 
    if let resourcePath = Bundle.main.resourcePath,
      // 3
      let image = UIImage(named: resourcePath + 
        "/Stickers/Camping/fire.png") {
       Image(uiImage: image)
    } else {
      EmptyView()
    }
  }
}
Fire sticker
Hitu rjejyup

@State private var stickerNames: [String] = []
func loadStickers() -> [String] {
  var themes: [URL] = []
  var stickerNames: [String] = []
}
// 1
let fileManager = FileManager.default
if let resourcePath = Bundle.main.resourcePath,
  // 2
  let enumerator = fileManager.enumerator(
    at: URL(fileURLWithPath: resourcePath + "/Stickers"),
    includingPropertiesForKeys: nil,
    options: [
      .skipsSubdirectoryDescendants, 
      .skipsHiddenFiles
    ]) {
      // 3
      for case let url as URL in enumerator 
      where url.hasDirectoryPath {
        themes.append(url)
      }
}
while let url = enumerator.nextObject() as? URL {
  if url.hasDirectoryPath {
    themes.append(url)
  }
}
for theme in themes {
  if let files = try?
  fileManager.contentsOfDirectory(atPath: theme.path) {
    for file in files {
      stickerNames.append(theme.path + "/" + file)
    }
  }
}

return stickerNames
func image(from path: String) -> UIImage {
  print("loading:", path)
  return UIImage(named: path)
    ?? UIImage(named: "error-image")
    ?? UIImage()
}
var body: some View {
  ScrollView {
    ForEach(stickerNames, id: \.self) { sticker in
      Image(uiImage: image(from: sticker))
        .resizable()
        .aspectRatio(contentMode: .fit)
    }
  }
  .onAppear {
    stickerNames = loadStickers()
  }
}
Stickers loaded
Vbijkahr ruocen

Debug console output
Kidan liwmabe aakcar

LazyVStack {

Using lazy grid views

Skills you’ll learn in this section: grids

let columns = [
  GridItem(spacing: 0),
  GridItem(spacing: 0),
  GridItem(spacing: 0)
]
LazyVGrid(columns: columns) {
Vertical grid
Setbujoz blej

StickerPicker()
  .previewLayout(PreviewLayout.fixed(width: 896, height: 414))
Grid in landscape
Vmet az kupvkrelu

let columns = [
  GridItem(.adaptive(minimum: 120), spacing: 10)
]
Adaptive grid
Afohkezu gtiw

Selecting the sticker

Now that you have the stickers showing, you’ll tap one to select it, dismiss the modal and add the sticker to the card as a card element.

@Binding var stickerImage: UIImage?
@Environment(\.presentationMode) var presentationMode
.onTapGesture {
  stickerImage = image(from: sticker)
  presentationMode.wrappedValue.dismiss()
}
struct StickerPicker_Previews: PreviewProvider {
  static var previews: some View {
    StickerPicker(stickerImage: .constant(UIImage()))
  }
}
@State private var stickerImage: UIImage?
StickerPicker(stickerImage: $stickerImage)
.onDisappear {
  if let stickerImage = stickerImage {
    card.addElement(uiImage: stickerImage)
  }
  stickerImage = nil
}
mutating func addElement(uiImage: UIImage) {
  let image = Image(uiImage: uiImage)
  let element = ImageElement(image: image)
  elements.append(element)
}
Make a fun picture!
Duya e moc sinhume!

Challenges

Challenge 1: Set up a Dark Mode launch screen

Your app currently has different launch screens for portrait and also landscape, when the height size class is compact. Your challenge is to add different launch screens when the device is using Dark Mode. You’ll change the launch image’s Appearances property in the asset catalog. You’ll find the dark launch screen images in the assets folder. Drag these in to the appropriate spaces just as you did earlier in the chapter.

Challenge 2: Set up launch colors

This chapter did not cover colors specifically, but you can change appearance and device in the same way as with images. You’ve already set up a launch color in Info.plist to use as the launch background color. Change the launch color in the asset catalog. Click Show Color Panel to show the Color Panel and use white — FFFFFF — for device light appearance and the Hex Color 292A2E for dark appearance.

The Color Panel
Qyu Vuzur Tezam

Key points

  • Asset catalogs are where you should be managing your images and colors most of the time.
  • If the asset catalog is not suitable for purpose, then use reference folders.
  • In asset catalogs, favor vector images over bitmaps. They are smaller in file size and retain sharpness when scaled. Xcode will automatically scale to the appropriate dimensions for the current device.
  • Think about how you can make your app special. Good app design together with artwork can really make you stand out from the crowd.

Where to go from here?

In this chapter you used app icons and launch screens. The Apple Human Interface Guidelines, often referred to as the HIG, will point you at best use. You can find the HIG for iOS here: https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/launch-screen/.

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