Skip to content

Commit

Permalink
New behind the scenes implementation.
Browse files Browse the repository at this point in the history
- The controllers and animation/path generation has been upgraded to
allow for the addition of new marks.
- Changing between marks can now be animated.
- Adjusted mixed states for radio to remove animation glitches.
  • Loading branch information
brandon-mcquilkin-kr committed Oct 6, 2016
1 parent 489ab17 commit 48cc8d2
Show file tree
Hide file tree
Showing 17 changed files with 715 additions and 764 deletions.
4 changes: 2 additions & 2 deletions M13Checkbox Demo/DemoViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -438,9 +438,9 @@ class DemoViewController: UIViewController, UICollectionViewDataSource, UIPopove

func updateMarkType(_ sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0 {
checkbox?.markType = .checkmark
checkbox?.setMarkType(markType: .checkmark, animated: true)
} else {
checkbox?.markType = .radio
checkbox?.setMarkType(markType: .radio, animated: true)
}
}

Expand Down
4 changes: 2 additions & 2 deletions M13Checkbox.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "M13Checkbox"
s.version = "2.2.6"
s.version = "3.0.0"
s.summary = "A beautiful, customizable, extendable, animated checkbox for iOS."

s.description = <<-DESC
Expand All @@ -26,7 +26,7 @@ Pod::Spec.new do |s|

s.platform = :ios, '8.0'

s.source = { :git => "https://github.com/Marxon13/M13Checkbox.git", :tag => "2.2.6"}
s.source = { :git => "https://github.com/Marxon13/M13Checkbox.git", :tag => "#{s.version}"}

s.source_files = 'Sources/**/*'

Expand Down
66 changes: 38 additions & 28 deletions M13Checkbox.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

96 changes: 42 additions & 54 deletions Sources/M13Checkbox.swift
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ public class M13Checkbox: UIControl {

/// The manager that manages display and animations of the checkbox.
/// The default animation is a stroke.
fileprivate var manager: M13CheckboxController = M13CheckboxStrokeController()
fileprivate var controller: M13CheckboxController = M13CheckboxStrokeController()

//----------------------------
// MARK: - Initalization
Expand All @@ -243,11 +243,11 @@ public class M13Checkbox: UIControl {
/// The setup shared between initalizers.
fileprivate func sharedSetup() {
// Set up the inital state.
for aLayer in manager.layersToDisplay {
for aLayer in controller.layersToDisplay {
layer.addSublayer(aLayer)
}
manager.tintColor = tintColor
manager.resetLayersForState(.unchecked)
controller.tintColor = tintColor
controller.resetLayersForState(.unchecked)

let longPressGesture = M13CheckboxGestureRecognizer(target: self, action: #selector(M13Checkbox.handleLongPress(_:)))
addGestureRecognizer(longPressGesture)
Expand Down Expand Up @@ -289,7 +289,7 @@ public class M13Checkbox: UIControl {
/// The current state of the checkbox.
public var checkState: CheckState {
get {
return manager.state
return controller.state
}
set {
setCheckState(newValue, animated: false)
Expand All @@ -307,9 +307,9 @@ public class M13Checkbox: UIControl {
}

if animated {
manager.animate(checkState, toState: newState)
controller.animate(checkState, toState: newState)
} else {
manager.resetLayersForState(newState)
controller.resetLayersForState(newState)
}
}

Expand Down Expand Up @@ -339,10 +339,10 @@ public class M13Checkbox: UIControl {
/// The duration of the animation that occurs when the checkbox switches states. The default is 0.3 seconds.
@IBInspectable public var animationDuration: TimeInterval {
get {
return manager.animationGenerator.animationDuration
return controller.animationGenerator.animationDuration
}
set {
manager.animationGenerator.animationDuration = newValue
controller.animationGenerator.animationDuration = newValue
}
}

Expand All @@ -365,14 +365,10 @@ public class M13Checkbox: UIControl {
newManager.secondaryTintColor = secondaryTintColor
newManager.secondaryCheckmarkTintColor = secondaryCheckmarkTintColor
newManager.hideBox = hideBox

newManager.paths.boxLineWidth = manager.paths.boxLineWidth
newManager.paths.boxType = manager.paths.boxType
newManager.paths.checkmarkLineWidth = manager.paths.checkmarkLineWidth
newManager.paths.cornerRadius = manager.paths.cornerRadius
newManager.paths.markType = manager.paths.markType

newManager.animationGenerator.animationDuration = manager.animationGenerator.animationDuration
newManager.pathGenerator = controller.pathGenerator
newManager.animationGenerator.animationDuration = controller.animationGenerator.animationDuration
newManager.state = controller.state
newManager.setMarkType(type: controller.markType, animated: false)

// Set up the inital state.
for aLayer in newManager.layersToDisplay {
Expand All @@ -381,13 +377,8 @@ public class M13Checkbox: UIControl {

// Layout and reset
newManager.resetLayersForState(checkState)
manager = newManager

// TODO: - Add support for missing animations.
if markType == .radio && stateChangeAnimation == .spiral {
stateChangeAnimation = .stroke
print("WARNING: The spiral animation is currently unsupported with a radio mark.")
}
controller = newManager

}
}

Expand All @@ -414,99 +405,96 @@ public class M13Checkbox: UIControl {
/// The color of the checkbox's tint color when not in the unselected state. The tint color is is the main color used when not in the unselected state.
@IBInspectable public var secondaryTintColor: UIColor? {
get {
return manager.secondaryTintColor
return controller.secondaryTintColor
}
set {
manager.secondaryTintColor = newValue
controller.secondaryTintColor = newValue
}
}

/// The color of the checkmark when it is displayed against a filled background.
@IBInspectable public var secondaryCheckmarkTintColor: UIColor? {
get {
return manager.secondaryCheckmarkTintColor
return controller.secondaryCheckmarkTintColor
}
set {
manager.secondaryCheckmarkTintColor = newValue
controller.secondaryCheckmarkTintColor = newValue
}
}

/// The stroke width of the checkmark.
@IBInspectable public var checkmarkLineWidth: CGFloat {
get {
return manager.paths.checkmarkLineWidth
return controller.pathGenerator.checkmarkLineWidth
}
set {
manager.paths.checkmarkLineWidth = newValue
manager.resetLayersForState(checkState)
controller.pathGenerator.checkmarkLineWidth = newValue
controller.resetLayersForState(checkState)
}
}

// The type of mark to display.
/// The type of mark to display.
@IBInspectable public var markType: MarkType {
get {
return manager.paths.markType
return controller.markType
}
set {
manager.paths.markType = newValue

// TODO: - Add support for missing animations.
if markType == .radio && stateChangeAnimation == .spiral {
manager.paths.markType = .checkmark
print("WARNING: The spiral animation is currently unsupported with a radio mark.")
}

manager.resetLayersForState(checkState)
controller.markType = newValue
setNeedsLayout()
}
}

/// Set the mark type with the option of animating the change.
public func setMarkType(markType: MarkType, animated: Bool) {
controller.setMarkType(type: markType, animated: animated)
}

/// The stroke width of the box.
@IBInspectable public var boxLineWidth: CGFloat {
get {
return manager.paths.boxLineWidth
return controller.pathGenerator.boxLineWidth
}
set {
manager.paths.boxLineWidth = newValue
manager.resetLayersForState(checkState)
controller.pathGenerator.boxLineWidth = newValue
controller.resetLayersForState(checkState)
}
}

/// The corner radius of the box if the box type is square.
@IBInspectable public var cornerRadius: CGFloat {
get {
return manager.paths.cornerRadius
return controller.pathGenerator.cornerRadius
}
set {
manager.paths.cornerRadius = newValue
controller.pathGenerator.cornerRadius = newValue
setNeedsLayout()
}
}

/// The shape of the checkbox.
public var boxType: BoxType {
get {
return manager.paths.boxType
return controller.pathGenerator.boxType
}
set {
manager.paths.boxType = newValue
controller.pathGenerator.boxType = newValue
setNeedsLayout()
}
}

/// Wether or not to hide the checkbox.
@IBInspectable public var hideBox: Bool {
get {
return manager.hideBox
return controller.hideBox
}
set {
manager.hideBox = newValue
controller.hideBox = newValue
}
}

public override func tintColorDidChange() {
super.tintColorDidChange()
manager.tintColor = tintColor
controller.tintColor = tintColor
}

//----------------------------
Expand All @@ -516,8 +504,8 @@ public class M13Checkbox: UIControl {
public override func layoutSubviews() {
super.layoutSubviews()
// Update size
manager.paths.size = min(frame.size.width, frame.size.height)
controller.pathGenerator.size = min(frame.size.width, frame.size.height)
// Layout
manager.layoutLayers()
controller.layoutLayers()
}
}
48 changes: 46 additions & 2 deletions Sources/M13CheckboxController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ internal class M13CheckboxController {
//----------------------------

/// The path presets for the manager.
var paths: M13CheckboxPathPresets = M13CheckboxPathPresets()
var pathGenerator: M13CheckboxPathGenerator = M13CheckboxCheckPathGenerator()

/// The animation presets for the manager.
var animationGenerator: M13CheckboxAnimationGenerator = M13CheckboxAnimationGenerator()
Expand All @@ -44,6 +44,50 @@ internal class M13CheckboxController {
/// - Note: Subclasses should override didSet to update the layers when this value changes.
var hideBox: Bool = false

// The type of mark to display.
var markType: M13Checkbox.MarkType = .checkmark {
willSet {
if markType == newValue {
return
}
setMarkType(type: markType, animated: false)
}
}

func setMarkType(type: M13Checkbox.MarkType, animated: Bool) {
var newPathGenerator: M13CheckboxPathGenerator? = nil
if type != markType {
switch type {
case .checkmark:
newPathGenerator = M13CheckboxCheckPathGenerator()
break
case .radio:
newPathGenerator = M13CheckboxRadioPathGenerator()
break
}

newPathGenerator?.boxLineWidth = pathGenerator.boxLineWidth
newPathGenerator?.boxType = pathGenerator.boxType
newPathGenerator?.checkmarkLineWidth = pathGenerator.checkmarkLineWidth
newPathGenerator?.cornerRadius = pathGenerator.cornerRadius
newPathGenerator?.size = pathGenerator.size

// Animate the change.
if state != .unchecked && animated {
let previousState = state
animate(state, toState: .unchecked, completion: { [weak self] in
self?.pathGenerator = newPathGenerator!
self?.animate(.unchecked, toState: previousState)
})
} else {
pathGenerator = newPathGenerator!
resetLayersForState(state)
}

markType = type
}
}

//----------------------------
// MARK: - Layers
//----------------------------
Expand All @@ -62,7 +106,7 @@ internal class M13CheckboxController {
- parameter fromState: The previous state of the checkbox.
- parameter toState: The new state of the checkbox.
*/
func animate(_ fromState: M13Checkbox.CheckState, toState: M13Checkbox.CheckState) {
func animate(_ fromState: M13Checkbox.CheckState, toState: M13Checkbox.CheckState, completion: (() -> Void)? = nil) {
state = toState
}

Expand Down
Loading

0 comments on commit 48cc8d2

Please sign in to comment.