Swift
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Optionals
-
Section 4: Object-Oriented and Value Types
-
Section 5: Memory Management
-
Section 6: Generics and Error Handling
-
Section 7: Concurrency
-
Section 8: Working with Collections
-
Section 9: Codable and Data Handling
-
Section 10: Protocol-Oriented Programming
-
Section 11: Testing and Tooling
-
Section 12: Practical Projects
-
Section 13: Interview Practice
-
Section 14: More Practice Exercises
-
Section 15: More Standard Library
-
Section 16: Advanced Concurrency
-
Section 17: Foundation Framework Deep Dive
-
Section 18: URLSession and Networking Deep Dive
-
Section 19: Combine Framework
-
Section 20: SwiftUI Fundamentals for Swift Developers
-
Section 21: Server-Side Swift with Vapor
-
Section 22: Swift Package Manager Deep Dive
-
Section 23: Swift Concurrency Deep Dive
-
Section 24: More Language Features
-
Section 25: Error Handling Deep Dive
-
Section 26: Testing Deep Dive
-
Section 27: Data Structures and Algorithms in Swift
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Swift Macros (Swift 5.9+)
-
Section 31: Property Wrappers Ecosystem
-
Section 32: Swift Interop Deep Dive
-
Section 33: iOS App Architecture Patterns
-
Section 34: Performance and Debugging
-
Section 35: App Distribution and CI/CD
-
Section 36: More Practical Projects
-
Section 37: SwiftData and Persistence
-
Section 38: More Design Patterns
-
Section 39: More Review and Practice
-
Section 40: More Foundation Deep Dive
-
Section 41: Advanced Collections in Swift
-
Section 42: Advanced Generics Practice
-
Section 43: UIKit for Legacy and Hybrid Apps
-
Section 44: watchOS and visionOS Development Basics
-
Section 45: More Networking Patterns
-
Section 46: More Testing Practice
-
Section 47: Accessibility in Swift Apps
-
Section 48: Localization
-
Section 49: More Practical Projects Round 2
-
Section 50: Swift Charts Framework
-
Section 51: More Interview and Algorithm Practice
-
Section 52: Final Practice and Mastery
-
Section 53: Swift Compiler and Build System
-
Section 54: More Concurrency Practice
-
Section 55: App Store Guidelines and Review
-
Section 56: More Design and Architecture
206: Reduce Motion and Accessibility Settings
I’ve seen this happen in countless code reviews: a developer spends hours polishing a "wow" moment—maybe a card that slides in from the side with a spring bounce or a complex zooming transition—only to realize they've created a nightmare for users with vestibular disorders. When a user enables "Reduce Motion" in their iOS settings, they aren't just asking for a faster app; they're telling you that certain types of movement make them physically ill.
Here is a snippet of code I recently encountered. It’s a simple onboarding screen where a "Welcome" card slides up from the bottom of the screen when the view appears.
struct OnboardingView: View {
@State private var isVisible = false
var body: some View {
VStack {
if isVisible {
Text("Welcome to the App!")
.font(.largeTitle)
.transition(.move(edge: .bottom))
}
}
.onAppear {
withAnimation(.spring()) {
isVisible = true
}
}
}
}
The Vertigo Trigger: Ignoring System Motion Preferences
At first glance, this looks great. It's fluid and modern. But there's a major problem: it's hard-coded to animate. If a user has Reduce Motion turned on in their System Settings, this view will still slide up from the bottom. For someone prone to motion sickness, that sliding movement can cause genuine discomfort. We're essentially ignoring a direct request from the user about how they need to interact with their device.
The mistake here is assuming that a "good" animation is always good. In professional Swift development, your animations should be conditional. You need to ask the system, "Does the user want to see this motion?" before you trigger it.
Listening to the Environment with accessibilityReduceMotion
To fix this, we use the @Environment property wrapper. SwiftUI provides a specific key called \.accessibilityReduceMotion. This is a Boolean that tells you exactly what the user has selected in their system settings. If it's true, we should either strip the animation entirely or replace the "sliding" motion with something more subtle, like a simple cross-fade.
Here is how I would rewrite that onboarding view to be actually accessible:
struct OnboardingView: View {
@State private var isVisible = false
// We pull the user's preference from the environment
@Environment(\.accessibilityReduceMotion) var reduceMotion
var body: some View {
VStack {
if isVisible {
Text("Welcome to the App!")
.font(.largeTitle)
// Use a ternary to decide the transition
.transition(reduceMotion ? .opacity : .move(edge: .bottom))
}
}
.onAppear {
// If reduceMotion is true, we can use a much simpler animation
// or remove the spring bounce entirely.
withAnimation(reduceMotion ? .linear(duration: 0.2) : .spring()) {
isVisible = true
}
}
}
}
Notice what happened here. I didn't just delete the animation—because some users who "Reduce Motion" still appreciate a gentle fade-in so the UI doesn't just "pop" into existence. Instead, I swapped a spatial transition (moving from point A to B) for a non-spatial transition (changing opacity). This preserves the polish of the app without triggering the physiological response that Reduce Motion is designed to prevent.
I recommend making this a habit. Whenever you reach for .transition(.move(...)) or .offset() inside an animation block, immediately ask yourself if that movement is necessary or if it should be gated behind the reduceMotion check.
📋 Practical Task
Adapting the Dashboard Slide-In for Motion Sensitivity
You have been handed a Dashboard view that features a "Notification Banner" that slides in from the top of the screen whenever a new alert arrives. Currently, it uses a high-energy spring animation and a .move(edge: .top) transition, which violates the project's accessibility guidelines.
Your Task: Modify the provided code to respect the user's system settings. If accessibilityReduceMotion is enabled, the banner should simply fade in (using .opacity) with a short, linear animation instead of sliding in with a spring.
struct NotificationBanner: View {
@State private var isShowing = false
// TODO: Add the environment variable here
var body: some View {
VStack {
if isShowing {
Text("You have a new message!")
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
// TODO: Update this transition based on accessibility settings
.transition(.move(edge: .top))
}
Button("Show Notification") {
// TODO: Update this animation based on accessibility settings
withAnimation(.spring()) {
isShowing = true
}
}
}
}
}There are no comments for now.