Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c3cf5527fd | |||
| ad0cf786c8 | |||
| ea35c92b6a | |||
| 1490573852 | |||
| 68f31bfa1f | |||
| 6086b3bad3 | |||
| 42dbab3b89 | |||
| c234281a9e | |||
| 070d2615c3 | |||
| 4206b108a9 | |||
| 9596434dca | |||
| 539be74ce8 | |||
| 70fa6d1acd |
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
</plist>
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="T5Y-X3-uD8">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="T5Y-X3-uD8">
|
||||
<device id="retina6_1" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@@ -39,13 +39,13 @@
|
||||
<color key="value" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
<userDefinedRuntimeAttribute type="color" keyPath="color2">
|
||||
<color key="value" red="0.15235996039999999" green="0.17974209029999999" blue="0.22156283139999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="value" red="0.15294117647058825" green="0.1803921568627451" blue="0.2196078431372549" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
<userDefinedRuntimeAttribute type="color" keyPath="color1">
|
||||
<color key="value" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
<userDefinedRuntimeAttribute type="color" keyPath="color4">
|
||||
<color key="value" red="0.13288810049999999" green="0.14611955109999999" blue="0.1740877602" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="value" red="0.13333333333333333" green="0.14509803921568626" blue="0.17254901960784313" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
<userDefinedRuntimeAttribute type="color" keyPath="maxidotOffColor">
|
||||
<color key="value" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
@@ -57,7 +57,7 @@
|
||||
<color key="value" red="1" green="0.5" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
<userDefinedRuntimeAttribute type="color" keyPath="highlightColor">
|
||||
<color key="value" red="0.33810979099999999" green="0.36411823659999998" blue="0.42480460930000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="value" red="0.33725490196078434" green="0.36470588235294116" blue="0.42352941176470588" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
<userDefinedRuntimeAttribute type="color" keyPath="textColor">
|
||||
<color key="value" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
@@ -70,7 +70,7 @@
|
||||
<real key="value" value="0.80000000000000004"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
<userDefinedRuntimeAttribute type="color" keyPath="pointerColor2">
|
||||
<color key="value" red="0.2530443235" green="0.27250927159999999" blue="0.31792748339999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="value" red="0.25490196078431371" green="0.27058823529411763" blue="0.31764705882352939" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
<userDefinedRuntimeAttribute type="color" keyPath="pointerColor1">
|
||||
<color key="value" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
@@ -87,7 +87,7 @@
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="14" translatesAutoresizingMaskIntoConstraints="NO" id="6nV-kU-kJg">
|
||||
<rect key="frame" x="41.5" y="565" width="331" height="257"/>
|
||||
<subviews>
|
||||
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.86000001430511475" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="Yng-5x-Nhy" customClass="DesignableSlider" customModule="JOCircularSlider_Example" customModuleProvider="target">
|
||||
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.85999999999999999" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="Yng-5x-Nhy" customClass="DesignableSlider" customModule="JOCircularSlider_Example" customModuleProvider="target">
|
||||
<rect key="frame" x="-2" y="0.0" width="335" height="16"/>
|
||||
<color key="tintColor" red="0.20392156859999999" green="0.23529411759999999" blue="0.28235294119999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="maximumTrackTintColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
@@ -197,7 +197,7 @@
|
||||
<viewLayoutGuide key="safeArea" id="zts-4c-D2G"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="color" keyPath="color1">
|
||||
<color key="value" red="0.10980392160000001" green="0.12941176469999999" blue="0.15686274510000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="value" red="0.10980392156862745" green="0.12941176470588234" blue="0.15686274509803921" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
<userDefinedRuntimeAttribute type="color" keyPath="color2">
|
||||
<color key="value" red="0.1333333333" green="0.14117647059999999" blue="0.1647058824" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.5</string>
|
||||
<string>2.0.3</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@@ -11,7 +11,14 @@ import JOCircularSlider
|
||||
|
||||
class ViewController: UIViewController {
|
||||
|
||||
@IBOutlet weak var circularSlider: CircularSlider!
|
||||
@IBOutlet weak var circularSlider: CircularSlider! {
|
||||
didSet {
|
||||
circularSlider.addTarget(self, action: #selector(handleValueChanged(_:)), for: .valueChanged)
|
||||
circularSlider.addTarget(self, action: #selector(didBeginEditing), for: .editingDidBegin)
|
||||
circularSlider.addTarget(self, action: #selector(didEndEditing), for: .editingDidEnd)
|
||||
}
|
||||
}
|
||||
|
||||
@IBOutlet weak var stackView: UIStackView!
|
||||
@IBOutlet weak var widthConstraint: NSLayoutConstraint! {
|
||||
didSet {
|
||||
@@ -71,7 +78,8 @@ class ViewController: UIViewController {
|
||||
circularSlider.startAngle = direction.startAngle
|
||||
circularSlider.endAngle = direction.endAngle
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
self.circularSlider.setValue(self.circularSlider.value, isPercentage: true)
|
||||
let value = self.circularSlider.value
|
||||
self.circularSlider.value = value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,8 +112,8 @@ class ViewController: UIViewController {
|
||||
circularSlider.knobMiddleCircleMultiplier = newMultiplier
|
||||
}
|
||||
|
||||
circularSlider.setValue(circularSlider.value, isPercentage: true)
|
||||
|
||||
let value = circularSlider.value
|
||||
circularSlider.value = value
|
||||
}
|
||||
|
||||
@IBAction private func changeDotCount(_ sender: UISlider) {
|
||||
@@ -117,6 +125,23 @@ class ViewController: UIViewController {
|
||||
circularSlider.maxidotOnColor = UIColor(hue: CGFloat(sender.value), saturation: 1, brightness: 1, alpha: 1)
|
||||
}
|
||||
|
||||
|
||||
@objc func didBeginEditing() {
|
||||
print("\n" + "didBeginEditing\n")
|
||||
}
|
||||
|
||||
@objc func didEndEditing() {
|
||||
print("\n" + "didBeginEditing\n")
|
||||
}
|
||||
|
||||
@IBAction func handleValueChanged(_ sender: CircularSlider) {
|
||||
print("value: \(sender.value)")
|
||||
|
||||
// If you need to override the formatting of the text in the
|
||||
// label, then use this.
|
||||
// circularSlider.setLabelText(String(format: "%.3f", sender.value))
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1010"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "2A42614483C3321D5589EF03C68BD21C"
|
||||
BuildableName = "JOCircularSlider.framework"
|
||||
BlueprintName = "JOCircularSlider"
|
||||
ReferencedContainer = "container:Pods.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "2A42614483C3321D5589EF03C68BD21C"
|
||||
BuildableName = "JOCircularSlider.framework"
|
||||
BlueprintName = "JOCircularSlider"
|
||||
ReferencedContainer = "container:Pods.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.5</string>
|
||||
<string>2.0.3</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.5</string>
|
||||
<string>2.0.3</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.5</string>
|
||||
<string>2.0.3</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'JOCircularSlider'
|
||||
s.version = '1.0.5'
|
||||
s.version = '2.0.3'
|
||||
s.summary = 'A highly customisable and reusable iOS circular slider.'
|
||||
|
||||
# This description is used to generate tags and improve search results.
|
||||
|
||||
@@ -227,21 +227,24 @@ open class CircularSlider: UIControl {
|
||||
set { renderer.miniDotView.highlightColor = newValue ? highlightColor : nil }
|
||||
}
|
||||
|
||||
@IBInspectable open var minimumValue: CGFloat {
|
||||
get { return CGFloat(renderer.minimumValue) }
|
||||
/**
|
||||
This value will be pinned to minimumValue/maximumValue
|
||||
The default value of this property is 0.0.
|
||||
*/
|
||||
@IBInspectable open var value: CGFloat { // always min <= value <= max
|
||||
get { return CGFloat(renderer.value) * (maximumValue - minimumValue) + minimumValue }
|
||||
set {
|
||||
renderer.minimumValue = Float(newValue)
|
||||
setValue(value, isPercentage: true)
|
||||
guard maximumValue >= minimumValue else {
|
||||
fatalError("`maximumValue` should be greater then `minimumValue`.")
|
||||
}
|
||||
let rangedValue = min(maximumValue, max(minimumValue, newValue))
|
||||
renderer.value = Float((rangedValue - minimumValue) / (maximumValue - minimumValue))
|
||||
}
|
||||
}
|
||||
|
||||
@IBInspectable open var maximumValue: CGFloat {
|
||||
get { return CGFloat(renderer.maximumValue) }
|
||||
set {
|
||||
renderer.maximumValue = Float(newValue)
|
||||
setValue(value, isPercentage: true)
|
||||
}
|
||||
}
|
||||
@IBInspectable open var minimumValue: CGFloat = 0
|
||||
|
||||
@IBInspectable open var maximumValue: CGFloat = 100
|
||||
|
||||
@IBInspectable open var startAngle: CGFloat {
|
||||
get { return renderer.startAngle.toDegree }
|
||||
@@ -258,29 +261,29 @@ open class CircularSlider: UIControl {
|
||||
set { renderer.isClockwise = newValue }
|
||||
}
|
||||
|
||||
@IBInspectable open var labelDecimalPlaces: Int = 0
|
||||
|
||||
// MARK: - Private Properties
|
||||
|
||||
lazy private var renderer = Renderer(with: self)
|
||||
private var lastTouchAngle: CGFloat = 0
|
||||
open private (set) var value: Float = 0 // 0 <= value <= 1
|
||||
|
||||
private var centerPoint: CGPoint { return CGPoint(x: bounds.midX, y: bounds.midY) }
|
||||
|
||||
// MARK: - Functions
|
||||
|
||||
override public init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
setValue(renderer.minimumValue)
|
||||
value = minimumValue
|
||||
}
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
setValue(renderer.minimumValue)
|
||||
value = minimumValue
|
||||
}
|
||||
|
||||
override open func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
renderer.update(bounds, value: value)
|
||||
renderer.updateUI(in: bounds)
|
||||
}
|
||||
|
||||
// MARK: - Touches
|
||||
@@ -291,6 +294,7 @@ open class CircularSlider: UIControl {
|
||||
|
||||
let distanceToOrigin = location - centerPoint
|
||||
lastTouchAngle = atan2(distanceToOrigin.y, distanceToOrigin.x).toDegree
|
||||
sendActions(for: .editingDidBegin)
|
||||
}
|
||||
|
||||
override open func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
@@ -307,40 +311,32 @@ open class CircularSlider: UIControl {
|
||||
|
||||
let angelDeltaAsPercentage = Float(angelDelta / angleRange)
|
||||
guard abs(angelDeltaAsPercentage) < 1 else { return }
|
||||
let newValue = isClockwise ? value + angelDeltaAsPercentage : value - angelDeltaAsPercentage
|
||||
setValue(newValue, isPercentage: true)
|
||||
let newValue = renderer.value + (isClockwise ? angelDeltaAsPercentage : -angelDeltaAsPercentage)
|
||||
renderer.value = max(0, min(newValue, 1))
|
||||
|
||||
}
|
||||
|
||||
open override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
super.touchesEnded(touches, with: event)
|
||||
sendActions(for: .editingDidEnd)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
open func setValue(_ newValue: Float, isPercentage: Bool = false) {
|
||||
guard maximumValue > minimumValue else {
|
||||
fatalError("`maximumValue` should be greater then `minimumValue`.")
|
||||
}
|
||||
|
||||
if isPercentage {
|
||||
value = min(1, max(0, newValue))
|
||||
} else {
|
||||
// convert to percentage
|
||||
let rangedValue = min(renderer.maximumValue, max(renderer.minimumValue, newValue))
|
||||
value = (rangedValue - renderer.minimumValue) / (renderer.maximumValue - renderer.minimumValue)
|
||||
}
|
||||
|
||||
// updates
|
||||
renderer.updateText(with: value)
|
||||
renderer.updatePointerView(in: bounds, value: value)
|
||||
renderer.maxiDotView.updateColors(using: value)
|
||||
}
|
||||
|
||||
open func setTextFont(named: String, textColor: UIColor, multiplier: CGFloat) {
|
||||
open func setLabelFont(named: String, textColor: UIColor, multiplier: CGFloat) {
|
||||
textFontSizeMultiplier = multiplier
|
||||
let textSize = bounds.height * multiplier
|
||||
renderer.setTextFont(named: named, textColor: textColor, textSize: textSize)
|
||||
}
|
||||
|
||||
open func setTextShadow(color: UIColor, opacity: Float = 1, offset: CGSize = CGSize(width: 1, height: 1), radius: CGFloat = 0) {
|
||||
open func setLabelShadow(color: UIColor, opacity: Float = 1, offset: CGSize = CGSize(width: 1, height: 1), radius: CGFloat = 0) {
|
||||
renderer.setTextShadow(color: color, opacity: opacity, offset: offset, radius: radius)
|
||||
}
|
||||
|
||||
open func setLabelText(_ text: String?) {
|
||||
renderer.textField.text = text
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Renderer {
|
||||
@@ -381,9 +377,18 @@ class Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var minimumValue: Float = 0.0
|
||||
fileprivate var maximumValue: Float = 100.0
|
||||
fileprivate var value: Float = 0.0 { // 0 <= renderer.value <= 1
|
||||
didSet {
|
||||
// updates
|
||||
updateText()
|
||||
if let bounds = circularSlider?.bounds {
|
||||
updatePointerView(in: bounds)
|
||||
}
|
||||
maxiDotView.updateColors(using: value)
|
||||
|
||||
circularSlider?.sendActions(for: .valueChanged)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
@@ -392,8 +397,8 @@ class Renderer {
|
||||
commonInit()
|
||||
}
|
||||
|
||||
func update(_ bounds: CGRect, value: Float) {
|
||||
updatePointerView(in: bounds, value: value)
|
||||
func updateUI(in bounds: CGRect) {
|
||||
updatePointerView(in: bounds)
|
||||
updateTextField(in: bounds)
|
||||
updateDotViews(in: bounds)
|
||||
}
|
||||
@@ -449,7 +454,7 @@ private extension Renderer {
|
||||
textField.layer.cornerRadius = 3
|
||||
textField.textAlignment = .center
|
||||
textField.text = ""
|
||||
textField.keyboardType = .numberPad
|
||||
textField.keyboardType = .numbersAndPunctuation
|
||||
textField.keyboardAppearance = .dark
|
||||
textField.clearsOnInsertion = true
|
||||
textField.adjustsFontSizeToFitWidth = true
|
||||
@@ -472,13 +477,14 @@ private extension Renderer {
|
||||
|
||||
@objc func keyboardDoneButtonTapped() {
|
||||
textField.endEditing(true)
|
||||
guard let circularSlider = circularSlider else { return }
|
||||
guard let text = textField.text, let newValue = Int(text) else {
|
||||
updateText(with: circularSlider.value, isPercentage: true)
|
||||
updateText()
|
||||
return
|
||||
}
|
||||
circularSlider.setValue(Float(newValue))
|
||||
maxiDotView.layoutSubviews()
|
||||
circularSlider?.value = CGFloat(newValue)
|
||||
circularSlider?.sendActions(for: .editingDidEnd)
|
||||
|
||||
//maxiDotView.setNeedsLayout()
|
||||
}
|
||||
|
||||
// MARK: - Updates
|
||||
@@ -508,20 +514,28 @@ private extension Renderer {
|
||||
textField.isHidden = textFieldIsHidden
|
||||
}
|
||||
|
||||
func updateText(with newValue: Float, isPercentage: Bool = true) {
|
||||
var value = Int(newValue)
|
||||
if isPercentage {
|
||||
// convert value (0...1) to a value within proposed range
|
||||
value = Int(newValue * (maximumValue - minimumValue) + minimumValue)
|
||||
func updateText() {
|
||||
guard let circularSlider = circularSlider else { return }
|
||||
|
||||
let minimum = Float(circularSlider.minimumValue)
|
||||
let maximum = Float(circularSlider.maximumValue)
|
||||
let _value = Float(circularSlider.value)
|
||||
|
||||
var decimalPlaces = circularSlider.labelDecimalPlaces
|
||||
if value != 0 && _value.rounded(.towardZero) == 0 && decimalPlaces == 0 {
|
||||
decimalPlaces = 2
|
||||
}
|
||||
switch value {
|
||||
case Int(minimumValue): textField.text = "MIN"
|
||||
case Int(maximumValue): textField.text = "MAX"
|
||||
default: textField.text = "\(value)"
|
||||
|
||||
let newValue = _value.roundedDown(toPlaces: decimalPlaces)
|
||||
|
||||
switch newValue {
|
||||
case minimum: textField.text = "MIN"
|
||||
case maximum: textField.text = "MAX"
|
||||
default: textField.text = String(format: "%.\(decimalPlaces)f", newValue)
|
||||
}
|
||||
}
|
||||
|
||||
func updatePointerView(in bounds: CGRect, value: Float) {
|
||||
func updatePointerView(in bounds: CGRect) {
|
||||
|
||||
let angleRange = angleDifferenceInDegree(from: startAngle.toDegree, to: endAngle.toDegree, isClockwise: isClockwise)
|
||||
let angle = isClockwise ? startAngle.toDegree + (angleRange * CGFloat(value)) : startAngle.toDegree - (angleRange * CGFloat(value))
|
||||
|
||||
@@ -24,6 +24,13 @@ extension FloatingPoint {
|
||||
let degree = self * 180 / .pi
|
||||
return degree >= 0 ? degree : 360 + degree
|
||||
}
|
||||
|
||||
public func roundedDown(toPlaces places: Int) -> Self {
|
||||
let rounded = self.rounded(.down)
|
||||
let power = Self(Int(powf(10, Float(places))))
|
||||
let decimal = ((self - rounded) * power).rounded(.down)
|
||||
return rounded + (decimal / power)
|
||||
}
|
||||
}
|
||||
|
||||
extension CGPoint {
|
||||
|
||||
@@ -44,6 +44,8 @@ class KnobView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
var shadowOpacity: Float = 0.4 {
|
||||
didSet {
|
||||
middleCircleShadow.shadowOpacity = shadowOpacity
|
||||
|
||||
@@ -35,12 +35,15 @@ it, simply add the following line to your Podfile:
|
||||
pod 'JOCircularSlider'
|
||||
```
|
||||
|
||||
JOCircularSlider is also available through [Carthage](https://github.com/Carthage/Carthage). To install it, simply add the following line to your [Cartfile](https://github.com/Carthage/Carthage/blob/master/Documentation/Artifacts.md#cartfile):
|
||||
|
||||
``` ruby
|
||||
github "ouraigua/JOCircularSlider"
|
||||
```
|
||||
|
||||
## Usage
|
||||
1. Visually:
|
||||
|
||||
```swift
|
||||
import JOCircularSlider
|
||||
```
|
||||
Drag a UIView to your storyboard, change its class of to CircularSlider and start visually customising the design to your liking.
|
||||
All the parameters are IBInspectable, so you can configure the slider straight from the attribute inspector tab, without having to write a single line of code.
|
||||
|
||||
@@ -60,28 +63,58 @@ circularSlider.isClockwise = false
|
||||
```
|
||||
These are just few of the many params you can configure for the slider.
|
||||
|
||||
The slider's `value` property is read-only.
|
||||
The slider's min/max values are IBInspectable and default to 0.0 and 100.0 respectively.
|
||||
They also support negative and positive values or a mixture of them.
|
||||
|
||||
```swift
|
||||
/**
|
||||
returns a value in the range 0.0 (minimumValue) to 1.0 (maximumValue).
|
||||
The default value of this property is 0.0.
|
||||
*/
|
||||
open private (set) var value: Float = 0
|
||||
```
|
||||
To set the slider's value use the following:
|
||||
```swift
|
||||
/**
|
||||
Set the `value`.
|
||||
- parameter newValue: if `isPercentage` then new value must be in the range 0.0 to 1.0
|
||||
- parameter isPercentage: specifies if newValue is in the range [0.0, 1.0] or not
|
||||
*/
|
||||
open func setValue(_ newValue: Float, isPercentage: Bool = false)
|
||||
@IBInspectable open var minimumValue: CGFloat = 0
|
||||
@IBInspectable open var maximumValue: CGFloat = 100
|
||||
```
|
||||
|
||||
To Control the text's appearance, use these:
|
||||
The slider's `value` property is both { get set } and it is designed to work similarly to the familiar value property of UIKit's generic UISlider.
|
||||
|
||||
```swift
|
||||
open func setTextFont(named: String, textColor: UIColor, multiplier: CGFloat)
|
||||
open func setTextShadow(color: UIColor, opacity: Float = 1, offset: CGSize = CGSize(width: 1, height: 1), radius: CGFloat = 0)
|
||||
/**
|
||||
This value will be pinned to minimumValue/maximumValue
|
||||
The default value of this property is 0.0.
|
||||
*/
|
||||
@IBInspectable open var value: CGFloat // { get set }
|
||||
|
||||
```
|
||||
|
||||
In order to get value change notifications, use the `Target-Action` pattern which is an inherent part of UIControl, like so:
|
||||
``` swift
|
||||
/**
|
||||
Add target/action for particular event.
|
||||
- parameter target: The object whose action method is called
|
||||
- parameter action: A selector identifying the action method to be called
|
||||
- parameter Event: The control-specific events for which the action method is called
|
||||
So far I've only implemented the following:
|
||||
|
||||
UIControl.Event.valueChanged
|
||||
UIControl.Event.editingDidBegin
|
||||
UIControl.Event.editingDidEnd
|
||||
*/
|
||||
circularSlider.addTarget(target: Any?, action: Selector, for: UIControl.Event)
|
||||
|
||||
```
|
||||
|
||||
To Control the appearance of the label displaying the value, use these:
|
||||
```swift
|
||||
open func setLabelFont(named: String, textColor: UIColor, multiplier: CGFloat)
|
||||
open func setLabelShadow(color: UIColor, opacity: Float = 1, offset: CGSize = CGSize(width: 1, height: 1), radius: CGFloat = 0)
|
||||
```
|
||||
|
||||
You can also specify the number of decimal places this label should show.
|
||||
The default is 0.0 unless for example the value is within [0.0, 1.0]
|
||||
```swift
|
||||
@IBInspectable open var labelDecimalPlaces: Int = 0
|
||||
```
|
||||
If you need to override the text in the label, then just implement the following function inside the selector of the `Target-Action`
|
||||
method for the event `UIControl.Event.valueChanged`
|
||||
|
||||
```swift
|
||||
open func setLabelText(_ text: String?)
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
Reference in New Issue
Block a user