Building a Speedometer App with SwiftUI: A Step-by-Step Guide
SwiftUI has revolutionized the way we create user interfaces on Apple platforms. With its declarative syntax and powerful features, SwiftUI allows developers to build elegant and efficient apps with less code. In this article, we’ll explore how to create a Speedometer App using SwiftUI, leveraging the framework’s capabilities to design a visually appealing and interactive user interface.
Introduction
Our Speedometer App will consist of a circular meter with an arrow indicating the speed progress. Users can update the speed by tapping the “Update” button and reset it to zero with the “Reset” button. Let’s break down the implementation step by step.
Setting up the Project
To get started, create a new SwiftUI project and replace the default code in the SpeedometerApp
struct with the following:
@main
struct SpeedometerApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
This code defines the entry point of our app, pointing to the ContentView
struct.
ContentView Structure
Now, let’s define the ContentView
struct, which serves as the main container for our Speedometer App.
struct ContentView: View {
var body: some View {
Home()
}
}
The ContentView
struct simply embeds the Home
view, where the main functionality and UI elements are implemented.
Home View
The Home
view is where the core functionality of the Speedometer App resides. It includes a meter, update, and reset buttons, and handles the progress state:
struct Home: View {
@State var progress: CGFloat = 0
let colors = [Color("color1"), Color("color2")]
var body: some View {
VStack {
Meter(progress: self.$progress)
HStack(spacing: 25) {
// Update Button
Button(action: {
if self.progress != 100 {
withAnimation(Animation.default.speed(0.55)) {
self.progress += 10
}
}
}) {
// Button styling
Text("Update")
.padding(.vertical)
.frame(width: (UIScreen.main.bounds.width)/2.5)
}
// Button background
.background(Capsule().stroke(LinearGradient(gradient: .init(colors: self.colors), startPoint: .leading, endPoint: .trailing), lineWidth: 2))
.padding()
// Reset Button
Button(action: {
withAnimation(Animation.default.speed(0.55)) {
self.progress = 0
}
}) {
// Button styling
Text("Reset")
.padding(.vertical)
.frame(width: (UIScreen.main.bounds.width)/2.5)
}
// Button background
.background(Capsule().stroke(LinearGradient(gradient: .init(colors: self.colors), startPoint: .leading, endPoint: .trailing), lineWidth: 2))
.padding()
}
}
}
}
The Home
view encapsulates the meter, update, and reset buttons in a vertical stack (VStack
). The Meter
view is responsible for rendering the circular speedometer.
Meter View
The Meter
view defines the appearance of our speedometer, including the circular progress bar and arrow indicator:
struct Meter: View {
@Binding var progress: CGFloat
let colors = [Color("color1"), Color("color2")]
var body: some View {
ZStack {
// Circular progress bar
ZStack {
Circle()
.trim(from: 0, to: 0.5)
.stroke(Color.black.opacity(0.2), lineWidth: 55)
.frame(width: 280, height: 280)
Circle()
.trim(from: 0, to: self.setProgress())
.stroke(AngularGradient(gradient: .init(colors: self.colors), center: .center, angle: .init(degrees: 180)), lineWidth: 55)
.frame(width: 280, height: 280)
}
.rotationEffect(.init(degrees: 180))
// Arrow indicator
ZStack (alignment: .bottom) {
// Arrow stem
self.colors[0]
.frame(width: 2, height: 95)
// Arrow head
Circle()
.fill(self.colors[1])
.frame(width: 15, height: 15)
}
.offset(y: -35)
.rotationEffect(.init(degrees: -90))
.rotationEffect(.init(degrees: self.setArrow()))
}
.padding()
.padding(.bottom, -140)
}
// Calculate progress for the circular bar
func setProgress() -> CGFloat {
let temp = self.progress / 2
return (temp * 0.01)
}
// Calculate rotation for the arrow indicator
func setArrow() -> Double {
let temp = self.progress/100
return (temp * 180)
}
}
The Meter
view combines two circular shapes to create the progress bar and uses gradients for a visually appealing effect. The arrow indicator is positioned and rotated based on the current progress.
Previewing the App
Finally, let’s add a preview for the ContentView
:
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Now, you can run your Speedometer App and see the circular meter in action!
Conclusion
In this article, we explored how to create a Speedometer App using SwiftUI. Leveraging SwiftUI’s declarative syntax, we built a visually appealing and interactive user interface with a circular progress bar and arrow indicator. Feel free to customize and enhance the app further to suit your preferences or integrate it into a larger project.
You can download the GitHub code for the above Speedometer example.
Happy coding!