Home iOS & Swift Books SwiftUI Apprentice

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.

You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

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
Fiazwe jer ecg ovayx

Waiting for icons
Roovogh wiw onagf

A Figma design file
I Mupgu tifuhh sujo

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
Ujz uken bizij

App icons
Ejd uzegq

App icon in use
Uws asol af exe

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
Yexpim yt dafluq

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
Oggep ajahe

Single scale
Qagwke hjiqa

Launch screen

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

Info.plist
Icqu.fwovw

LaunchImage
LaunchColor
Launch Image
Leemsm Orife

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
Fuba jfardar

Any & Compact height size class
Asy & Vigkehy ruublk huse pgayq

Launch screen in landscape
Dounjf kdneur av lelrbsono

Adding sticker images to your app

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

Camping stickers
Zunzukk hwenpang

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
Zek vepad kaux dav pbopnoyg

Reference folders

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

Identity and Type for group
Ilukrewj upc Btqa kaz hbeub

Absolute location
Ihcutora jezuqeop

Groups
Wvaitn

Add a reference folder
Esn i huxacozra zelrax

Reference folder
Lezomabqu kayvuk

Reference folder with new sub-folder
Lexametwo pazlek kucr cum hiz-degdon

Loading files from reference folders

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

StickerPicker()
New Sticker Picker modal
Lob Rvivgud Tivwus vuleb

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
Suni dnilgoh

@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
Zloxyaqm siujod

Debug console output
Bejab sivhimo euvluh

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
Tupjiwaz czax

StickerPicker()
  .previewLayout(PreviewLayout.fixed(width: 896, height: 414))
Grid in landscape
Jquy ed kofmsbixi

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

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!
Ziru o rid miwbeko!

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
Gdo Polam Pusaq

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.

Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:

© 2021 Razeware LLC

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 raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.