Inspired by Apple Watch Digital Crown, CrownControl is a tiny accessory view that makes scrolling through scrollable content possible without lifting your thumb.
The crown consists of background and foreground surfaces. The foreground is an indicator which spins around the center as the attached scroll view offset changes.
- Can be repositioned either using force touch or long press.
- Can be spinned clockwise and counter clockwise.
- Most of the user interaction actions are configurable.
- The background and foreground sizes are configurable.
- Can be fully stylized.
The example project contains samples where each demonstrates the CrownControl usability in a scrollable context.
Web View / PDF | Contacts | Photo Collection |
---|---|---|
- iOS 9 or any higher version.
- Swift 4.2 or any higher version.
- CrownControl leans heavily on QuickLayout - A lightwight library written in Swift that is used to easily layout views programmatically.
CocoaPods is a dependency manager for Cocoa projects. You can install it with the following command:
$ gem install cocoapods
To integrate CrownControl into your Xcode project using CocoaPods, specify it in your Podfile
:
source 'https://github.com/cocoapods/specs.git'
platform :ios, '9.0'
use_frameworks!
pod 'CrownControl', '1.0.0'
Then, run the following command:
$ pod install
Using CrownControl
is really simple.
In your view:
- Define and bind
CrownAttributes
to a scroll view instance, and optionally customize the attributes. - Instantiate and bind the
CrownControl
instance to theCrownAttributes
instance. - Setup the
CrownControl
instance in a given superview using constraints to determine its position.
private var crownControl: CrownControl!
private var scrollView: UIScrollView!
private func setupCrownViewController() {
let attributes = CrownAttributes(scrollView: scrollView, scrollAxis: .vertical)
// Cling the bottom of the crown to the bottom of a view with -50 offset
let verticalConstraint = CrownAttributes.AxisConstraint(crownEdge: .bottom, anchorView: scrollView, anchorViewEdge: .bottom, offset: -50)
// Cling the trailing edge of the crown to the trailing edge of a view with -50 offset
let horizontalConstraint = CrownAttributes.AxisConstraint(crownEdge: .trailing, anchorView: scrollView, anchorViewEdge: .trailing, offset: -50)
// Setup the crown control within *self*
crownControl = CrownControl(attributes: attributes, delegate: self)
crownControl.layout(in: view, horizontalConstaint: horizontalConstraint, verticalConstraint: verticalConstraint)
}
To make the crown respond to scrolling events that emanates from any other invoker but the crown, add to scrollViewDidScroll(_:)
the following:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
crownControl?.spinToMatchScrollViewOffset()
}
CrownAttributes
is the crown appearance descriptor.
Its nested properties describe the look and feel of the crown.
The axis of the scroll view is a .horizontal
or .vertical
. It must be set during CrownAttributes
initialization.
The anchor position of the foreground indicator. Indicates where the foreground is initially positioned.
attributes.anchorPosition = .left
The posssible values are .left
, .right
, .top
, .bottom
.
The default value is .top
.
The direction to which the the indicator spins.
Example for setting the spin direction to be counter-clockwise.
attributes.spinDirection = .counterClockwise
The default value is clockwise
.
Describes the user interaction with the crown. Currently supported user interaction gestures: tap, double tap, long-press, and force-touch events.
When a single tap event occurs, scroll forward with the specified offset value:
attributes.userInteraction.singleTap = .scrollsForwardWithOffset(value: 20, animated: true)
When a single tap event occurs, perform a custom action.
attributes.userInteraction.singleTap = .custom(action: {
/* Do something */
})
When a double tap event occurs, scroll to the leading edge of the scroll view.
attributes.userInteraction.doubleTap = .scrollsToLeadingEdge
The crown can be dragged and dropped using force-touch if the force-touch trait is supported by the device hardware. If not, there is a fallback to long-press gesture.
attributes.repositionGesture = .prefersForceTouch(attributes: .init())
The background and foreground surfaces can be customized with various styles.
Example for setting the crown background to a gradient style, and its border to a specific color and width.
attributes.backgroundStyle.content = .gradient(gradient: .init(colors: [.white, .gray], startPoint: .zero, endPoint: CGPoint(x: 1, y: 1)))
attributes.backgroundStyle.border = .value(color: .gray, width: 1)
Describes the size of the crown and relations between the foreground and the background surfaces.
In the following example, setting scrollRelation
property to 10 means that 10 full spins of the foreground would make the scroll view offset reach its trailing edge.
attributes.sizes.scrollRelation = 10
Example for setting the edge size (width and height) of the crown to 60pts, and the foreground edge ratio to 25 precent of that size, which is 15pts.
attributes.sizes.backgroundSurfaceDiameter = 60
attributes.sizes.foregroundSurfaceEdgeRatio = 0.25
Feedback descriptor for the foreground when it reaches the anchor point on the crown surface.
The device generates impact-haptic-feedback when the scroll-view offset reaches the leading edge. The background surface of the crown flashes with color.
attributes.feedback.leading.impactHaptic = .light
attributes.feedback.leading.backgroundFlash = .active(color: .white, fadeDuration: 0.3)
Daniel Huri, huri000@gmail.com
CrownControl is available under the MIT license. See the LICENSE file for more info.