Bird
Raised Fist0
iOS Swiftmobile~20 mins

Animated state changes in iOS Swift - Mini App: Build & Ship

Choose your learning style10 modes available
Build: Color Toggle Animation
A screen with a square that changes color and size with animation when tapped.
Target UI
┌───────────────────────────┐
│                           │
│         ■■■■■■■■          │
│         ■      ■          │
│         ■      ■          │
│         ■■■■■■■■          │
│                           │
│   [Tap the square to      ]│
│    change color & size]   │
└───────────────────────────┘
Display a square in the center of the screen.
When the user taps the square, it should animate smoothly to a new color and size.
Toggle between two colors and two sizes on each tap.
Use UIView animation with duration of 0.5 seconds.
Ensure the square remains centered during size changes.
Starter Code
iOS Swift
import UIKit

class ColorToggleViewController: UIViewController {
    private let squareView = UIView()
    private var isToggled = false

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        squareView.backgroundColor = .systemBlue
        squareView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(squareView)

        NSLayoutConstraint.activate([
            squareView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            squareView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            squareView.widthAnchor.constraint(equalToConstant: 100),
            squareView.heightAnchor.constraint(equalToConstant: 100)
        ])

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(squareTapped))
        squareView.addGestureRecognizer(tapGesture)
        squareView.isUserInteractionEnabled = true
    }

    @objc private func squareTapped() {
        // TODO: Add animation code here
    }
}
Task 1
Task 2
Task 3
Task 4
Solution
iOS Swift
import UIKit

class ColorToggleViewController: UIViewController {
    private let squareView = UIView()
    private var isToggled = false
    private var widthConstraint: NSLayoutConstraint!
    private var heightConstraint: NSLayoutConstraint!

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        squareView.backgroundColor = .systemBlue
        squareView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(squareView)

        widthConstraint = squareView.widthAnchor.constraint(equalToConstant: 100)
        heightConstraint = squareView.heightAnchor.constraint(equalToConstant: 100)

        NSLayoutConstraint.activate([
            squareView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            squareView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            widthConstraint,
            heightConstraint
        ])

        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(squareTapped))
        squareView.addGestureRecognizer(tapGesture)
        squareView.isUserInteractionEnabled = true
    }

    @objc private func squareTapped() {
        isToggled.toggle()

        widthConstraint.constant = isToggled ? 150 : 100
        heightConstraint.constant = isToggled ? 150 : 100

        UIView.animate(withDuration: 0.5) {
            self.squareView.backgroundColor = self.isToggled ? .systemGreen : .systemBlue
            self.view.layoutIfNeeded()
        }
    }
}

We keep references to the width and height constraints so we can change their constants when toggling.

When the square is tapped, we toggle the isToggled boolean.

We update the constraint constants to 150 or 100 depending on the toggle state.

Inside UIView.animate, we change the background color and call layoutIfNeeded() to animate the size change smoothly.

This keeps the square centered and animates both color and size changes over 0.5 seconds.

Final Result
Completed Screen
┌───────────────────────────┐
│                           │
│         ■■■■■■■■■■■■■      │
│         ■           ■      │
│         ■           ■      │
│         ■           ■      │
│         ■■■■■■■■■■■■■      │
│                           │
│   [Tap the square to      ]│
│    change color & size]   │
└───────────────────────────┘
User taps the blue square in the center.
The square smoothly grows larger and changes color to green over 0.5 seconds.
Tapping again shrinks the square back to original size and changes color back to blue.
Stretch Goal
Add a label below the square that updates to show "Large Green" or "Small Blue" depending on the square's state.
💡 Hint
Create a UILabel below the square and update its text inside the animation block in squareTapped().

Practice

(1/5)
1. What does the withAnimation function do in SwiftUI when changing a state variable?
easy
A. It delays the state change by a few seconds.
B. It disables all animations temporarily.
C. It resets the state variable to its initial value.
D. It animates the UI changes caused by the state update.

Solution

  1. Step 1: Understand the role of withAnimation

    The withAnimation function wraps state changes to animate the resulting UI updates smoothly.
  2. Step 2: Identify the effect on UI

    When a state variable changes inside withAnimation, SwiftUI animates the transition of views affected by that state.
  3. Final Answer:

    It animates the UI changes caused by the state update. -> Option D
  4. Quick Check:

    withAnimation = animate UI changes [OK]
Hint: Remember: withAnimation wraps state changes to animate UI [OK]
Common Mistakes:
  • Thinking withAnimation delays state changes
  • Confusing withAnimation with disabling animations
  • Assuming it resets state variables
2. Which of the following is the correct syntax to animate a state change in SwiftUI?
easy
A. animate { isOn.toggle() }
B. withAnimation(isOn.toggle())
C. withAnimation { isOn.toggle() }
D. animation { isOn.toggle() }

Solution

  1. Step 1: Recall the correct function syntax

    The correct syntax uses withAnimation as a function wrapping a closure that changes the state.
  2. Step 2: Check each option

    withAnimation { isOn.toggle() } correctly uses withAnimation { isOn.toggle() }. Others either misuse the function name or syntax.
  3. Final Answer:

    withAnimation { isOn.toggle() } -> Option C
  4. Quick Check:

    Correct syntax = withAnimation { ... } [OK]
Hint: Use withAnimation { stateChange } syntax for animation [OK]
Common Mistakes:
  • Calling withAnimation with parentheses around state change
  • Using non-existent functions like animate or animation incorrectly
  • Not wrapping state changes inside a closure
3. Given the code snippet:
struct ContentView: View {
  @State private var isVisible = false
  var body: some View {
    VStack {
      Button("Toggle") {
        withAnimation {
          isVisible.toggle()
        }
      }
      if isVisible {
        Text("Hello World")
          .transition(.slide)
      }
    }
  }
}

What happens when the button is tapped?
medium
A. The text "Hello World" appears or disappears with a sliding animation.
B. The text "Hello World" appears instantly without animation.
C. The app crashes due to missing animation modifier.
D. The button toggles but the text never appears.

Solution

  1. Step 1: Analyze the state toggle inside withAnimation

    The isVisible state toggles inside withAnimation, so UI changes animate.
  2. Step 2: Understand the transition modifier

    The .transition(.slide) modifier animates the text sliding in or out when it appears or disappears.
  3. Final Answer:

    The text "Hello World" appears or disappears with a sliding animation. -> Option A
  4. Quick Check:

    withAnimation + .transition = animated slide [OK]
Hint: withAnimation + .transition triggers smooth view animations [OK]
Common Mistakes:
  • Ignoring the transition modifier effect
  • Thinking animation requires .animation() modifier here
  • Assuming instant appearance without animation
4. Identify the error in this SwiftUI code snippet that tries to animate a color change:
@State private var isRed = true
var body: some View {
  Rectangle()
    .fill(isRed ? Color.red : Color.blue)
    .onTapGesture {
      isRed.toggle()
    }
    .animation(.easeInOut)
}
medium
A. The .animation modifier is deprecated and should be replaced with withAnimation.
B. The state variable isRed should not be toggled inside onTapGesture.
C. The Rectangle view cannot change fill color dynamically.
D. The .fill modifier must be outside the Rectangle view.

Solution

  1. Step 1: Understand .animation modifier usage

    The .animation modifier on a view is deprecated and may not animate state changes reliably.
  2. Step 2: Use withAnimation for state changes

    Wrapping the state toggle inside withAnimation ensures the color change animates properly.
  3. Final Answer:

    The .animation modifier is deprecated and should be replaced with withAnimation. -> Option A
  4. Quick Check:

    Use withAnimation, not .animation modifier [OK]
Hint: Use withAnimation to animate state changes, not .animation modifier [OK]
Common Mistakes:
  • Using .animation modifier on views instead of withAnimation
  • Thinking onTapGesture cannot toggle state
  • Misplacing .fill modifier
5. You want to animate a button's scale when pressed and return to normal size when released. Which approach correctly combines state and animation in SwiftUI?
hard
A. Use a Timer to change scale repeatedly for animation effect.
B. Use a @GestureState to track pressing and apply .scaleEffect with .animation inside the gesture.
C. Toggle a @State variable on tap and use .animation on the button without gestures.
D. Apply .animation modifier directly on the button without state changes.

Solution

  1. Step 1: Understand gesture-driven animations

    Using @GestureState tracks the pressing state smoothly during the gesture lifecycle.
  2. Step 2: Apply scaleEffect with animation inside gesture

    Applying .scaleEffect based on gesture state and animating it creates a responsive press effect.
  3. Step 3: Evaluate other options

    Options A, B, and D either lack gesture responsiveness or misuse animation techniques.
  4. Final Answer:

    Use a @GestureState to track pressing and apply .scaleEffect with .animation inside the gesture. -> Option B
  5. Quick Check:

    GestureState + scaleEffect + animation = correct press animation [OK]
Hint: Use @GestureState for press animations, not just @State [OK]
Common Mistakes:
  • Using @State instead of @GestureState for press tracking
  • Applying .animation modifier without state changes
  • Using Timer for simple press animations
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard