Chapters

Hide chapters

Real-World iOS by Tutorials

First Edition · iOS 15 · Swift 5.5 · Xcode 13

Before You Begin

Section 0: 4 chapters
Show chapters Hide chapters

9. Adding Animations & Custom Controls
Written by Josh Steele

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

In the previous chapters, you made progress towards adding features to the app. That’s good! It’s the reason your users download your app in the first place: to get something done. For this app, that means finding a potential future pet.

With the primary functionality in place, it’s time to ask how you can:

  • Customize your app to improve user engagement?
  • Make your app available to as many potential users as possible?
  • Lower or eliminate any real or perceived barriers to using your app?

Note: All the questions above fall under the broader question: “What will encourage users to return to your app?” That’s the overarching topic for this section of the book.

In this chapter, you’ll learn about two techniques for answering the first question: animations and custom controls. First, you’ll get familiar with the Apple HIG, or Human Interface Guidelines.

Apple Human Interface Guidelines

Apple’s HIG, or Human Interface Guidelines for iOS are a set of guidelines, not requirements, that you can reference when designing your app’s user interface. Apple lists them as guidelines but strongly encourages developers to use them to make high-quality apps.

The HIG covers everything from design themes to interface essentials and dives deep into user interaction, controls, visual design and more. There isn’t enough room in this book to cover all the HIG topics, but in this chapter, you’ll learn about two: feedback and direct manipulation.

Feedback

The last thing you want users to feel when using your app is uncertain. Your app should respond to user interactions. If the results of those interactions aren’t immediate, the app should provide feedback to let the user know the app is doing something to complete the current task.

Feedback use case: Animations

Animations can help bring your app to life by giving a sense of connection between your user and your app. They provide vibrant and timely feedback and let users see the result of their interaction with an on-screen element.

When not to use animations

Don’t use animations, or any feedback, unless it conveys essential, timely or actionable information. Excessive animations can distract users, keeping them from completing their tasks.

When to use animations

When used properly, animations can add flair to your app. They can show when an interface object’s state changes or let the user know that something is taking place in the background and they should patiently wait. Here are a few examples:

Animations are always optional

There are features like reduced motion that can affect (and even disable) effects or animations in your app. Knowing this you should always think of animations as optional. Therefore, don’t make animations an integral part of your app.

Adding an animation to PetSave

Animations can convey various feedback to the user. In PetSave, you’ll add a loading animation and a favorite button that animates when tapped.

Building animated GIFs

When apps fetch data from the network, there’s always a possibility of delays. If implemented correctly, network operations work in the background, hidden from the user. Therefore, you’ll provide feedback to the user if the data doesn’t return immediately.

// 1
struct LoadingAnimation: UIViewRepresentable {
  let animatedFrames: UIImage
  let image: UIImageView
  let squareDimension: CGFloat = 125

  // 2
  init() {
    var images: [UIImage] = []
    // 3
    for i in 1...127 {
      guard let image =
        UIImage(named: "dog_\(String(format: "%03d", i))")
        else { continue }
      images.append(image)
    }
    // 4
    animatedFrames = UIImage.animatedImage(with: images,
      duration: 4) ?? UIImage()
    // 5
    image = UIImageView(frame: CGRect(x: 0, y: 0, width: squareDimension, height: squareDimension))
  }

  // 6
  func makeUIView(context: Context) -> UIView {
    let view = UIView(frame: CGRect(x: 0, y: 0,
      width: squareDimension, height: squareDimension))
    image.clipsToBounds = true
    image.autoresizesSubviews = true
    image.contentMode = .scaleAspectFit
    image.image = animatedFrames
    image.center = CGPoint(x: view.frame.width / 2,
      y: view.frame.height / 2)
    view.backgroundColor = .red
    view.addSubview(image)

    return view
  }

  func updateUIView(_ uiView: UIViewType, context: Context) {
    // no code here; just for protocol
  }
}

// 7
struct LoadingAnimationView: View {
  var body: some View {
    VStack {
      LoadingAnimation()
    }
  }
}


// 8
struct LoadingAnimationView_Previews: PreviewProvider {
  static var previews: some View {
    LoadingAnimationView()
  }
}
// 1
HStack(alignment: .center) {
  // 2
  LoadingAnimation()
    .frame(maxWidth: 125, minHeight: 125)
  Text("Loading more animals...")
}
// 3
.task {
  await viewModel.fetchMoreAnimals()
}
The Animals Near You view.
Pqi Ujejuht Keak Gaa weoc.

Dialog confirming the Network Link Conditioner Installation.
Naibix wangofpibs nxu Durvozp Kalr Moncawaidej Upmwekkezeol.

The Network Link Conditioner preference pane.
Rfi Mozkugs Tefc Sezqiheimih hqitivafma kule.

The custom animation now appears next to the informative text (best viewed in your simulator or device).
Bre solnid avevutaom ruz obvaoyd rimk le zwe oxxopdutofe fimj (qufl ceusom ir feas bibajusoq iy voroba).

Animation modifiers in SwiftUI

You can also add animations to your app by using SwiftUI’s built-in animation capabilities. SwiftUI uses both implicit and explicit animations:


// 1
Image(systemName: favorited ? "heart.fill" : "heart")
  .font(.system(size: 50))
  .foregroundColor( favorited ? Color(.systemRed) : Color(.black))
  .frame(minWidth: 50, maxWidth: 50, minHeight: 50, maxHeight: 50)
  // 2
  .animation(favorited ? .interpolatingSpring(
    mass: 5,
    stiffness: 3.0,
    damping: 1.0,
    initialVelocity: 1) :
    .default,
    value: $favorited.wrappedValue)
  .onTapGesture {
    $favorited.wrappedValue.toggle()
}
Animal Details View with animation (best viewed in your simulator or device).
Irajon Kuguiwy Peaz qiwb uxalidoom (kaxn voazif al fiin bihibuhum on cileya).

Direct manipulation

Direct manipulation is the most, well, direct way a user can interact with your app. Users can reach out with their fingers and interact with your app.

Direct manipulation use case: Custom Controls

Developers can go beyond the built-in controls in iOS and make their own custom controls. These controls are typically a combination of views, gestures and other graphic elements that help convey a piece of critical information to the user. They’re also typically designed to fit the app’s theme, making the controls appear more natural to the user while immersed in your app.

When not to create custom controls

Apple’s set of controls is fairly expansive, and more importantly, familiar to users. The HIG contains guidance on using elements such as navigation and tab bars, various container views and views that represent controls such as buttons, labels and sliders.

When to create custom controls

iOS contains plenty of optimized controls for you to use during development. However, there are some reasons you might develop your own control:

Adding a custom control to PetSave

You’ll add a ranking control that lets users provide feedback on pet details and specify how likely they are to adopt that pet. To accomplish this, you’ll use the following techniques:

import SwiftUI

struct PetRankingView: View {
  // 1
  @ObservedObject var viewModel: PetRankingViewModel
  var animal: AnimalEntity

  // 2
  init(animal: AnimalEntity) {
    self.animal = animal
    viewModel = PetRankingViewModel(animal: animal)
  }

  // 3
  var body: some View {
    HStack {
      Text("Rank me!")
        .multilineTextAlignment(.center)
      ForEach(0...4, id: \.self) { index in
        PetRankImage(index: index, recentIndex: $viewModel.ranking)
      }
    }
  }
}
struct PetRankImage: View {
  let index: Int
  // 1
  @State var opacity: Double = 0.4
  @State var tapped = false
  @Binding var recentIndex: Int

  var body: some View {
    // 2
    Image("creature_dog-and-bone")
      .resizable()
      .aspectRatio(contentMode: .fit)
      .opacity(opacity)
      .frame(width: 50, height: 50)
      .onTapGesture {
        opacity = tapped ? 0.4 : 1.0
        tapped.toggle()
        recentIndex = index
      }
      .onChange(of: recentIndex) { value in
        checkOpacity(value: value)
      }
      .onAppear {
        checkOpacity(value: recentIndex)
      }
  }

  // 3
  func checkOpacity(value: Int) {
    opacity = value >= index ? 1.0 : 0.4
    tapped.toggle()
  }
}
final class PetRankingViewModel: ObservableObject {
  var animal: AnimalEntity
  // 1
  var ranking: Int {
    didSet {
      animal.ranking = Int32(ranking)
      objectWillChange.send()
    }
  }

  // 2
  init(animal: AnimalEntity) {
    self.animal = animal
    self.ranking = Int(animal.ranking)
  }
}
struct PetRankingView_Previews: PreviewProvider {
  static var previews: some View {
    if let animal = CoreDataHelper.getTestAnimalEntity() {
      PetRankingView(animal: animal)
        .padding()
        .previewLayout(.sizeThatFits)
    }
  }
}
The PetSave custom ranking control.
Vtu XuvPuka tabqid hucxalf hicbrup.

The PetSave custom ranking control has a 5 out of 5 ranking!
Vzi CugBusu narjel yoshulj yeybyuy fud u 3 uuq eb 5 nimcupm!

PetRankingView(animal: animal)
  .padding()
  .blur(radius: zoomed ? 20 : 0)
The completed Animal Details view!
Klu xuqpbulay Unobal Gibuidr quom!

Key points

  • Apple’s Human Interface Guidelines are a great resource for ensuring your app has a great look and feel and fits alongside other apps in the store.
  • Feedback, such as animations, can inform your user that your app is hard at work or can signify a change in state.
  • Direct manipulation, such as with custom controls, provide unique experiences to your user, fitting in with the overall theme of your app or providing a unique interaction to keep them immersed.

Where to go from here?

Congratulations! You took your first dive into the Apple Human Interface Guidelines. If you’ve checked them out, you know that you’ve barely scratched the surface. You’ll read about a few more areas in the later chapters of this section.

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