Skip to content

Commit

Permalink
added handle rotation feature and minor improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
ThunderStruct committed Jun 19, 2018
1 parent 9b81fc5 commit 1c6b899
Show file tree
Hide file tree
Showing 16 changed files with 205 additions and 18 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ The changelog for `MSCircularSlider`. Summarized release notes can be found in t

------------------------

## 1.2.2 - 19-06-2018
#### Added
- An option to rotate the handle image to always point outwards

#### Changed
- Minor structural improvement

## 1.2.1 - 09-02-2018
#### Fixed
- A setter conflict occuring on earlier Swift versions
Expand Down
4 changes: 2 additions & 2 deletions MSCircularSlider.podspec
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
Pod::Spec.new do |s|
s.name = 'MSCircularSlider'
s.version = '1.2.1'
s.version = '1.2.2'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.authors = { 'ThunderStruct' => '[email protected]' }
s.summary = 'A full-featured circular slider for iOS applications'
s.homepage = 'https://github.com/ThunderStruct/MSCircularSlider'

# Source Info
s.platform = :ios, '9.3'
s.source = { :git => 'https://github.com/ThunderStruct/MSCircularSlider.git', :branch => "master", :tag => "1.2.1" }
s.source = { :git => 'https://github.com/ThunderStruct/MSCircularSlider.git', :branch => "master", :tag => "1.2.2" }
s.source_files = 'MSCircularSlider/*.{swift}'

s.requires_arc = true
Expand Down
9 changes: 9 additions & 0 deletions MSCircularSlider/MSCircularSlider+IB.swift
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,15 @@ extension MSCircularSlider {
}
}

@IBInspectable public var _handleRotatable: Bool {
get {
return handleRotatable
}
set {
handleRotatable = newValue
}
}

//================================================================================
// LABELS PROPERTIES
//================================================================================
Expand Down
16 changes: 13 additions & 3 deletions MSCircularSlider/MSCircularSlider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,16 @@ public class MSCircularSlider: UIControl {
}
}

/** Specifies whether or not the handle should rotate to always point outwards - *default: false* */
public var handleRotatable: Bool {
set {
handle.isRotatable = newValue
}
get {
return handle.isRotatable
}
}

/** The calculated handle's diameter based on its type */
public var handleDiameter: CGFloat {
return handle.diameter
Expand Down Expand Up @@ -416,9 +426,10 @@ public class MSCircularSlider: UIControl {
drawLabels(ctx: ctx!)

// Rotate slider
self.transform = getRotationalTransform()
let rotationalTransform = getRotationalTransform()
self.transform = rotationalTransform
for view in subviews { // cancel rotation on all subviews added by the user
view.transform = getRotationalTransform().inverted()
view.transform = rotationalTransform.inverted()
}

}
Expand Down Expand Up @@ -832,7 +843,6 @@ public class MSCircularSlider: UIControl {
return transform
}
else {

if let rotation = self.rotationAngle {
return CGAffineTransform.identity.rotated(by: CGFloat(toRad(Double(rotation))))
}
Expand Down
53 changes: 51 additions & 2 deletions MSCircularSlider/MSCircularSliderHandle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ public class MSCircularSliderHandle: CALayer {
}
}

/** Specifies whether or not the handle should rotate to always point outwards - *default: false* */
internal var isRotatable: Bool = false {
didSet {
setNeedsDisplay()
}
}

/** The handle's color - *default: .darkGray* */
internal var color: UIColor = .darkGray {
didSet {
Expand Down Expand Up @@ -123,8 +130,13 @@ public class MSCircularSliderHandle: CALayer {
y: center().y - diameter * 0.5,
width: diameter,
height: diameter)
image?.draw(in: frame)

if isRotatable {
let rotatedImg = imageRotated(img: image!, byDegrees: angle)
rotatedImg.draw(in: frame)
}
else {
image?.draw(in: frame)
}
}
else if handleType == .doubleCircle {
calculatedHandleColor.withAlphaComponent(isHighlightable && isPressed ? 0.9 : 1.0).set()
Expand Down Expand Up @@ -159,4 +171,41 @@ public class MSCircularSliderHandle: CALayer {
return point.x >= center().x - handleRadius && point.x <= center().x + handleRadius && point.y >= center().y - handleRadius && point.y <= center().y + handleRadius
}

/** Converts degrees to radians */
private func toRad(_ degrees: Double) -> Double {
return ((Double.pi * degrees) / 180.0)
}

/** Converts radians to degrees */
private func toDeg(_ radians: Double) -> Double {
return ((180.0 * radians) / Double.pi)
}

/** Rotates a given image by the specified degrees */
private func imageRotated(img: UIImage, byDegrees degrees: CGFloat) -> UIImage {

// calculate the size of the rotated view's containing box for our drawing space
let rotatedViewBox = UIView(frame: CGRect(origin: CGPoint.zero, size: img.size))
let t = CGAffineTransform(rotationAngle: CGFloat(toRad(Double(degrees))))
rotatedViewBox.transform = t
let rotatedSize = rotatedViewBox.frame.size

// Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize)
let bitmap = UIGraphicsGetCurrentContext()

// Move the origin to the middle of the image so we will rotate and scale around the center.
bitmap?.translateBy(x: rotatedSize.width / 2.0, y: rotatedSize.height / 2.0)

// // Rotate the image context
bitmap?.rotate(by: CGFloat(toRad(Double(degrees))));

// Now, draw the rotated/scaled image into the context
bitmap?.draw(img.cgImage!, in: CGRect(x: -img.size.width / 2, y: -img.size.height / 2, width: img.size.width, height: img.size.height))

let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

return newImage!
}
}
8 changes: 8 additions & 0 deletions MSCircularSlider/MSDoubleHandleCircularSlider+IB.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,13 @@ extension MSDoubleHandleCircularSlider {
}
}

@IBInspectable public var _secondHandleRotatable: Bool {
get {
return secondHandleRotatable
}
set {
secondHandleRotatable = newValue
}
}

}
12 changes: 11 additions & 1 deletion MSCircularSlider/MSDoubleHandleCircularSlider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,16 @@ public class MSDoubleHandleCircularSlider: MSCircularSlider {
}
}

/** Specifies whether or not the second handle should rotate to always point outwards - *default: false* */
public var secondHandleRotatable: Bool {
set {
secondHandle.isRotatable = newValue
}
get {
return secondHandle.isRotatable
}
}

// CALCULATED MEMBERS

/** The calculated second handle's diameter based on its type */
Expand Down Expand Up @@ -186,7 +196,7 @@ public class MSDoubleHandleCircularSlider: MSCircularSlider {
secondHandle.center = {
return self.pointOnCircleAt(angle: self.secondAngle)
}
secondHandle.angle = 60
secondHandle.angle = CGFloat(max(0, 60.0).truncatingRemainder(dividingBy: Double(maximumAngle + 1)))
}

override init(frame: CGRect) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,15 @@ extension MSCircularSlider {
}
}

@IBInspectable public var _handleRotatable: Bool {
get {
return handleRotatable
}
set {
handleRotatable = newValue
}
}

//================================================================================
// LABELS PROPERTIES
//================================================================================
Expand Down
16 changes: 13 additions & 3 deletions MSCircularSliderExample/MSCircularSlider/MSCircularSlider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,16 @@ public class MSCircularSlider: UIControl {
}
}

/** Specifies whether or not the handle should rotate to always point outwards - *default: false* */
public var handleRotatable: Bool {
set {
handle.isRotatable = newValue
}
get {
return handle.isRotatable
}
}

/** The calculated handle's diameter based on its type */
public var handleDiameter: CGFloat {
return handle.diameter
Expand Down Expand Up @@ -416,9 +426,10 @@ public class MSCircularSlider: UIControl {
drawLabels(ctx: ctx!)

// Rotate slider
self.transform = getRotationalTransform()
let rotationalTransform = getRotationalTransform()
self.transform = rotationalTransform
for view in subviews { // cancel rotation on all subviews added by the user
view.transform = getRotationalTransform().inverted()
view.transform = rotationalTransform.inverted()
}

}
Expand Down Expand Up @@ -832,7 +843,6 @@ public class MSCircularSlider: UIControl {
return transform
}
else {

if let rotation = self.rotationAngle {
return CGAffineTransform.identity.rotated(by: CGFloat(toRad(Double(rotation))))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ public class MSCircularSliderHandle: CALayer {
}
}

/** Specifies whether or not the handle should rotate to always point outwards - *default: false* */
internal var isRotatable: Bool = false {
didSet {
setNeedsDisplay()
}
}

/** The handle's color - *default: .darkGray* */
internal var color: UIColor = .darkGray {
didSet {
Expand Down Expand Up @@ -123,8 +130,13 @@ public class MSCircularSliderHandle: CALayer {
y: center().y - diameter * 0.5,
width: diameter,
height: diameter)
image?.draw(in: frame)

if isRotatable {
let rotatedImg = imageRotated(img: image!, byDegrees: angle)
rotatedImg.draw(in: frame)
}
else {
image?.draw(in: frame)
}
}
else if handleType == .doubleCircle {
calculatedHandleColor.withAlphaComponent(isHighlightable && isPressed ? 0.9 : 1.0).set()
Expand Down Expand Up @@ -159,4 +171,41 @@ public class MSCircularSliderHandle: CALayer {
return point.x >= center().x - handleRadius && point.x <= center().x + handleRadius && point.y >= center().y - handleRadius && point.y <= center().y + handleRadius
}

/** Converts degrees to radians */
private func toRad(_ degrees: Double) -> Double {
return ((Double.pi * degrees) / 180.0)
}

/** Converts radians to degrees */
private func toDeg(_ radians: Double) -> Double {
return ((180.0 * radians) / Double.pi)
}

/** Rotates a given image by the specified degrees */
private func imageRotated(img: UIImage, byDegrees degrees: CGFloat) -> UIImage {

// calculate the size of the rotated view's containing box for our drawing space
let rotatedViewBox = UIView(frame: CGRect(origin: CGPoint.zero, size: img.size))
let t = CGAffineTransform(rotationAngle: CGFloat(toRad(Double(degrees))))
rotatedViewBox.transform = t
let rotatedSize = rotatedViewBox.frame.size

// Create the bitmap context
UIGraphicsBeginImageContext(rotatedSize)
let bitmap = UIGraphicsGetCurrentContext()

// Move the origin to the middle of the image so we will rotate and scale around the center.
bitmap?.translateBy(x: rotatedSize.width / 2.0, y: rotatedSize.height / 2.0)

// // Rotate the image context
bitmap?.rotate(by: CGFloat(toRad(Double(degrees))));

// Now, draw the rotated/scaled image into the context
bitmap?.draw(img.cgImage!, in: CGRect(x: -img.size.width / 2, y: -img.size.height / 2, width: img.size.width, height: img.size.height))

let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

return newImage!
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,13 @@ extension MSDoubleHandleCircularSlider {
}
}

@IBInspectable public var _secondHandleRotatable: Bool {
get {
return secondHandleRotatable
}
set {
secondHandleRotatable = newValue
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,16 @@ public class MSDoubleHandleCircularSlider: MSCircularSlider {
}
}

/** Specifies whether or not the second handle should rotate to always point outwards - *default: false* */
public var secondHandleRotatable: Bool {
set {
secondHandle.isRotatable = newValue
}
get {
return secondHandle.isRotatable
}
}

// CALCULATED MEMBERS

/** The calculated second handle's diameter based on its type */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
D935DC021F81AE7A00E6B6EE /* MarkersLabelsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = D935DC011F81AE7A00E6B6EE /* MarkersLabelsVC.swift */; };
D935DC041F81AE8500E6B6EE /* DoubleHandleVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = D935DC031F81AE8500E6B6EE /* DoubleHandleVC.swift */; };
D935DC061F81AE8E00E6B6EE /* GradientColorsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = D935DC051F81AE8E00E6B6EE /* GradientColorsVC.swift */; };
D953E8F320251CB7007E60B8 /* Icon1024.png in Resources */ = {isa = PBXBuildFile; fileRef = D953E8F220251CB7007E60B8 /* Icon1024.png */; };
D96C34881F813B050041B50C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D96C34871F813B050041B50C /* AppDelegate.swift */; };
D96C348A1F813B050041B50C /* ExamplesMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = D96C34891F813B050041B50C /* ExamplesMenu.swift */; };
D96C348D1F813B050041B50C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D96C348B1F813B050041B50C /* Main.storyboard */; };
Expand All @@ -40,7 +39,6 @@
D935DC011F81AE7A00E6B6EE /* MarkersLabelsVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkersLabelsVC.swift; sourceTree = "<group>"; };
D935DC031F81AE8500E6B6EE /* DoubleHandleVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DoubleHandleVC.swift; sourceTree = "<group>"; };
D935DC051F81AE8E00E6B6EE /* GradientColorsVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GradientColorsVC.swift; sourceTree = "<group>"; };
D953E8F220251CB7007E60B8 /* Icon1024.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Icon1024.png; path = "../../../../../../../Desktop/Communicate Misc/Icon1024.png"; sourceTree = "<group>"; };
D96C34841F813B050041B50C /* MSCircularSliderExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MSCircularSliderExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
D96C34871F813B050041B50C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
D96C34891F813B050041B50C /* ExamplesMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExamplesMenu.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -88,7 +86,6 @@
D935DC031F81AE8500E6B6EE /* DoubleHandleVC.swift */,
D935DC051F81AE8E00E6B6EE /* GradientColorsVC.swift */,
D9254D221F852E71006F7A81 /* ColorPickerView.swift */,
D953E8F220251CB7007E60B8 /* Icon1024.png */,
D96C348B1F813B050041B50C /* Main.storyboard */,
D96C348E1F813B060041B50C /* Assets.xcassets */,
D96C34901F813B060041B50C /* LaunchScreen.storyboard */,
Expand Down Expand Up @@ -174,7 +171,6 @@
D96C34921F813B060041B50C /* LaunchScreen.storyboard in Resources */,
D96C348F1F813B060041B50C /* Assets.xcassets in Resources */,
D96C348D1F813B050041B50C /* Main.storyboard in Resources */,
D953E8F320251CB7007E60B8 /* Icon1024.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
Binary file not shown.
Loading

0 comments on commit 1c6b899

Please sign in to comment.