A configurable horizontal ruler picker for iOS. Built with UIKit for smooth, snappy scrolling — wrapped in SwiftUI for easy integration.
- Smooth UIKit-backed scrolling with snap-to-tick behavior
- Configurable haptic feedback (selection, impact, or none)
- Fully configurable: range, increments, tick spacing, labels, indicator color
- Works in any SwiftUI layout
- iOS 17+
In Xcode: File > Add Package Dependencies... and enter:
https://github.com/unibrix/SwiftHorizontalRuler
Or add to your Package.swift:
dependencies: [
.package(url: "https://github.com/unibrix/SwiftHorizontalRuler", from: "1.0.0")
]import SwiftHorizontalRulerstruct WeightPicker: View {
@State private var weight: Double = 70
var body: some View {
VStack {
Text("\(Int(weight)) kg")
.font(.largeTitle.bold())
HorizontalRuler(
value: $weight,
config: HorizontalRulerConfig(
minValue: 30,
maxValue: 200,
minorIncrement: 0.5,
majorIncrement: 5,
tickSpacing: 6
)
)
.frame(height: 70)
}
}
}HorizontalRuler(
value: $calories,
config: HorizontalRulerConfig(
minValue: 0,
maxValue: 5000,
minorIncrement: 50,
majorIncrement: 500,
tickSpacing: 6,
labelFormatter: { value in
value >= 1000 ? String(format: "%.0fk", value / 1000) : String(format: "%.0f", value)
}
)
)
.frame(height: 70)HorizontalRuler(
value: $volume,
config: HorizontalRulerConfig(
minValue: 10,
maxValue: 1000,
minorIncrement: 10,
majorIncrement: 50,
tickSpacing: 7,
indicatorColor: .systemOrange
)
)
.frame(height: 70)Haptics are enabled by default (.selection style). You can change the style or disable them:
// Heavier haptic feedback
HorizontalRuler(
value: $weight,
config: HorizontalRulerConfig(
minValue: 30, maxValue: 200,
minorIncrement: 0.5, majorIncrement: 5,
hapticStyle: .medium
)
)
.frame(height: 70)
// No haptics
HorizontalRuler(
value: $weight,
config: HorizontalRulerConfig(
minValue: 30, maxValue: 200,
minorIncrement: 0.5, majorIncrement: 5,
hapticStyle: .none
)
)
.frame(height: 70)Available styles: .none, .selection (default), .light, .medium, .heavy
Define app-specific presets:
extension HorizontalRulerConfig {
static func bodyWeight() -> HorizontalRulerConfig {
HorizontalRulerConfig(
minValue: 30, maxValue: 200,
minorIncrement: 0.5, majorIncrement: 5,
tickSpacing: 6
)
}
static func drinkVolume() -> HorizontalRulerConfig {
HorizontalRulerConfig(
minValue: 10, maxValue: 1000,
minorIncrement: 10, majorIncrement: 50,
tickSpacing: 7
)
}
}Then use them:
HorizontalRuler(value: $weight, config: .bodyWeight())
.frame(height: 70)| Parameter | Type | Default | Description |
|---|---|---|---|
minValue |
Double |
— | Minimum selectable value |
maxValue |
Double |
— | Maximum selectable value |
minorIncrement |
Double |
— | Step between minor ticks |
majorIncrement |
Double |
— | Step between labeled ticks |
tickSpacing |
CGFloat |
6 |
Point spacing between minor ticks |
indicatorColor |
UIColor |
.tintColor |
Center indicator line/triangle color |
hapticStyle |
RulerHapticStyle |
.selection |
Haptic feedback on each tick |
labelFormatter |
(Double) -> String |
"%.0f" |
Formats major tick labels |
- iOS 17+
- Swift 5.9+
- Xcode 15+
MIT License. See LICENSE for details.

