General Working order.
This commit is contained in:
parent
83370956c1
commit
ab0ff64677
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user