Skip to content

Commit 48cc8d2

Browse files
New behind the scenes implementation.
- 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.
1 parent 489ab17 commit 48cc8d2

17 files changed

+715
-764
lines changed

M13Checkbox Demo/DemoViewController.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -438,9 +438,9 @@ class DemoViewController: UIViewController, UICollectionViewDataSource, UIPopove
438438

439439
func updateMarkType(_ sender: UISegmentedControl) {
440440
if sender.selectedSegmentIndex == 0 {
441-
checkbox?.markType = .checkmark
441+
checkbox?.setMarkType(markType: .checkmark, animated: true)
442442
} else {
443-
checkbox?.markType = .radio
443+
checkbox?.setMarkType(markType: .radio, animated: true)
444444
}
445445
}
446446

M13Checkbox.podspec

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = "M13Checkbox"
3-
s.version = "2.2.6"
3+
s.version = "3.0.0"
44
s.summary = "A beautiful, customizable, extendable, animated checkbox for iOS."
55

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

2727
s.platform = :ios, '8.0'
2828

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

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

M13Checkbox.xcodeproj/project.pbxproj

+38-28
Large diffs are not rendered by default.

Sources/M13Checkbox.swift

+42-54
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ public class M13Checkbox: UIControl {
224224

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

229229
//----------------------------
230230
// MARK: - Initalization
@@ -243,11 +243,11 @@ public class M13Checkbox: UIControl {
243243
/// The setup shared between initalizers.
244244
fileprivate func sharedSetup() {
245245
// Set up the inital state.
246-
for aLayer in manager.layersToDisplay {
246+
for aLayer in controller.layersToDisplay {
247247
layer.addSublayer(aLayer)
248248
}
249-
manager.tintColor = tintColor
250-
manager.resetLayersForState(.unchecked)
249+
controller.tintColor = tintColor
250+
controller.resetLayersForState(.unchecked)
251251

252252
let longPressGesture = M13CheckboxGestureRecognizer(target: self, action: #selector(M13Checkbox.handleLongPress(_:)))
253253
addGestureRecognizer(longPressGesture)
@@ -289,7 +289,7 @@ public class M13Checkbox: UIControl {
289289
/// The current state of the checkbox.
290290
public var checkState: CheckState {
291291
get {
292-
return manager.state
292+
return controller.state
293293
}
294294
set {
295295
setCheckState(newValue, animated: false)
@@ -307,9 +307,9 @@ public class M13Checkbox: UIControl {
307307
}
308308

309309
if animated {
310-
manager.animate(checkState, toState: newState)
310+
controller.animate(checkState, toState: newState)
311311
} else {
312-
manager.resetLayersForState(newState)
312+
controller.resetLayersForState(newState)
313313
}
314314
}
315315

@@ -339,10 +339,10 @@ public class M13Checkbox: UIControl {
339339
/// The duration of the animation that occurs when the checkbox switches states. The default is 0.3 seconds.
340340
@IBInspectable public var animationDuration: TimeInterval {
341341
get {
342-
return manager.animationGenerator.animationDuration
342+
return controller.animationGenerator.animationDuration
343343
}
344344
set {
345-
manager.animationGenerator.animationDuration = newValue
345+
controller.animationGenerator.animationDuration = newValue
346346
}
347347
}
348348

@@ -365,14 +365,10 @@ public class M13Checkbox: UIControl {
365365
newManager.secondaryTintColor = secondaryTintColor
366366
newManager.secondaryCheckmarkTintColor = secondaryCheckmarkTintColor
367367
newManager.hideBox = hideBox
368-
369-
newManager.paths.boxLineWidth = manager.paths.boxLineWidth
370-
newManager.paths.boxType = manager.paths.boxType
371-
newManager.paths.checkmarkLineWidth = manager.paths.checkmarkLineWidth
372-
newManager.paths.cornerRadius = manager.paths.cornerRadius
373-
newManager.paths.markType = manager.paths.markType
374-
375-
newManager.animationGenerator.animationDuration = manager.animationGenerator.animationDuration
368+
newManager.pathGenerator = controller.pathGenerator
369+
newManager.animationGenerator.animationDuration = controller.animationGenerator.animationDuration
370+
newManager.state = controller.state
371+
newManager.setMarkType(type: controller.markType, animated: false)
376372

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

382378
// Layout and reset
383379
newManager.resetLayersForState(checkState)
384-
manager = newManager
385-
386-
// TODO: - Add support for missing animations.
387-
if markType == .radio && stateChangeAnimation == .spiral {
388-
stateChangeAnimation = .stroke
389-
print("WARNING: The spiral animation is currently unsupported with a radio mark.")
390-
}
380+
controller = newManager
381+
391382
}
392383
}
393384

@@ -414,99 +405,96 @@ public class M13Checkbox: UIControl {
414405
/// 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.
415406
@IBInspectable public var secondaryTintColor: UIColor? {
416407
get {
417-
return manager.secondaryTintColor
408+
return controller.secondaryTintColor
418409
}
419410
set {
420-
manager.secondaryTintColor = newValue
411+
controller.secondaryTintColor = newValue
421412
}
422413
}
423414

424415
/// The color of the checkmark when it is displayed against a filled background.
425416
@IBInspectable public var secondaryCheckmarkTintColor: UIColor? {
426417
get {
427-
return manager.secondaryCheckmarkTintColor
418+
return controller.secondaryCheckmarkTintColor
428419
}
429420
set {
430-
manager.secondaryCheckmarkTintColor = newValue
421+
controller.secondaryCheckmarkTintColor = newValue
431422
}
432423
}
433424

434425
/// The stroke width of the checkmark.
435426
@IBInspectable public var checkmarkLineWidth: CGFloat {
436427
get {
437-
return manager.paths.checkmarkLineWidth
428+
return controller.pathGenerator.checkmarkLineWidth
438429
}
439430
set {
440-
manager.paths.checkmarkLineWidth = newValue
441-
manager.resetLayersForState(checkState)
431+
controller.pathGenerator.checkmarkLineWidth = newValue
432+
controller.resetLayersForState(checkState)
442433
}
443434
}
444435

445-
// The type of mark to display.
436+
/// The type of mark to display.
446437
@IBInspectable public var markType: MarkType {
447438
get {
448-
return manager.paths.markType
439+
return controller.markType
449440
}
450441
set {
451-
manager.paths.markType = newValue
452-
453-
// TODO: - Add support for missing animations.
454-
if markType == .radio && stateChangeAnimation == .spiral {
455-
manager.paths.markType = .checkmark
456-
print("WARNING: The spiral animation is currently unsupported with a radio mark.")
457-
}
458-
459-
manager.resetLayersForState(checkState)
442+
controller.markType = newValue
460443
setNeedsLayout()
461444
}
462445
}
463446

447+
/// Set the mark type with the option of animating the change.
448+
public func setMarkType(markType: MarkType, animated: Bool) {
449+
controller.setMarkType(type: markType, animated: animated)
450+
}
451+
464452
/// The stroke width of the box.
465453
@IBInspectable public var boxLineWidth: CGFloat {
466454
get {
467-
return manager.paths.boxLineWidth
455+
return controller.pathGenerator.boxLineWidth
468456
}
469457
set {
470-
manager.paths.boxLineWidth = newValue
471-
manager.resetLayersForState(checkState)
458+
controller.pathGenerator.boxLineWidth = newValue
459+
controller.resetLayersForState(checkState)
472460
}
473461
}
474462

475463
/// The corner radius of the box if the box type is square.
476464
@IBInspectable public var cornerRadius: CGFloat {
477465
get {
478-
return manager.paths.cornerRadius
466+
return controller.pathGenerator.cornerRadius
479467
}
480468
set {
481-
manager.paths.cornerRadius = newValue
469+
controller.pathGenerator.cornerRadius = newValue
482470
setNeedsLayout()
483471
}
484472
}
485473

486474
/// The shape of the checkbox.
487475
public var boxType: BoxType {
488476
get {
489-
return manager.paths.boxType
477+
return controller.pathGenerator.boxType
490478
}
491479
set {
492-
manager.paths.boxType = newValue
480+
controller.pathGenerator.boxType = newValue
493481
setNeedsLayout()
494482
}
495483
}
496484

497485
/// Wether or not to hide the checkbox.
498486
@IBInspectable public var hideBox: Bool {
499487
get {
500-
return manager.hideBox
488+
return controller.hideBox
501489
}
502490
set {
503-
manager.hideBox = newValue
491+
controller.hideBox = newValue
504492
}
505493
}
506494

507495
public override func tintColorDidChange() {
508496
super.tintColorDidChange()
509-
manager.tintColor = tintColor
497+
controller.tintColor = tintColor
510498
}
511499

512500
//----------------------------
@@ -516,8 +504,8 @@ public class M13Checkbox: UIControl {
516504
public override func layoutSubviews() {
517505
super.layoutSubviews()
518506
// Update size
519-
manager.paths.size = min(frame.size.width, frame.size.height)
507+
controller.pathGenerator.size = min(frame.size.width, frame.size.height)
520508
// Layout
521-
manager.layoutLayers()
509+
controller.layoutLayers()
522510
}
523511
}

Sources/M13CheckboxController.swift

+46-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ internal class M13CheckboxController {
2020
//----------------------------
2121

2222
/// The path presets for the manager.
23-
var paths: M13CheckboxPathPresets = M13CheckboxPathPresets()
23+
var pathGenerator: M13CheckboxPathGenerator = M13CheckboxCheckPathGenerator()
2424

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

47+
// The type of mark to display.
48+
var markType: M13Checkbox.MarkType = .checkmark {
49+
willSet {
50+
if markType == newValue {
51+
return
52+
}
53+
setMarkType(type: markType, animated: false)
54+
}
55+
}
56+
57+
func setMarkType(type: M13Checkbox.MarkType, animated: Bool) {
58+
var newPathGenerator: M13CheckboxPathGenerator? = nil
59+
if type != markType {
60+
switch type {
61+
case .checkmark:
62+
newPathGenerator = M13CheckboxCheckPathGenerator()
63+
break
64+
case .radio:
65+
newPathGenerator = M13CheckboxRadioPathGenerator()
66+
break
67+
}
68+
69+
newPathGenerator?.boxLineWidth = pathGenerator.boxLineWidth
70+
newPathGenerator?.boxType = pathGenerator.boxType
71+
newPathGenerator?.checkmarkLineWidth = pathGenerator.checkmarkLineWidth
72+
newPathGenerator?.cornerRadius = pathGenerator.cornerRadius
73+
newPathGenerator?.size = pathGenerator.size
74+
75+
// Animate the change.
76+
if state != .unchecked && animated {
77+
let previousState = state
78+
animate(state, toState: .unchecked, completion: { [weak self] in
79+
self?.pathGenerator = newPathGenerator!
80+
self?.animate(.unchecked, toState: previousState)
81+
})
82+
} else {
83+
pathGenerator = newPathGenerator!
84+
resetLayersForState(state)
85+
}
86+
87+
markType = type
88+
}
89+
}
90+
4791
//----------------------------
4892
// MARK: - Layers
4993
//----------------------------
@@ -62,7 +106,7 @@ internal class M13CheckboxController {
62106
- parameter fromState: The previous state of the checkbox.
63107
- parameter toState: The new state of the checkbox.
64108
*/
65-
func animate(_ fromState: M13Checkbox.CheckState, toState: M13Checkbox.CheckState) {
109+
func animate(_ fromState: M13Checkbox.CheckState, toState: M13Checkbox.CheckState, completion: (() -> Void)? = nil) {
66110
state = toState
67111
}
68112

0 commit comments

Comments
 (0)