{"id":15717915,"url":"https://github.com/bradhowes/knob","last_synced_at":"2025-05-12T20:55:33.393Z","repository":{"id":43660706,"uuid":"314813807","full_name":"bradhowes/Knob","owner":"bradhowes","description":"Simple knob control for iOS and macOS that depicts its path as an arc using CoreAnimation layers.","archived":false,"fork":false,"pushed_at":"2024-11-22T22:18:10.000Z","size":1354,"stargazers_count":22,"open_issues_count":0,"forks_count":5,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-17T16:13:06.949Z","etag":null,"topics":["ios","knob-control","macos","macosx","swift5","swiftpm","uislider"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bradhowes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"bradhowes"}},"created_at":"2020-11-21T13:04:56.000Z","updated_at":"2025-04-03T00:22:35.000Z","dependencies_parsed_at":"2023-02-08T14:16:35.259Z","dependency_job_id":"88734c4a-eb87-4b7a-96ad-8bca76d6f2a5","html_url":"https://github.com/bradhowes/Knob","commit_stats":{"total_commits":110,"total_committers":2,"mean_commits":55.0,"dds":0.009090909090909038,"last_synced_commit":"77943f04047fecf52f5be40a5692e19d0e315307"},"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradhowes%2FKnob","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradhowes%2FKnob/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradhowes%2FKnob/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradhowes%2FKnob/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bradhowes","download_url":"https://codeload.github.com/bradhowes/Knob/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253822908,"owners_count":21969834,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ios","knob-control","macos","macosx","swift5","swiftpm","uislider"],"created_at":"2024-10-03T21:51:37.222Z","updated_at":"2025-05-12T20:55:33.338Z","avatar_url":"https://github.com/bradhowes.png","language":"Swift","funding_links":["https://github.com/sponsors/bradhowes"],"categories":[],"sub_categories":[],"readme":"[![CI](https://github.com/bradhowes/Knob/workflows/CI/badge.svg)](https://github.com/bradhowes/Knob)\n[![COV](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/bradhowes/b307c10e9eb7882e6a83a3bcb1e5cd5d/raw/Knob-coverage.json)](https://github.com/bradhowes/Knob/blob/main/.github/workflows/CI.yml)\n[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fbradhowes%2FKnob%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/bradhowes/Knob)\n[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fbradhowes%2FKnob%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/bradhowes/Knob)\n[![License: MIT](https://img.shields.io/badge/License-MIT-A31F34.svg)](https://opensource.org/licenses/MIT)\n\n# Knob\n\nSimple slider-like control that depicts its path as an arc using CoreAnimation layers. Supports both iOS and macOS \nplatforms.\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003cimg src=\"https://github.com/bradhowes/Knob/blob/main/KnobMove.gif?raw=true\" alt=\"Animation of knob\"/\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003cimg src=\"https://github.com/bradhowes/Knob/blob/main/example.png?raw=true\" alt=\"Knobs in SoundFonts app\"/\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\nLike a slider, touch movements in the control change the value. For this implementation:\n\n* Only vertical movements change the value. Moving up will increase the value, moving down will decrease it. Simple.\n* By default, touch sensitivity is set to 1x the height of the knob -- a touch moving 1x the height would\n  change the value from 0.0 to 1.0. See the documentation for the `touchSensitivity` parameter.\n* Touch sensitivity can be decreased by moving the touch horizontally away from the control (either direction).\n  This is similar to the change in \"scrubbing\" speed when watching a video -- the further the touch moves away\n  from the scrubber, the finer the positioning is within the video (larger movements for same amount of change in value)\n* For now, this control reports value changes continuously -- there is no way to disable this as there is for\n  a UISlider.\n\nThe above picture was taken from my [SoundFonts](https://github.com/bradhowes/SoundFonts) iOS app where the\nknobs control various audio effects settings.\n\nIncluded is a playground for playing with a knob.\n\n# Versions\n\nThere are two major versions that have a slight change in the API. Originally (up to v1.6.0) the width of the knob's track and indicator were given as\nnotional pixel values. With v2.0.0, this changed to _factor_ which is multiplied with the minimum of the bounds width and height values to arrive at the\nwidth value to use. This allows for scaling of the knob features as the bounds of the knob changes.\n\n* old-style track/indicator width values -- use version v1.6.0\n* new-style track/indicator scaling factor values -- use version v2.x.y\n\n## Configuration\n\n* `minimumValue` -- the lowest value that the control will report out.\n* `maximumValue` -- the highest value that the control will report out.\n* `value` -- the current value of the control (settable).\n* `travelDistance` -- the number of pixels to use in mouse/touch event distance calculations. By default this is the \nsmaller of the frame width/height.\n* `touchSensitivity` -- scaling factor between touch movement and value change. The default is `1` which means that one\nmust drag 1x `travelDistance` in order to change the value from `miminimumValue` to `maximumValue`; a value of `2` would\nrequire 2x `travelDistance`.\n* `maxChangeRegionWidthPercentage` -- percentage of `travelDistance` that will always produce maximum value change. This\ndefines a vertical region in the center of the view. Mouse/touch events outside of this region will have increased \nsensitivity as the event X is further from the view center X, requiring more movement for the same change in value.\n* `trackWidthFactor` -- the line width of the knob's arc that appears as a track for the progress indicator. This is multiplied by the min width/height\nbounds value. Allows for scaling of the knob as the size increases.\n* `trackColor` -- the color of the arc that is drawn from the current value to the end.\n* `progressWidthFactor` -- the line width of the knob's arc that is drawn from the start to the current value. This is multipled by the min width/height\nbounds value. Allows for scaling of the knob as the size increases.\n* `progressColor` -- the color of the arc that is drawn from the start to the current value.\n* `indicatorWidthFactor` -- the line width of the knob's indicator that is draw from the current value towards the knob center. See `progressWidthFactor`.\n* `indicatorColor` -- the color of the line that is drawn from the current value to the center.\n* `indicatorLineLength` -- the amount of the line that is drawn from the current value to the center, where 0.0 \nindicates no line will be drawn, and 0.5 results in a line that is half-way to the knob center point.\n* `startAngle` -- the starting point in radians of the arc (see below)\n* `endAngle` -- the ending point in radians of the arc\n* `tickCount` -- number of ticks to show\n* `tickLineLength` -- fraction of a radius to draw (0.0 - 1.0)\n* `tickLineWidth` -- width of the tick line\n* `tickColor` -- the color of the tick line\n* `valueLabel` -- an optional UILabel/NSText to use to show a formatted textual representation of the current value.\n* `valueName` -- optional name to show in the `valueLabel` when the knob is not being manipulated. If `nil`, the knob \nvalue is always shown.\n* `valueFormatter` -- optional NumberFormatter to use to generate the textual representation shown in the `valueLabel`.\n* `valuePersistence` -- the number of seconds to persist the last value shown before showing the `valueName` \n(if non-nil). Only applies after the end of a mouse or touch event.\n\nArc angles are explained well in the \n[UIBezierPath documentation](https://developer.apple.com/documentation/uikit/uibezierpath/1624358-init). In brief, an \nangle of 0 will extend along the X axis, whereas an angle of π/2 will extend along the negative Y axis.\n\n![](https://docs-assets.developer.apple.com/published/741002b545/radians_circle_4de280d3-557c-4d69-8f12-efed200dbbd3.jpg)\n\nThe `draw` method used to render the knob's arc path draws in a clockwise fashion, so the end arc angle must be greater \nthan the start arc angle. The default values leave the opening in the arc path centered around the negative Y axis \n(pointing down), with an arc distance of 2/16 of the circumference.\n\n## Mouse/Touch Tracking\n\nAs one would expect, a touch in the knob's view area is tracked and any changes are reported to any registered actions:\n\n* Moving vertically up will increase the knob's value\n* Moving vertically down will decrease the knob's value\n* Moving horizontally does not affect the value but it does alter the `touchSensitivity` value that is used to generate \nupdates to the control's `value`, and the further away a touch moves horizontally from the center, the less sensitive \nthe vertical movements become -- one must move larger vertical distances to achieve the same value change.\n\n# SwiftUI Support\n\nThe package also includes a SwiftUI implementation: `KnobView`. The defaults should be good enough to start with, but \nthere are modifiers you can apply to your KnobView to configure the look you want. The `KnobView` constructor requires\ntwo state bindings, one for the knob's value and another for the knob's manipulating flag. Additionally, you can provide\nthe `minimumValue` and `maximumValue` values to override the defaults of 0.0 and 1.0 respectively.\n\n```\nKnobView(value: $volumeValue, manipulating: $volumeManipulating, minimum: 0.0, maximum: 1.0)\n  .trackStyle(width: trackWidth, color: trackColor)\n  .progressStyle(width: progressWidth, color: progressColor)\n  .indicatorStyle(width: progressWidth, color: progressColor, length: 0.3)\n```\n\n# Demo Apps\n\nThe `KnobDemo` folder contains an Xcode project which you can open to build simple demo apps for macOS and iOS \nplatforms. These also contain UI tests that make sure that the knobs properly track and report out their values.\n\nThe demo apps use SwiftUI for their view definitions. They both contain a `ContentView` properly wires up two `KnobView`\ninstances with a `Text` view that shows the value of a KnobView.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbradhowes%2Fknob","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbradhowes%2Fknob","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbradhowes%2Fknob/lists"}