General Working order.

This commit is contained in:
Kevin G Christiano 2019-12-10 14:38:32 -05:00
parent 83370956c1
commit ab0ff64677

View File

@ -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<UITouch>, with event: UIEvent) {
thumbEnlargeAnimation()
UIView.animate(withDuration: 0.1, animations: {
self.thumbWidthConstraint?.constant += PaddingOne
self.layoutIfNeeded()
})
sendActions(for: .touchDown)
}
public func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
public override func touchesEnded(_ touches: Set<UITouch>, 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<UITouch>, 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<UITouch>?) -> 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<UITouch>, 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
}
}