How to Create a Custom Weight Picker in SwiftUI
Lately, I’ve been working on a health app and needed a custom weight picker. I didn’t want to use the standard iOS picker. I was aiming for something that stands out and looks much better. I also wanted users to be able to choose between kg and lbs. Here’s what I came up with.
What Will You Learn?
- How to use built-in pickers in SwiftUI.
- How to create a custom component for more advanced requirements.
- How to handle dynamic switching between lbs and kg units.
Picker in SwiftUI – Where to Start?
SwiftUI comes with a native Picker, which works great in many scenarios. For simple use cases like selecting a number, just a few lines of code are enough:
struct WeightPicker: View {
@State private var selectedWeight: Int = 150
var body: some View {
Picker("Select Weight", selection: $selectedWeight) {
ForEach(50...300, id: \.self) { weight in
Text("\(weight) lbs")
}
}
.pickerStyle(.wheel)
}
}
This kind of picker works well, but sometimes a designer has a different vision – or maybe you just want your app to stand out. That was the case for me. So, I built my own custom picker.
Custom Weight Picker – Step by Step
Let’s create a WheelPickerView that will:
- Support both lbs and kg units.
- Have a clean design and support different styles.
- Allow dynamic adjustment of the weight range.
Implementation of WheelPicker
Here’s the code for our custom component. It’s a complete implementation that you can copy into your project and customize as needed:
struct WheelPicker: View {
var config: Config
@Binding var value: CGFloat
@State var isLoaded: Bool = false
@State private var scrollPosition: Int?
var body: some View {
GeometryReader {
let size = $0.size
let horizontalPadding = size.width / 2
ScrollView(.horizontal) {
HStack(spacing: config.spacing) {
let totalSteps = config.steps * config.count
ForEach(0...totalSteps, id: \.self) { index in
let isMajorTick = index % config.steps == 0
Divider()
.background(isMajorTick ? Color.primary : .gray)
.frame(width: 0, height: isMajorTick ? 20 : 10)
.frame(maxHeight: 20, alignment: .bottom)
.overlay(alignment: .bottom) {
if isMajorTick && config.showsText {
Text("\(index / config.steps * config.multiplier)")
.font(.caption)
.fontWeight(.semibold)
.fixedSize()
.offset(y: 20)
}
}
}
}
.frame(height: size.height)
.scrollTargetLayout()
}
.scrollIndicators(.hidden)
.scrollTargetBehavior(.viewAligned)
.scrollPosition(id: $scrollPosition)
.overlay(alignment: .bottom) {
Rectangle()
.frame(width: 1, height: 40)
.padding(.bottom, 20)
}
.safeAreaPadding(.horizontal, horizontalPadding)
.onAppear {
scrollPosition = (Int(value) * config.steps) / config.multiplier
isLoaded = true
}
.onChange(of: scrollPosition) { newValue in
if let newValue {
value = (CGFloat(newValue) / CGFloat(config.steps))
* CGFloat(config.multiplier)
}
}
}
}
struct Config: Equatable {
var count: Int
var steps: Int = 10
var multiplier: Int = 10
var spacing: CGFloat = 5
var showsText: Bool = true
}
}
How to Use It in Practice?
Now that we have our component ready, let’s see how to use it. Let’s say we want to create a view that allows selecting weight in lbs or kg:
struct WeightPickerView: View {
@State private var unit: Unit = .lbs
@State private var weight: CGFloat = 150
var body: some View {
VStack {
Picker("Unit", selection: $unit) {
Text("lbs").tag(Unit.lbs)
Text("kg").tag(Unit.kg)
}
.pickerStyle(SegmentedPickerStyle())
.padding()
WheelPicker(
config: .init(
count: unit == .lbs ? 300 : 150,
multiplier: unit == .lbs ? 1 : 1,
steps: 10
),
value: $weight
)
.frame(height: 200)
.padding()
Text("Selected Weight: \(weight, specifier: "%.1f") \(unit.rawValue)")
.font(.headline)
.padding()
}
}
enum Unit: String {
case lbs = "lbs"
case kg = "kg"
}
}
Visualization in Action
Here’s an example of how our picker works in action:
Customization and Optimization
Thanks to the modular structure, you can easily customize the WheelPicker
to fit your needs. For example:
- Change the number of steps (config.steps).
- Add animations or visual effects.
- Adjust weight ranges based on the user’s region (e.g., different ranges for lbs and kg).
Summary
In this post, I showed you:
- How to build a
WheelPicker
from scratch.
- How to integrate it with dynamic units (lbs and kg).
- How to easily adjust weight ranges.
I hope you found this article helpful.