Take On / Off Light Animation

Sometimes, you have to explore different paths to find where you truly belong. For a developer, that path is often paved with different languages, frameworks, and platforms. My own journey has taken me through the worlds of backend development, cross-platform apps with React Native and Flutter, and of course, native iOS. And after seeing what each has to offer, I’ve come to a clear and exciting realization: my true passion lies in building for native iOS.

TakeOffLight

A Journey Across the Stack

Every technology I’ve worked with has taught me something invaluable.

  • Building backend systems gave me a deep appreciation for data architecture, APIs, and the logic that powers applications from behind the scenes.
  • Working with React Native and Flutter was a fantastic lesson in efficiency and the challenge of creating a consistent experience across different operating systems. The promise of “write once, run anywhere” is compelling, and I learned a great deal about managing a single codebase for multiple targets.

This broad perspective is something I wouldn’t trade. It gave me a holistic view of how a product comes to life, from the database all the way to the user’s screen. But it also created a point of comparison that continually highlighted what makes native development, and specifically iOS, so special to me.

The Pull of Native iOS

There’s a certain elegance and satisfaction in iOS development that I kept coming back to. The seamless integration between the Swift language, powerful frameworks like SwiftUI, and the hardware itself allows for a level of polish and performance that is simply a joy to create. The pursuit of the perfect animation, the crispness of a native UI component, and the satisfaction of building something that feels completely at home on the device—that’s what excites me as a developer.

After reflecting on this, I felt a renewed surge of energy and inspiration. To channel it into something tangible, I decided to build a small, focused project that captures the kind of delightful interaction I love: a simple “lights-out” animation.

Project: The “Lights-Out” Animation

I wanted to create more than just a toggle. I wanted to build an experience. The idea was to mimic the satisfying, physical act of pulling a cord to turn a light on and off, complete with animated light beams, a draggable cord, and a crisp sound effect.

Here is a video of the final result!

This project, while small, was a great way to put SwiftUI’s strengths to the test, focusing on:

  • Declarative UI: Building complex views that react to state changes.
  • State Management: Using @State and @Binding to drive the entire UI from a single source of truth (isOn).
  • Animation: Leveraging withAnimation and animation modifiers to create fluid transitions and spring physics for the pull cord.
  • Gestures: Implementing a DragGesture to create an interactive and intuitive pull-cord mechanism.

A Look at the Code

For those interested in how it works, the full source code is available on my GitHub. But here are a few key pieces that bring the experience to life.

The core of the app is the ContentView, which manages the isOn state. This single boolean drives everything from the background color to the sound playback.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
struct ContentView: View {
@State var isOn = true

var body: some View {
ZStack {
// The background's gradient changes based on the 'isOn' state
LightBeamBackground(isOn: self.$isOn)
.ignoresSafeArea()

// The main lightbulb button that can also toggle the state
Button(action: {
withAnimation {
self.isOn.toggle()
}
}) {
// ... button content
}
}
.overlay(alignment: .topTrailing) {
// The interactive pull cord is an overlay
PullCord(isOn: self.$isOn)
.padding(.top, -40)
}
.onChange(of: self.isOn) { _, newValue in
// Play a sound effect whenever the state changes
SoundPlayer.shared.play(newValue ? .lightOn : .lightOff)
}
}
}

The most interactive piece is the PullCord view. It uses a DragGesture to track the user’s finger and provides physical feedback by stretching. When the drag is released, it decides whether to toggle the light based on how far it was pulled.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct PullCord: View {
@Binding var isOn: Bool
@State private var dragOffsetY: CGFloat = 0
private let maxDragDistance: CGFloat = 140

var body: some View {
ZStack(alignment: .top) {
// ... visual components for the cord and handle
}
.gesture(
DragGesture(minimumDistance: 0)
.onChanged { value in
let dy = max(0, value.translation.height)
dragOffsetY = min(dy, maxDragDistance)
}
.onEnded { value in
let shouldToggle = value.translation.height > maxDragDistance * 0.6
withAnimation(.spring(response: 0.35, dampingFraction: 0.8)) {
dragOffsetY = 0
if shouldToggle {
isOn.toggle()
}
}
}
)
}
}

This project reaffirmed my belief in the power and elegance of SwiftUI for creating these kinds of delightful, polished user experiences.

What’s Next

This journey of exploration across the tech stack has been invaluable, but now I know where I want to build my future. I’m currently based in Calgary and am actively seeking my next role as an iOS Developer. I’m looking for a team where I can contribute my diverse experience, my passion for Apple’s ecosystem, and my drive to build beautiful, high-performing applications.

If you’re looking for a passionate iOS developer with a broad technical perspective, I would love to connect. You can find my LinkedIn profile here.

Good night, Calgary. Hopeful for new opportunities and interviews tomorrow!