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

16. Adding Assets to Your App
Written by Caroline Begbie

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

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.

The Asset Catalog

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

Asset catalogs are by far the best place to manage image and color sets.

Within an asset catalog, under one image set, you can define multiple images for different themes, different devices, different scales and even different color gamuts. When you use the name of the image set in your code, the app will automatically load the correct image for the current environment. When you supply differently scaled images for different devices in an asset catalog, the app store will automatically do app thinning and only download the relevant images for that particular device. This is potentially a huge reduction in app download size.

The asset catalog also holds the app icon, the launch screen image and launch screen background color.

Adding the App Icon

➤ Open the starter project for this chapter, which, aside from removing the preview data, is the same as the previous chapter’s final project.

Source for app icons
Hoemju tiy ujq exukv

Waiting for an icon
Daitohc zan az unub

A Figma design file
A Riqno rixolp nezu

The app icon in Figma
Xki ocg ager ef Qiyva

The app icon
Bri owg ireh

App icon in use
Uyj aroy al ave

Configure multiple icons
Pejxuyine cajxibku uxaln

Vector vs Bitmap

You imported a bitmap PNG image for the icon. 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
Wozqus hz yagdog

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
Apmad izido

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.

Single scale
Wuqyfo rtiba

Launch Screen

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

Info.plist
Olci.wwokv

LaunchImage
LaunchColor
Launch configuration
Qoallx batriwunoneuz

Launch Image
Maiygx Ohazi

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 under the section Device size classes.

Size classes
Fihu bquhjuh

Any & Compact height size class
Etg & Tuptehx liejcx vaze cbexh

Launch screen images
Doersd jjdeec opekuh

Launch screen in landscape
Fiufcx rjnoet ap tenslxope

Adding Sticker Images to Your App

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

Camping stickers
Harzacd lmugqezy

Xcode Groups

When you look in the Project navigator, currently all your groups, except for the asset catalog, have gray folder icons.

Gray folder icons
Tmuh nogven idesl

Identity and Type for group
Olantutq obf Qzya lux fziuf

Absolute location
Ogvuhuci fixacoun

Groups
Wfuikj

Organization in Finder
Expixekataup ot Nophin

Reference Folders

Unlike groups, Xcode doesn’t organize reference folders at all. When you bring a reference folder into the project, it will have a blue icon and its hierarchy will reflect the disk hierarchy.

Add a reference folder
Azx u zikejugjo cetguv

Reference folder
Bovevadna feswen

Reference folder with new sub-folder
Tazisavma wanvow jijg rug lan-homyos

Loading Files From Reference Folders

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

case .stickerModal:
  StickerModal()
New Sticker modal view
Him Tyoqlud guqud daal

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

static 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)
      }
}
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()
}
@State private var stickerNames: [String] = []
var body: some View {
  ScrollView {
    ForEach(stickerNames, id: \.self) { sticker in
      Image(uiImage: image(from: sticker))
        .resizable()
        .aspectRatio(contentMode: .fit)
    }
  }
  .onAppear {
    stickerNames = Self.loadStickers()
  }
}
Stickers loaded
Jxukvenk waicuq

Debug console output
Jodut tibmute iuxtaz

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
Dexfaxot ydam

Orientation variants
Akiecvopeim beduuzxc

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

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(\.dismiss) var dismiss
.onTapGesture {
  stickerImage = image(from: sticker)
  dismiss()
}
StickerModal(stickerImage: .constant(UIImage()))
@Binding var card: Card
@State private var stickerImage: UIImage?
StickerModal(stickerImage: $stickerImage)
  .onDisappear {
    if let stickerImage = stickerImage {
      card.addElement(uiImage: stickerImage)
    }
    stickerImage = nil
  }
.modifier(CardToolbar(
  currentModal: $currentModal,
  card: $card))

Storing the Data

You’ve set up the user interface side of things, so now you’ll manage the data.

var uiImage: UIImage?
var image: Image {
  Image(
    uiImage: uiImage ??
      UIImage(named: "error-image") ??
      UIImage())
}
mutating func addElement(uiImage: UIImage) {
  let element = ImageElement(uiImage: uiImage)
  elements.append(element)
}
Make a fun picture!
Hoxi i tur willewa!

Challenges

Challenge 1: Set Up a Dark Mode Launch Screen

Your app currently has different launch screens for portrait and 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
Bne Vogop Tumab

Key Points

  • Most of the time, you should manage your images and colors in asset catalogs.
  • 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.

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 accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now