FlywheelControl

main

πŸŒ€ FlywheelControl A tactile, physics-based radial scrolling control for SwiftUI β€” inspired by analog flywheels and designed for intuitive zooming or value adjustment. Built with haptics, momentum, and accessibility in mind.
aweiner42/FlywheelControl

FlywheelControl

SwiftPM Compatible Platforms

A tactile, momentum-based radial control for SwiftUI β€” inspired by analog flywheels.
FlywheelControl Logo

A SwiftUI-based, physics-inspired radial scroller for zooming, scrubbing, and precision value adjustments.
FlywheelControl mimics the feel of a real-world dial β€” complete with momentum, resistance, haptic feedback, and pen-friendly input.


πŸŒ€ Why FlywheelControl?

We needed a more natural way to zoom β€” something better than pinch and expand.
So we built a rotary-style control that works with a finger or stylus, and feels real thanks to physics and CoreHaptics.


✨ Features

  • πŸŽ›οΈ Inertial spinning like a physical dial
  • πŸ“± One-finger- and Apple Pencil-friendly
  • πŸ’₯ Haptic ticks for tactile feedback
  • 🎨 Fully SwiftUI and easy to customize
  • 🧠 Binding-driven: tracks a zoom position with clamped min/max offsets and live span adjustments
  • πŸš€ Smooth momentum decay and natural stopping behavior
  • 🎨 Custom skin support: Apply your own ruler artwork for a fully branded experience
  • πŸ›‘ Clamp limits: minValue and maxValue must be > 0
  • πŸ“ spanCM defines visible range (must be > 0)

πŸ“¦ Installation

Swift Package Manager

In Xcode:

  1. Go to File β†’ Add Packages…
  2. Enter the URL: https://github.com/aweiner42/FlywheelControl
  3. Choose the latest version (e.g., 1.1.0)

Or add it to your Package.swift:

.package(url: "https://github.com/aweiner42/FlywheelControl.git", from: "1.1.0")

Then add the dependency to your target:

.target(
    name: "YourApp",
    dependencies: ["FlywheelControl"]
)

Import the module where needed:

import FlywheelControl

πŸ–Ό Adding a Custom Skin

Place your ruler artwork (e.g., RulerSkin.png) in your app’s Asset Catalog (Assets.xcassets) and assign it the name RulerSkin. FlywheelControl will automatically load this image if present.

The package includes a sample ruler skin (DefaultRuler.png). You can copy it into your app’s Asset Catalog and rename it RulerSkin for immediate use.


🎯 Example Integration

@State private var zoomPosition: Double = 0
@State private var minZoom: Double = 1
@State private var maxZoom: Double = 90
@State private var zoomSpan: Double = 20

var body: some View {
   FlywheelControl(
    trackImage: Image("RulerSkin"), // custom ruler skin from Assets
    position: $zoomPosition,        // current scroll/zoom value
    maxOffset: $maxZoom,            // maximum scroll value (must be > 0)
    minOffset: $minZoom,            // minimum scroll value (must be > 0)
    spanCM: $zoomSpan               // visible range in cm (must be > 0)
    )
    .onChange(of: zoomPosition) { newValue in
        zoomManager.adjustZoom(by: CGFloat(newValue))
    }
}

πŸ”§ Requirements

  • iOS 17.0+
  • macOS 12.0+
  • Swift 5.9+
  • SwiftUI + Combine

πŸ§ͺ Previews & Tests

FlywheelControl includes:

  • πŸ” SwiftUI Previews
  • 🧱 Modular design (no app dependencies)

πŸ”„ Try It Live

Clone this repo and open FlywheelDemoApp/FlywheelDemoApp.xcodeproj to explore the latest refinements including improved momentum physics and control skinning.


✍️ Created by

Alan Weiner β€’ SIME Corp
Inventor. Designer. Engineer. Collaborating with AI to shape intuitive interfaces.

Version 1.1.0 includes performance tuning, refined gesture handling, and support for custom ruler skins with clamped value ranges.

Description

  • Swift Tools 6.1.0
View More Packages from this Author

Dependencies

  • None
Last updated: Mon Aug 11 2025 01:12:34 GMT-0900 (Hawaii-Aleutian Daylight Time)