From ab0ff64677e39bdb30f5906cc847c247bc0e33fb Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 10 Dec 2019 14:38:32 -0500 Subject: [PATCH] General Working order. --- MVMCoreUI/Atoms/Views/Switch.swift | 311 ++++++++++------------------- 1 file changed, 102 insertions(+), 209 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Switch.swift b/MVMCoreUI/Atoms/Views/Switch.swift index da2c13bf..332bacc5 100644 --- a/MVMCoreUI/Atoms/Views/Switch.swift +++ b/MVMCoreUI/Atoms/Views/Switch.swift @@ -10,25 +10,25 @@ import MVMCore import UIKit /** - A custom implementation of Apple's UISwitch. - - Track: The background of the switch control. - Thumb: The circular indicator that slides on the track. + A custom implementation of Apple's UISwitch. + + Track: The background of the switch control. + Thumb: The circular indicator that slides on the track. */ @objcMembers open class Switch: Control, MVMCoreUIViewConstrainingProtocol, FormValidationFormFieldProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - + public var trackTintColor: (on: UIColor?, off: UIColor?)? = (on: .mfShamrock(), off: .black) public var thumbTintColor: (on: UIColor?, off: UIColor?)? = (on: .white, off: .white) public var disabledTintColor: (track: UIColor?, thumb: UIColor?)? = (track: .mfSilver(), thumb: .white) - private var valueShouldChange = false -// private var canChangeValue = false - private var shouldTouchToSwitch = false + public var isAnimated = true + + private var valueShouldChange = true + private var shouldTouchToSwitch = true - private var valueChangedBlock: ActionBlock? private var actionBlock: ActionBlock? // Sizes are from InVision design specs. @@ -37,7 +37,12 @@ import UIKit static let shakeIntensity: CGFloat = 2 - private var thumbView = View() + private var thumbView: View = { + let view = View() + view.backgroundColor = .white + view.layer.cornerRadius = Switch.getThumbHeight() / 2.0 + return view + }() //-------------------------------------------------- // MARK: - Computed Properties @@ -53,8 +58,49 @@ import UIKit open var isOn: Bool = false { didSet { - backgroundColor = isOn ? trackTintColor?.on : trackTintColor?.off - thumbView.backgroundColor = isOn ? thumbTintColor?.on : thumbTintColor?.off + isSelected = isOn + + if isAnimated { + UIView.animate(withDuration: 0.2, delay: 0.0, options: .curveEaseIn, animations: { + if self.isOn { + self.thumbView.backgroundColor = self.thumbTintColor?.on + self.backgroundColor = self.trackTintColor?.on + + } else { + self.thumbView.backgroundColor = self.thumbTintColor?.off + self.backgroundColor = self.trackTintColor?.off + } + + }, completion: nil) + + UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.3, options: [], animations: { + if self.isOn { + self.thumbLeadingConstraint?.isActive = true + self.thumbTrailingConstraint?.isActive = false + self.relativeThumbLeadingConstraint?.isActive = false + self.relativeThumbTrailingConstraint?.isActive = true + + } else { + self.thumbLeadingConstraint?.isActive = false + self.thumbTrailingConstraint?.isActive = true + self.relativeThumbLeadingConstraint?.isActive = true + self.relativeThumbTrailingConstraint?.isActive = false + } + + self.thumbWidthConstraint?.constant = Switch.getThumbWidth() + PaddingOne + self.layoutIfNeeded() + + }, completion: nil) + + } else { + backgroundColor = isOn ? trackTintColor?.on : trackTintColor?.off + thumbView.backgroundColor = isOn ? thumbTintColor?.on : thumbTintColor?.off + } + + FormValidator.enableByValidationWith(delegate: delegateObject?.formValidationProtocol) + accessibilityValue = isOn ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff") + setNeedsLayout() + layoutIfNeeded() } } @@ -70,6 +116,9 @@ import UIKit private var thumbLeadingConstraint: NSLayoutConstraint? private var thumbTrailingConstraint: NSLayoutConstraint? + private var relativeThumbLeadingConstraint: NSLayoutConstraint? + private var relativeThumbTrailingConstraint: NSLayoutConstraint? + private var thumbHeightConstraint: NSLayoutConstraint? private var thumbWidthConstraint: NSLayoutConstraint? private var heightConstraint: NSLayoutConstraint? @@ -88,15 +137,15 @@ import UIKit self.init(frame: .zero) } - public convenience init(isOn: Bool, changeBlock: ActionBlock?) { + public convenience init(isOn: Bool, actionBlock: ActionBlock?) { self.init(frame: .zero) self.isOn = isOn - valueChangedBlock = changeBlock + self.actionBlock = actionBlock } - public convenience init(changeBlock: ActionBlock?) { + public convenience init(actionBlock: ActionBlock?) { self.init(frame: .zero) - valueChangedBlock = changeBlock + self.actionBlock = actionBlock } public required init?(coder: NSCoder) { @@ -126,10 +175,6 @@ import UIKit guard subviews.isEmpty else { return } -// canChangeValue = true - shouldTouchToSwitch = true - valueShouldChange = true - heightConstraint = heightAnchor.constraint(equalToConstant: Switch.trackSize.height) heightConstraint?.isActive = true @@ -137,10 +182,7 @@ import UIKit widthConstraint?.isActive = true layer.cornerRadius = Switch.trackSize.height / 2.0 - - let thumbView = MVMCoreUICommonViewsUtility.commonView() - thumbView.backgroundColor = .white - thumbView.layer.cornerRadius = Switch.getThumbHeight() / 2.0 + backgroundColor = trackTintColor?.off addSubview(thumbView) @@ -149,13 +191,13 @@ import UIKit thumbView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true thumbTrailingConstraint = trailingAnchor.constraint(equalTo: thumbView.trailingAnchor, constant: 1) + relativeThumbTrailingConstraint = trailingAnchor.constraint(greaterThanOrEqualTo: thumbView.trailingAnchor) + relativeThumbTrailingConstraint?.isActive = true + + relativeThumbLeadingConstraint = thumbView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor) thumbLeadingConstraint = thumbView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 1) thumbLeadingConstraint?.isActive = true - shouldTouchToSwitch = false - setState(isOn, animated: false) - shouldTouchToSwitch = true - accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "Switch_buttonlabel") } @@ -176,59 +218,23 @@ import UIKit } //-------------------------------------------------- - // MARK: - Methods + // MARK: - Actions //-------------------------------------------------- - @objc func addCustomAction() { - - actionBlock?() + open override func sendAction(_ action: Selector, to target: Any?, for event: UIEvent?) { + super.sendAction(action, to: target, for: event) + toggleAndAction() } - public func setState(_ state: Bool, animated: Bool) { - - setState(state, withoutBlockAnimated: animated) - valueChangedBlock?() - FormValidator.enableByValidationWith(delegate: delegateObject?.formValidationProtocol) + open override func sendActions(for controlEvents: UIControl.Event) { + super.sendActions(for: controlEvents) + toggleAndAction() } - public func setState(_ state: Bool, withoutBlockAnimated animated: Bool) { - - isOn = state - - if !shouldTouchToSwitch { - thumbEnlargeAnimation() - thumbMoveAnitmationTo(on: isOn) - thumbShakeAnitmationTo(on: isOn) - } - - if isOn { - thumbLeadingConstraint?.priority = UILayoutPriority(1) - thumbTrailingConstraint?.priority = UILayoutPriority(999) - thumbTrailingConstraint?.constant = 1 - - } else { - thumbTrailingConstraint?.priority = UILayoutPriority(1) - thumbLeadingConstraint?.priority = UILayoutPriority(999) - thumbLeadingConstraint?.constant = 1 - } - - setBaseColorToOn(isOn, animated: animated) - thumbReformAnimation(animated) - accessibilityValue = state ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff") - setNeedsLayout() - layoutIfNeeded() - } - - @discardableResult - public func changeValue() -> Bool { - + /// This will toggle the state of the Switch and execute the actionBlock if provided. + public func toggleAndAction() { isOn.toggle() - shouldTouchToSwitch = false - setState(isOn, animated: true) - shouldTouchToSwitch = true - sendActions(for: .valueChanged) - - return isOn + actionBlock?() } //-------------------------------------------------- @@ -244,61 +250,24 @@ import UIKit public func touchesBegan(_ touches: Set, with event: UIEvent) { - thumbEnlargeAnimation() + UIView.animate(withDuration: 0.1, animations: { + self.thumbWidthConstraint?.constant += PaddingOne + self.layoutIfNeeded() + }) + sendActions(for: .touchDown) } - public func touchesEnded(_ touches: Set, with event: UIEvent) { + public override func touchesEnded(_ touches: Set, with event: UIEvent?) { - if shouldTouchToSwitch && valueShouldChange { - changeValue() - } - - sendActions(for: .touchUpInside) - thumbMoveAnitmationTo(on: isOn) - thumbShakeAnitmationTo(on: isOn) - thumbReformAnimation(true) + thumbReformAnimation() valueShouldChange = true - } - - public func touchesMoved(_ touches: Set, with event: UIEvent) { - - if shouldTouchToSwitch { - if touchMoves(toLeft: touches) { - thumbMoveAnitmationTo(on: false) - thumbShakeAnitmationTo(on: false) - thumbReformAnimation(true) - } else { - thumbMoveAnitmationTo(on: true) - thumbShakeAnitmationTo(on: true) - thumbReformAnimation(true) - } - } - /* - if touchIsOutSide(touches) { - sendActions(for: .touchDragOutside) - if !shouldTouchToSwitch { - thumbReformAnimation(true) - } - } else { - sendActions(for: .touchDragInside) - } - */ - } - - func touchMoves(toLeft touches: Set?) -> Bool { - - let location = touches?.first?.location(in: self) - let x = CGFloat(location?.x ?? 0.0) - return x < frame.size.width / 2.0 + sendActions(for: .touchUpInside) } public func touchesCancelled(_ touches: Set, with event: UIEvent) { - if shouldTouchToSwitch { - thumbReformAnimation(true) - } - + thumbReformAnimation() sendActions(for: .touchCancel) } @@ -306,17 +275,9 @@ import UIKit // MARK: - Animations //-------------------------------------------------- - public func thumbEnlargeAnimation() { + public func thumbReformAnimation() { - UIView.animate(withDuration: 0.1, animations: { - self.thumbWidthConstraint?.constant += PaddingOne - self.layoutIfNeeded() - }) - } - - public func thumbReformAnimation(_ animated: Bool) { - - if animated { + if isAnimated { UIView.animate(withDuration: 0.1, animations: { self.thumbWidthConstraint?.constant = Switch.getThumbWidth() self.layoutIfNeeded() @@ -326,82 +287,6 @@ import UIKit layoutIfNeeded() } } - - public func thumbMoveAnitmationTo(on toOn: Bool) { - - UIView.animate(withDuration: 0.1, animations: { - if toOn { - self.thumbLeadingConstraint?.priority = UILayoutPriority(1) - self.thumbTrailingConstraint?.priority = UILayoutPriority(999) - self.thumbTrailingConstraint?.constant = 1 - - } else { - self.thumbLeadingConstraint?.priority = UILayoutPriority(999) - self.thumbTrailingConstraint?.priority = UILayoutPriority(1) - self.thumbLeadingConstraint?.constant = 1 - } - - self.setBaseColorToOn(toOn, animated: true) - self.thumbWidthConstraint?.constant = Switch.getThumbWidth() + PaddingOne - self.valueShouldChange = toOn != self.isOn - self.layoutIfNeeded() - }) - } - - public func setBaseColorToOn(_ toOn: Bool, animated: Bool) { - - if animated { - UIView.beginAnimations(nil, context: nil) - UIView.setAnimationDuration(0.2) - UIView.setAnimationCurve(.easeIn) - - if toOn { - thumbView.backgroundColor = thumbTintColor?.on - backgroundColor = trackTintColor?.on - - } else { - thumbView.backgroundColor = thumbTintColor?.off - backgroundColor = trackTintColor?.off - } - UIView.commitAnimations() - - } else if isOn { - thumbView.backgroundColor = thumbTintColor?.on - backgroundColor = trackTintColor?.on - - } else { - thumbView.backgroundColor = thumbTintColor?.off - backgroundColor = trackTintColor?.off - } - } - - //used after thumb moving to match the behavior of default uiswitch - public func thumbShakeAnitmationTo(on toOn: Bool) { - - UIView.animate(withDuration: 0.1, delay: 0.1, options: .curveEaseIn, animations: { - if toOn { - self.thumbTrailingConstraint?.constant = Switch.shakeIntensity - } else { - self.thumbLeadingConstraint?.constant = Switch.shakeIntensity - } - - self.layoutIfNeeded() - - }, completion: nil) - - UIView.animate(withDuration: 0.2, delay: 0.1, options: [], animations: { - if toOn { - self.thumbTrailingConstraint?.constant = 1 - } else { - self.thumbLeadingConstraint?.constant = 1 - } - - self.layoutIfNeeded() - - }) { finished in - self.valueShouldChange = true - } - } } // MARK: - Accessibility @@ -455,10 +340,18 @@ extension Switch { thumbTintColor?.off = UIColor.mfGet(forHex: color) } - setState(dictionary["state"] as? Bool ?? false, animated: false) + if let state = dictionary["state"] as? Bool { + isAnimated = false + isOn = state + isAnimated = true + } - if let _ = dictionary["actionMap"] as? [String: Any] { - addTarget(self, action: #selector(addCustomAction), for: .touchUpInside) + if let actionMap = dictionary.optionalDictionaryForKey("actionMap") { +// actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } + } + + if let isAnimated = dictionary["state"] as? Bool { + self.isAnimated = isAnimated } }