From aea4a1a660cfb4108995bdc678844f92ec5f9a1c Mon Sep 17 00:00:00 2001 From: Ali madhoun Date: Sun, 23 Apr 2023 22:54:42 +0200 Subject: [PATCH 1/3] - Added onProgressChanged, onToucEnd and onTouchesBegan and track Slider's progress - Support drawing a progress slider with more than two colors gradient - Draw Disk color on progress changed - Added Extra Label into the Slider for better UX - Added a stroke color into the slider's knob - Fix minor issue related with sliding into the max progress --- Example/Pods/Pods.xcodeproj/project.pbxproj | 12 +- .../project.pbxproj | 14 +- .../Base.lproj/Main.storyboard | 32 +--- Example/SVCircularSlider/SpeedSlider.swift | 38 ++++ Example/SVCircularSlider/ViewController.swift | 19 ++ Source/CircularSlider.swift | 167 ++++++++++++++---- 6 files changed, 208 insertions(+), 74 deletions(-) create mode 100644 Example/SVCircularSlider/SpeedSlider.swift diff --git a/Example/Pods/Pods.xcodeproj/project.pbxproj b/Example/Pods/Pods.xcodeproj/project.pbxproj index 9bf8b91..7c7fdce 100644 --- a/Example/Pods/Pods.xcodeproj/project.pbxproj +++ b/Example/Pods/Pods.xcodeproj/project.pbxproj @@ -436,7 +436,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "Target Support Files/Pods-SVCircularSlider_Tests/Pods-SVCircularSlider_Tests-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACH_O_TYPE = staticlib; MODULEMAP_FILE = "Target Support Files/Pods-SVCircularSlider_Tests/Pods-SVCircularSlider_Tests.modulemap"; @@ -470,7 +470,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "Target Support Files/Pods-SVCircularSlider_Example/Pods-SVCircularSlider_Example-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACH_O_TYPE = staticlib; MODULEMAP_FILE = "Target Support Files/Pods-SVCircularSlider_Example/Pods-SVCircularSlider_Example.modulemap"; @@ -504,7 +504,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "Target Support Files/Pods-SVCircularSlider_Tests/Pods-SVCircularSlider_Tests-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACH_O_TYPE = staticlib; MODULEMAP_FILE = "Target Support Files/Pods-SVCircularSlider_Tests/Pods-SVCircularSlider_Tests.modulemap"; @@ -539,7 +539,7 @@ GCC_PREFIX_HEADER = "Target Support Files/SVCircularSlider/SVCircularSlider-prefix.pch"; INFOPLIST_FILE = "Target Support Files/SVCircularSlider/SVCircularSlider-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/SVCircularSlider/SVCircularSlider.modulemap"; PRODUCT_MODULE_NAME = SVCircularSlider; @@ -573,7 +573,7 @@ GCC_PREFIX_HEADER = "Target Support Files/SVCircularSlider/SVCircularSlider-prefix.pch"; INFOPLIST_FILE = "Target Support Files/SVCircularSlider/SVCircularSlider-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MODULEMAP_FILE = "Target Support Files/SVCircularSlider/SVCircularSlider.modulemap"; PRODUCT_MODULE_NAME = SVCircularSlider; @@ -728,7 +728,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "Target Support Files/Pods-SVCircularSlider_Example/Pods-SVCircularSlider_Example-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACH_O_TYPE = staticlib; MODULEMAP_FILE = "Target Support Files/Pods-SVCircularSlider_Example/Pods-SVCircularSlider_Example.modulemap"; diff --git a/Example/SVCircularSlider.xcodeproj/project.pbxproj b/Example/SVCircularSlider.xcodeproj/project.pbxproj index e208dcf..f6bff92 100644 --- a/Example/SVCircularSlider.xcodeproj/project.pbxproj +++ b/Example/SVCircularSlider.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 41DCAFF529F5C41E00168694 /* SpeedSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41DCAFF429F5C41E00168694 /* SpeedSlider.swift */; }; 5B4590B0626C6A6EAF3F669A /* Pods_SVCircularSlider_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B0E23BC87D64C110AA574D53 /* Pods_SVCircularSlider_Example.framework */; }; 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; }; @@ -30,6 +31,7 @@ /* Begin PBXFileReference section */ 1381BBB639A14A2C6FA43ACE /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 2828F6F8215C4C40B05821FB /* SVCircularSlider.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = SVCircularSlider.podspec; path = ../SVCircularSlider.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 41DCAFF429F5C41E00168694 /* SpeedSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpeedSlider.swift; sourceTree = ""; }; 49B247F4AD919C7937487656 /* Pods_SVCircularSlider_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SVCircularSlider_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 607FACD01AFB9204008FA782 /* SVCircularSlider_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SVCircularSlider_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -99,6 +101,7 @@ 607FACDC1AFB9204008FA782 /* Images.xcassets */, 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, 607FACD31AFB9204008FA782 /* Supporting Files */, + 41DCAFF429F5C41E00168694 /* SpeedSlider.swift */, ); name = "Example for SVCircularSlider"; path = SVCircularSlider; @@ -334,6 +337,7 @@ files = ( 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */, 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, + 41DCAFF529F5C41E00168694 /* SpeedSlider.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -420,7 +424,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -466,7 +470,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -481,7 +485,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = K47F9Z9P4D; INFOPLIST_FILE = SVCircularSlider/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MODULE_NAME = ExampleApp; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; @@ -498,7 +502,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = K47F9Z9P4D; INFOPLIST_FILE = SVCircularSlider/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MODULE_NAME = ExampleApp; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; @@ -521,6 +525,7 @@ "$(inherited)", ); INFOPLIST_FILE = Tests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -539,6 +544,7 @@ "$(inherited)", ); INFOPLIST_FILE = Tests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Example/SVCircularSlider/Base.lproj/Main.storyboard b/Example/SVCircularSlider/Base.lproj/Main.storyboard index b6246b5..8402b2a 100644 --- a/Example/SVCircularSlider/Base.lproj/Main.storyboard +++ b/Example/SVCircularSlider/Base.lproj/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -18,35 +18,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Example/SVCircularSlider/SpeedSlider.swift b/Example/SVCircularSlider/SpeedSlider.swift new file mode 100644 index 0000000..4fc0f2f --- /dev/null +++ b/Example/SVCircularSlider/SpeedSlider.swift @@ -0,0 +1,38 @@ +// +// SpeedSlider.swift +// SVCircularSlider_Example +// +// Created by ali on 23/04/2023. +// Copyright © 2023 CocoaPods. All rights reserved. +// + +import SVCircularSlider + +class SpeedSlider: CircularSlider { + + override init(frame: CGRect) { + super.init(frame: frame) + + progressColors = [ + .white.withAlphaComponent(0.1), + .white.withAlphaComponent(0.6), + .blue.withAlphaComponent(0.9), + ] + + counterColor = .blue + diskColor = .blue.withAlphaComponent(0.1) + titleFont = .systemFont(ofSize: 14) + progressFont = .systemFont(ofSize: 12) + titleColor = .black + progressColor = .black + knobColor = .white + backgroundColor = .clear + knobBorderColor = .blue + knobBorderWidth = 3 + sliderTitle = "Speed" + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/Example/SVCircularSlider/ViewController.swift b/Example/SVCircularSlider/ViewController.swift index a80c92c..587dd48 100644 --- a/Example/SVCircularSlider/ViewController.swift +++ b/Example/SVCircularSlider/ViewController.swift @@ -19,6 +19,25 @@ class ViewController: UIViewController { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + let slider = SpeedSlider(frame: .init(x: 100, y: 100, width: 200, height: 200)) + self.view.addSubview(slider) + + slider.onProgressChanged = { progress in + print("Progress changged: \(progress)") + } + + slider.onToucEnd = { + print("Slider onToucEnd") + } + + slider.onTouchesBegan = { + print("Slider onTouchesBegan") + } + } } diff --git a/Source/CircularSlider.swift b/Source/CircularSlider.swift index 5c5ec8b..64ef50b 100644 --- a/Source/CircularSlider.swift +++ b/Source/CircularSlider.swift @@ -9,33 +9,56 @@ import Foundation import UIKit // PRCENTAGE VIEW -@available(iOS 9.1, *) -@IBDesignable -public class CircularSlider: UIView { +open class CircularSlider: UIView { // CONSTANTS FOR DRAWING private struct Constants { static let max : CGFloat = 1.0 static let lineWidth: CGFloat = 5.0 - static let arcWidth: CGFloat = 30 + static let arcWidth: CGFloat = 10 static var halfOfLineWidth: CGFloat { return lineWidth / 2 } + static let arcPadding: CGFloat = 15 } + // PROGRESS: Indicates the percentage with a number between 0 and 1 - @IBInspectable public var progress: CGFloat = 0.65 { - didSet { - if !(0...1).contains(progress) { - // clamp: if progress is over 1 or less than 0 give it a value between them - progress = max(0, min(1, progress)) + @IBInspectable public var progress: CGFloat = 0.5 { + didSet { + if !(0...1).contains(progress) { + // clamp: if progress is over 1 or less than 0 give it a value between them + progress = max(0, min(1, progress)) + } + setNeedsDisplay() } - setNeedsDisplay() - } } + + public override init(frame: CGRect) { + percentageLabel = UILabel() + titleLabel = UILabel() + super.init(frame: frame) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + // INSPECTABLE VARIABLES TO COLOR EACH ITEM FROM STORYBOARD - @IBInspectable public var firstFillColor: UIColor = UIColor.red { didSet { setNeedsDisplay() } } - @IBInspectable public var secondFillColor: UIColor = UIColor.yellow { didSet { setNeedsDisplay() } } + @IBInspectable public var progressColors: [UIColor] = [.blue.withAlphaComponent(0.5), + .white.withAlphaComponent(0.6), + .blue.withAlphaComponent(0.9)] { didSet { setNeedsDisplay() } } + @IBInspectable public var counterColor: UIColor = UIColor.orange { didSet { setNeedsDisplay() } } @IBInspectable public var knobColor: UIColor = .gray { didSet { setNeedsDisplay() } } + @IBInspectable public var knobBorderColor: UIColor = .gray { didSet { setNeedsDisplay() } } + @IBInspectable public var diskColor: UIColor = .white { didSet { setNeedsDisplay() } } + @IBInspectable public var knobBorderWidth: CGFloat = 0 { didSet { setNeedsDisplay() } } + @IBInspectable public var sliderTitle: String = "" { didSet { setNeedsDisplay() } } + @IBInspectable public var titleFont: UIFont = .systemFont(ofSize: 12) { didSet { setNeedsDisplay() } } + @IBInspectable public var progressFont: UIFont = .systemFont(ofSize: 12) { didSet { setNeedsDisplay() } } + @IBInspectable public var progressColor: UIColor = .black { didSet { setNeedsDisplay() } } + @IBInspectable public var titleColor: UIColor = .black { didSet { setNeedsDisplay() } } + // Label init - let percentageLabel = UILabel(frame: CGRect(x: 150, y: 150, width: 200, height: 40)) + public let percentageLabel: UILabel + let titleLabel: UILabel // position to be set everytime the progress is updated public fileprivate(set) var pointerPosition: CGPoint = CGPoint() // boolean which chooses if the knob can be dragged or not @@ -43,6 +66,10 @@ public class CircularSlider: UIView { // variable that stores the lenght of the arc based on the last touch var oldLength : CGFloat = 300 + public var onProgressChanged: (_ progress: CGFloat) -> Void = { progress in} + public var onTouchesBegan: () -> Void = { } + public var onToucEnd: () -> Void = { } + // TOUCHES BEGAN: if the touch is near thw pointer let it be possible to be dragged override public func touchesBegan(_ touches: Set, with event: UIEvent?) { if let firstTouch = touches.first { @@ -51,12 +78,21 @@ public class CircularSlider: UIView { // distance of touch from pointer let xDist = CGFloat(firstTouch.preciseLocation(in: hitView).x - pointerPosition.x) let yDist = CGFloat(firstTouch.preciseLocation(in: hitView).y - pointerPosition.y) - let distance = CGFloat(sqrt((xDist * xDist) + (yDist * yDist))) + let distance = CGFloat(sqrt((xDist * xDist) + (yDist * yDist))) - 20 canDrag = true - guard distance < 30 else { return canDrag = false } + guard distance < Constants.arcWidth else { + return canDrag = false + } + + self.onTouchesBegan() } } } + + public override func touchesEnded(_ touches: Set, with event: UIEvent?) { + self.onToucEnd() + } + // TOUCHES MOVED: If touchesBegan says that the pointer can be dragged let it be dregged by the touch of the user public override func touchesMoved(_ touches: Set, with event: UIEvent?) { if let firstTouch = touches.first { @@ -94,15 +130,34 @@ public class CircularSlider: UIView { var newArcLength = CGFloat(theta) * radius // CHECK CONDITIONS OF THE POINTER'S POSITION - if 480.0 ... 550.0 ~= newArcLength { newArcLength = 480 } - else if 550.0 ... 630.0 ~= newArcLength { newArcLength = 0 } - if oldLength == 480 && 0 ... 465 ~= newArcLength { newArcLength = 480 } - else if oldLength == 0 && 15 ... 480 ~= newArcLength { newArcLength = 0 } + if 480.0 ... 550.0 ~= newArcLength { + newArcLength = 480 + + } + else if 550.0 ... 630.0 ~= newArcLength { + newArcLength = 0 + + } + if oldLength == 480 && 0 ... 465 ~= newArcLength { + newArcLength = 480 + + } + else if oldLength == 0 && 15 ... 480 ~= newArcLength { + newArcLength = 0 + } + + oldLength = newArcLength // PERCENTAGE TO BE ASSIGNED TO THE PROGRES VAR let newPercentage = newArcLength/arcLength + if (CGFloat(newPercentage) - 1) >= 0.15 { + oldLength = 300 + return + } + progress = CGFloat(newPercentage) + } } } @@ -110,11 +165,27 @@ public class CircularSlider: UIView { // LABEL public func label() { // DRAW THE PERCENTAGE LABEL - percentageLabel.translatesAutoresizingMaskIntoConstraints = true - percentageLabel.font = percentageLabel.font.withSize(28) - percentageLabel.center = CGPoint(x: 115, y: 115) - percentageLabel.textAlignment = .center - self.addSubview(percentageLabel) +// percentageLabel.translatesAutoresizingMaskIntoConstraints = true + if percentageLabel.superview == nil { + self.addSubview(percentageLabel) + self.percentageLabel.textColor = progressColor + self.percentageLabel.font = progressFont + percentageLabel.translatesAutoresizingMaskIntoConstraints = false + percentageLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true + percentageLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true + } + + if titleLabel.superview == nil { + self.addSubview(titleLabel) + + self.titleLabel.translatesAutoresizingMaskIntoConstraints = false + self.titleLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true + self.titleLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -10).isActive = true + self.titleLabel.text = sliderTitle + self.titleLabel.textColor = titleColor + self.titleLabel.font = titleFont + } + let percentage = Int(Double(progress * 100)) percentageLabel.text = "\(percentage)%" } @@ -123,11 +194,14 @@ public class CircularSlider: UIView { // call label function label() + // notify the delegate + self.onProgressChanged(progress) + //DRAW THE OUTLINE // 1 Define the center point you’ll rotate the arc around. let center = CGPoint(x: bounds.width / 2, y: bounds.height / 2) // 2 Calculate the radius based on the maximum dimension of the view. - let radius = max(bounds.width, bounds.height) + let radius = max(bounds.width, bounds.height) - 10 // 3 Define the start and end angles for the arc. let startAngle: CGFloat = 3 * .pi / 4 let endAngle: CGFloat = .pi / 4 @@ -156,44 +230,69 @@ public class CircularSlider: UIView { // radius is the same as main path let insidePath = UIBezierPath( arcCenter: center, + radius: radius/2 - Constants.arcWidth / 2, + startAngle: startAngle, + endAngle: outlineEndAngle, + clockwise: true) + + let insidePath2 = UIBezierPath( + arcCenter: center, radius: radius/2 - Constants.arcWidth/2, startAngle: startAngle, endAngle: outlineEndAngle, clockwise: true) + //outlineColor.setStroke() insidePath.lineCapStyle = .round - insidePath.lineWidth = CGFloat(30.0) + insidePath.lineWidth = CGFloat(Constants.arcWidth) insidePath.stroke() + diskColor.setFill() + insidePath2.addLine(to: center) + insidePath2.fill() + // GRADIENT: create a context to clip to the insidePath // graphicContext let c = UIGraphicsGetCurrentContext()! let clipPath: CGPath = insidePath.cgPath c.saveGState() - c.setLineWidth(30.0) + c.setLineWidth(Constants.arcWidth) c.addPath(clipPath) c.setLineCap(.round) c.replacePathWithStrokedPath() c.clip() // Draw gradient - let colors = [firstFillColor.cgColor, secondFillColor.cgColor] - let offsets = [ CGFloat(0.0), CGFloat(1.0) ] - let grad = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: colors as CFArray, locations: offsets) + + let colors = progressColors.map { $0.cgColor } + + let grad = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: colors as CFArray, locations: nil) + let start = CGPoint(x: 0, y: 0) - let end = CGPoint(x: 230, y: 230) + let end = CGPoint(x: self.frame.maxX, y: self.frame.maxY) c.drawLinearGradient(grad!, start: start, end: end, options: []) c.restoreGState() // result - let result = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() + // DRAW THE POINTER - let pointerRect = CGRect(x: insidePath.currentPoint.x - Constants.arcWidth / 2, y: insidePath.currentPoint.y - Constants.arcWidth / 2, width: Constants.arcWidth, height: Constants.arcWidth) + let pointerRect = CGRect(x: insidePath.currentPoint.x - 16 / 2, y: insidePath.currentPoint.y - 16 / 2, width: 16, height: 16) let pointer = UIBezierPath(ovalIn: pointerRect) knobColor.setFill() pointer.fill() insidePath.append(pointer) + let pointerRectBorder = CGRect(x: insidePath.currentPoint.x - 16 / 2, y: insidePath.currentPoint.y - 16 / 2, width: 16, height: 16) + let pointerBorder = UIBezierPath(ovalIn: pointerRectBorder) + + knobBorderColor.setStroke() +// pointerBorder.lineWidth = 10 + + pointer.lineWidth = knobBorderWidth + pointer.stroke() + insidePath.append(pointerBorder) + // SET THE POSITION pointerPosition = CGPoint(x: insidePath.currentPoint.x - Constants.arcWidth / 2, y: insidePath.currentPoint.y - Constants.arcWidth / 2) } From 16b3fac68e87194e47d2357cd8c8b19187a8701b Mon Sep 17 00:00:00 2001 From: Ali madhoun Date: Mon, 24 Apr 2023 00:27:33 +0200 Subject: [PATCH 2/3] Show counterColor for empty progress slider area --- Example/SVCircularSlider/SpeedSlider.swift | 8 ++++---- Source/CircularSlider.swift | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Example/SVCircularSlider/SpeedSlider.swift b/Example/SVCircularSlider/SpeedSlider.swift index 4fc0f2f..6d8a5cc 100644 --- a/Example/SVCircularSlider/SpeedSlider.swift +++ b/Example/SVCircularSlider/SpeedSlider.swift @@ -14,12 +14,12 @@ class SpeedSlider: CircularSlider { super.init(frame: frame) progressColors = [ - .white.withAlphaComponent(0.1), - .white.withAlphaComponent(0.6), - .blue.withAlphaComponent(0.9), + .blue, + .green, + .red, ] - counterColor = .blue + counterColor = .gray diskColor = .blue.withAlphaComponent(0.1) titleFont = .systemFont(ofSize: 14) progressFont = .systemFont(ofSize: 12) diff --git a/Source/CircularSlider.swift b/Source/CircularSlider.swift index 64ef50b..72be359 100644 --- a/Source/CircularSlider.swift +++ b/Source/CircularSlider.swift @@ -89,7 +89,7 @@ open class CircularSlider: UIView { } } - public override func touchesEnded(_ touches: Set, with event: UIEvent?) { + open override func touchesEnded(_ touches: Set, with event: UIEvent?) { self.onToucEnd() } @@ -266,7 +266,6 @@ open class CircularSlider: UIView { let colors = progressColors.map { $0.cgColor } let grad = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: colors as CFArray, locations: nil) - let start = CGPoint(x: 0, y: 0) let end = CGPoint(x: self.frame.maxX, y: self.frame.maxY) c.drawLinearGradient(grad!, start: start, end: end, options: []) From 778711c14e7209769e7f654237e1443d49993130 Mon Sep 17 00:00:00 2001 From: Ali madhoun Date: Mon, 24 Apr 2023 00:28:12 +0200 Subject: [PATCH 3/3] Updated Example slider gradient colors --- Example/SVCircularSlider/SpeedSlider.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Example/SVCircularSlider/SpeedSlider.swift b/Example/SVCircularSlider/SpeedSlider.swift index 6d8a5cc..5ec70d9 100644 --- a/Example/SVCircularSlider/SpeedSlider.swift +++ b/Example/SVCircularSlider/SpeedSlider.swift @@ -14,9 +14,9 @@ class SpeedSlider: CircularSlider { super.init(frame: frame) progressColors = [ - .blue, - .green, .red, + .green, + .blue, ] counterColor = .gray