General working order of the Toggle.

This commit is contained in:
Kevin G Christiano 2019-12-11 15:10:26 -05:00
parent d1ee242ca7
commit eab910011e
3 changed files with 102 additions and 104 deletions

View File

@ -28,7 +28,7 @@
0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */; };
0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */; };
0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */; };
0AA33B3A2398524F0067DD0F /* Switch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AA33B392398524F0067DD0F /* Switch.swift */; };
0AA33B3A2398524F0067DD0F /* Toggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AA33B392398524F0067DD0F /* Toggle.swift */; };
943784F5236B77BB006A1E82 /* GraphView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943784F3236B77BB006A1E82 /* GraphView.swift */; };
943784F6236B77BB006A1E82 /* GraphViewAnimationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943784F4236B77BB006A1E82 /* GraphViewAnimationHandler.swift */; };
9455B19C234F8A0400A574DB /* MVMAnimationFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9455B19B234F8A0400A574DB /* MVMAnimationFramework.framework */; };
@ -227,7 +227,7 @@
0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyButton.swift; sourceTree = "<group>"; };
0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = "<group>"; };
0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxWithLabelView.swift; sourceTree = "<group>"; };
0AA33B392398524F0067DD0F /* Switch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Switch.swift; sourceTree = "<group>"; };
0AA33B392398524F0067DD0F /* Toggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toggle.swift; sourceTree = "<group>"; };
943784F3236B77BB006A1E82 /* GraphView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphView.swift; sourceTree = "<group>"; };
943784F4236B77BB006A1E82 /* GraphViewAnimationHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphViewAnimationHandler.swift; sourceTree = "<group>"; };
9455B19B234F8A0400A574DB /* MVMAnimationFramework.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MVMAnimationFramework.framework; path = ../SharedFrameworks/MVMAnimationFramework.framework; sourceTree = "<group>"; };
@ -781,7 +781,7 @@
01004F2F22721C3800991ECC /* RadioButton.swift */,
943784F3236B77BB006A1E82 /* GraphView.swift */,
943784F4236B77BB006A1E82 /* GraphViewAnimationHandler.swift */,
0AA33B392398524F0067DD0F /* Switch.swift */,
0AA33B392398524F0067DD0F /* Toggle.swift */,
);
path = Views;
sourceTree = "<group>";
@ -1085,7 +1085,7 @@
D29DF11721E6805F003B2FB9 /* UIColor+MFConvenience.m in Sources */,
D29DF25321E6A177003B2FB9 /* MFDigitTextField.m in Sources */,
D2B18B7F2360913400A9AEDC /* Control.swift in Sources */,
0AA33B3A2398524F0067DD0F /* Switch.swift in Sources */,
0AA33B3A2398524F0067DD0F /* Toggle.swift in Sources */,
D29DF12F21E6851E003B2FB9 /* MVMCoreUITopAlertMainView.m in Sources */,
DBC4392122491730001AB423 /* LabelWithInternalButton.swift in Sources */,
D224798C231450C8003FCCF9 /* HeadlineBodySwitch.swift in Sources */,

View File

@ -1,5 +1,5 @@
//
// Switch.swift
// Toggle.swift
// MVMCoreUI
//
// Created by Kevin Christiano on 12/4/19.
@ -14,33 +14,33 @@ public typealias ActionBlockConfirmation = () -> (Bool)
/**
A custom implementation of Apple's UISwitch.
Track: The background of the switch control.
Thumb: The circular indicator that slides on the track.
Container: The background of the toggle control.
Knob: The circular indicator that slides on the container.
*/
@objcMembers open class Switch: Control, MVMCoreUIViewConstrainingProtocol, FormValidationFormFieldProtocol {
@objcMembers open class Toggle: 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)
public var containerTintColor: (on: UIColor?, off: UIColor?)? = (on: .mfShamrock(), off: .black)
public var knobTintColor: (on: UIColor?, off: UIColor?)? = (on: .white, off: .white)
public var disabledTintColor: (container: UIColor?, knob: UIColor?)? = (container: .mfSilver(), knob: .white)
public var isAnimated = true
public var didSwitchAction: ActionBlock?
public var shouldSwitchAction: ActionBlockConfirmation? = {
public var didToggleAction: ActionBlock?
public var shouldToggleAction: ActionBlockConfirmation? = {
return { return true }
}()
// Sizes are from InVision design specs.
static let trackSize = CGSize(width: 46, height: 24)
static let thumbSize = CGSize(width: 22, height: 22)
static let containerSize = CGSize(width: 46, height: 24)
static let knobSize = CGSize(width: 22, height: 22)
private var thumbView: View = {
private var knobView: View = {
let view = View()
view.backgroundColor = .white
view.layer.cornerRadius = Switch.getThumbHeight() / 2.0
view.layer.cornerRadius = Toggle.getKnobHeight() / 2.0
return view
}()
@ -51,8 +51,8 @@ public typealias ActionBlockConfirmation = () -> (Bool)
open override var isEnabled: Bool {
didSet {
isUserInteractionEnabled = isEnabled
backgroundColor = isEnabled ? trackTintColor?.on : disabledTintColor?.track
thumbView.backgroundColor = isEnabled ? thumbTintColor?.on : disabledTintColor?.thumb
backgroundColor = isEnabled ? containerTintColor?.on : disabledTintColor?.container
knobView.backgroundColor = isEnabled ? knobTintColor?.on : disabledTintColor?.knob
}
}
@ -69,28 +69,25 @@ public typealias ActionBlockConfirmation = () -> (Bool)
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
self.knobView.backgroundColor = self.knobTintColor?.on
self.backgroundColor = self.containerTintColor?.on
} else {
self.thumbView.backgroundColor = self.thumbTintColor?.off
self.backgroundColor = self.trackTintColor?.off
self.knobView.backgroundColor = self.knobTintColor?.off
self.backgroundColor = self.containerTintColor?.off
}
}, completion: nil)
UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.2, options: [], animations: {
self.constrainThumb()
self.thumbWidthConstraint?.constant = Switch.getThumbWidth()
self.constrainKnob()
self.knobWidthConstraint?.constant = Toggle.getKnobWidth()
self.layoutIfNeeded()
}, completion: nil)
} else {
backgroundColor = isOn ? trackTintColor?.on : trackTintColor?.off
thumbView.backgroundColor = isOn ? thumbTintColor?.on : thumbTintColor?.off
self.constrainThumb()
backgroundColor = isOn ? containerTintColor?.on : containerTintColor?.off
knobView.backgroundColor = isOn ? knobTintColor?.on : knobTintColor?.off
self.constrainKnob()
}
FormValidator.enableByValidationWith(delegate: delegateObject?.formValidationProtocol)
@ -110,17 +107,17 @@ public typealias ActionBlockConfirmation = () -> (Bool)
// MARK: - Constraints
//--------------------------------------------------
private var thumbLeadingConstraint: NSLayoutConstraint?
private var thumbTrailingConstraint: NSLayoutConstraint?
private var thumbHeightConstraint: NSLayoutConstraint?
private var thumbWidthConstraint: NSLayoutConstraint?
private var knobLeadingConstraint: NSLayoutConstraint?
private var knobTrailingConstraint: NSLayoutConstraint?
private var knobHeightConstraint: NSLayoutConstraint?
private var knobWidthConstraint: NSLayoutConstraint?
private var heightConstraint: NSLayoutConstraint?
private var widthConstraint: NSLayoutConstraint?
private func constrainThumb() {
private func constrainKnob() {
self.thumbLeadingConstraint?.isActive = isOn
self.thumbTrailingConstraint?.isActive = !isOn
self.knobLeadingConstraint?.isActive = !isOn
self.knobTrailingConstraint?.isActive = isOn
}
//--------------------------------------------------
@ -136,26 +133,26 @@ public typealias ActionBlockConfirmation = () -> (Bool)
self.init(frame: .zero)
}
public convenience init(isOn: Bool, didSwitchAction: ActionBlock?) {
public convenience init(isOn: Bool, didToggleAction: ActionBlock?) {
self.init(frame: .zero)
self.isOn = isOn
self.didSwitchAction = didSwitchAction
self.didToggleAction = didToggleAction
}
public convenience init(didSwitchAction: ActionBlock?) {
public convenience init(didToggleAction: ActionBlock?) {
self.init(frame: .zero)
self.didSwitchAction = didSwitchAction
self.didToggleAction = didToggleAction
}
public convenience init(shouldSwitchAction: ActionBlockConfirmation?, didSwitchAction: ActionBlock?) {
public convenience init(shouldToggleAction: ActionBlockConfirmation?, didToggleAction: ActionBlock?) {
self.init(frame: .zero)
self.didSwitchAction = didSwitchAction
self.shouldSwitchAction = shouldSwitchAction
self.didToggleAction = didToggleAction
self.shouldToggleAction = shouldToggleAction
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
fatalError("Switch does not support xib.")
fatalError("Toggle does not support xib.")
}
//--------------------------------------------------
@ -165,14 +162,14 @@ public typealias ActionBlockConfirmation = () -> (Bool)
public override func updateView(_ size: CGFloat) {
super.updateView(size)
heightConstraint?.constant = Switch.getTrackHeight()
widthConstraint?.constant = Switch.getTrackWidth()
heightConstraint?.constant = Toggle.getContainerHeight()
widthConstraint?.constant = Toggle.getContainerWidth()
thumbHeightConstraint?.constant = Switch.getThumbHeight()
thumbWidthConstraint?.constant = Switch.getThumbWidth()
knobHeightConstraint?.constant = Toggle.getKnobHeight()
knobWidthConstraint?.constant = Toggle.getKnobWidth()
layer.cornerRadius = Switch.getTrackHeight() / 2.0
thumbView.layer.cornerRadius = Switch.getThumbHeight() / 2.0
layer.cornerRadius = Toggle.getContainerHeight() / 2.0
knobView.layer.cornerRadius = Toggle.getKnobHeight() / 2.0
}
public override func setupView() {
@ -180,50 +177,50 @@ public typealias ActionBlockConfirmation = () -> (Bool)
guard subviews.isEmpty else { return }
heightConstraint = heightAnchor.constraint(equalToConstant: Switch.trackSize.height)
heightConstraint = heightAnchor.constraint(equalToConstant: Toggle.containerSize.height)
heightConstraint?.isActive = true
widthConstraint = widthAnchor.constraint(equalToConstant: Switch.trackSize.width)
widthConstraint = widthAnchor.constraint(equalToConstant: Toggle.containerSize.width)
widthConstraint?.isActive = true
layer.cornerRadius = Switch.trackSize.height / 2.0
backgroundColor = trackTintColor?.off
layer.cornerRadius = Toggle.containerSize.height / 2.0
backgroundColor = containerTintColor?.off
addSubview(thumbView)
addSubview(knobView)
thumbHeightConstraint = thumbView.heightAnchor.constraint(equalToConstant: Switch.thumbSize.height)
thumbHeightConstraint?.isActive = true
thumbWidthConstraint = thumbView.widthAnchor.constraint(equalToConstant: Switch.thumbSize.width)
thumbWidthConstraint?.isActive = true
thumbView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
thumbView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor).isActive = true
bottomAnchor.constraint(greaterThanOrEqualTo: thumbView.bottomAnchor).isActive = true
knobHeightConstraint = knobView.heightAnchor.constraint(equalToConstant: Toggle.knobSize.height)
knobHeightConstraint?.isActive = true
knobWidthConstraint = knobView.widthAnchor.constraint(equalToConstant: Toggle.knobSize.width)
knobWidthConstraint?.isActive = true
knobView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
knobView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor).isActive = true
bottomAnchor.constraint(greaterThanOrEqualTo: knobView.bottomAnchor).isActive = true
thumbTrailingConstraint = trailingAnchor.constraint(equalTo: thumbView.trailingAnchor, constant: 1)
thumbLeadingConstraint = thumbView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 1)
thumbLeadingConstraint?.isActive = true
knobTrailingConstraint = trailingAnchor.constraint(equalTo: knobView.trailingAnchor, constant: 1)
knobLeadingConstraint = knobView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 1)
knobLeadingConstraint?.isActive = true
accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "Switch_buttonlabel")
accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel")
}
class func getTrackWidth() -> CGFloat {
let trackWidth = Switch.trackSize.width
return (MFSizeObject(standardSize: trackWidth, standardiPadPortraitSize: CGFloat(Switch.trackSize.width * 1.5)))?.getValueBasedOnApplicationWidth() ?? trackWidth
class func getContainerWidth() -> CGFloat {
let containerWidth = Toggle.containerSize.width
return (MFSizeObject(standardSize: containerWidth, standardiPadPortraitSize: CGFloat(Toggle.containerSize.width * 1.5)))?.getValueBasedOnApplicationWidth() ?? containerWidth
}
class func getTrackHeight() -> CGFloat {
let trackHeight = Switch.trackSize.height
return (MFSizeObject(standardSize: trackHeight, standardiPadPortraitSize: CGFloat(Switch.trackSize.height * 1.5)))?.getValueBasedOnApplicationWidth() ?? trackHeight
class func getContainerHeight() -> CGFloat {
let containerHeight = Toggle.containerSize.height
return (MFSizeObject(standardSize: containerHeight, standardiPadPortraitSize: CGFloat(Toggle.containerSize.height * 1.5)))?.getValueBasedOnApplicationWidth() ?? containerHeight
}
class func getThumbWidth() -> CGFloat {
let thumbWidth = Switch.thumbSize.width
return (MFSizeObject(standardSize: thumbWidth, standardiPadPortraitSize: CGFloat(Switch.thumbSize.width * 1.5)))?.getValueBasedOnApplicationWidth() ?? thumbWidth
class func getKnobWidth() -> CGFloat {
let knobWidth = Toggle.knobSize.width
return (MFSizeObject(standardSize: knobWidth, standardiPadPortraitSize: CGFloat(Toggle.knobSize.width * 1.5)))?.getValueBasedOnApplicationWidth() ?? knobWidth
}
class func getThumbHeight() -> CGFloat {
let thumbHeight = Switch.thumbSize.width
return (MFSizeObject(standardSize: thumbHeight, standardiPadPortraitSize: CGFloat(Switch.thumbSize.height * 1.5)))?.getValueBasedOnApplicationWidth() ?? thumbHeight
class func getKnobHeight() -> CGFloat {
let knobHeight = Toggle.knobSize.width
return (MFSizeObject(standardSize: knobHeight, standardiPadPortraitSize: CGFloat(Toggle.knobSize.height * 1.5)))?.getValueBasedOnApplicationWidth() ?? knobHeight
}
//--------------------------------------------------
@ -240,12 +237,12 @@ public typealias ActionBlockConfirmation = () -> (Bool)
toggleAndAction()
}
/// This will toggle the state of the Switch and execute the actionBlock if provided.
/// This will toggle the state of the Toggle and execute the actionBlock if provided.
public func toggleAndAction() {
if let result = shouldSwitchAction?(), result {
if let result = shouldToggleAction?(), result {
isOn.toggle()
didSwitchAction?()
didToggleAction?()
}
}
@ -263,16 +260,17 @@ public typealias ActionBlockConfirmation = () -> (Bool)
open override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
UIView.animate(withDuration: 0.1, animations: {
self.thumbWidthConstraint?.constant += PaddingOne
self.knobWidthConstraint?.constant += PaddingOne
self.layoutIfNeeded()
})
}
public override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
thumbReformAnimation()
knobReformAnimation()
guard let coordinates: CGPoint = touches.first?.location(in: self),
// Action only occurs of the user lifts up from withing acceptable region of the toggle.
guard let coordinates = touches.first?.location(in: self),
coordinates.x > -20,
coordinates.x < bounds.width + 20,
coordinates.y > -20,
@ -284,7 +282,7 @@ public typealias ActionBlockConfirmation = () -> (Bool)
public func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
thumbReformAnimation()
knobReformAnimation()
sendActions(for: .touchCancel)
}
@ -292,22 +290,23 @@ public typealias ActionBlockConfirmation = () -> (Bool)
// MARK: - Animations
//--------------------------------------------------
public func thumbReformAnimation() {
public func knobReformAnimation() {
if isAnimated {
UIView.animate(withDuration: 0.1, animations: {
self.thumbWidthConstraint?.constant = Switch.getThumbWidth()
self.knobWidthConstraint?.constant = Toggle.getKnobWidth()
self.layoutIfNeeded()
}, completion: nil)
} else {
thumbWidthConstraint?.constant = Switch.getThumbWidth()
knobWidthConstraint?.constant = Toggle.getKnobWidth()
layoutIfNeeded()
}
}
}
// MARK: - Accessibility
extension Switch {
extension Toggle {
public func formFieldGroupName() -> String? {
return json?["groupName"] as? String
@ -315,7 +314,7 @@ extension Switch {
}
// MARK: - FormValidationProtocol
extension Switch {
extension Toggle {
public func isValidField() -> Bool {
return isOn && json?["required"] as? Bool ?? false
@ -331,7 +330,7 @@ extension Switch {
}
// MARK: - MVMCoreUIMoleculeViewProtocol
extension Switch {
extension Toggle {
public override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
@ -342,19 +341,19 @@ extension Switch {
guard let dictionary = json else { return }
if let color = dictionary["onTintColor"] as? String {
trackTintColor?.on = UIColor.mfGet(forHex: color)
containerTintColor?.on = UIColor.mfGet(forHex: color)
}
if let color = dictionary["offTintColor"] as? String {
trackTintColor?.off = UIColor.mfGet(forHex: color)
containerTintColor?.off = UIColor.mfGet(forHex: color)
}
if let color = dictionary["onThumbTintColor"] as? String {
thumbTintColor?.on = UIColor.mfGet(forHex: color)
if let color = dictionary["onKnobTintColor"] as? String {
knobTintColor?.on = UIColor.mfGet(forHex: color)
}
if let color = dictionary["offThumbTintColor"] as? String {
thumbTintColor?.off = UIColor.mfGet(forHex: color)
if let color = dictionary["offKnobTintColor"] as? String {
knobTintColor?.off = UIColor.mfGet(forHex: color)
}
if let state = dictionary["state"] as? Bool {
@ -364,17 +363,16 @@ extension Switch {
}
if let actionMap = dictionary.optionalDictionaryForKey("actionMap") {
actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) }
didToggleAction = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) }
}
if let isAnimated = dictionary["state"] as? Bool {
if let isAnimated = dictionary["isAnimated"] as? Bool {
self.isAnimated = isAnimated
}
}
public class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return Switch.getTrackHeight()
return Toggle.getContainerHeight()
}
public func needsToBeConstrained() -> Bool {

View File

@ -48,7 +48,7 @@
@"radioButtonLabel": RadioButtonLabel.class,
@"listItem": MoleculeTableViewCell.class,
@"accordionListItem": AccordionMoleculeTableViewCell.class,
@"switch": Switch.class,
@"toggle": Toggle.class,
@"leftRightLabelView": LeftRightLabelView.class,
@"actionDetailWithImage": ActionDetailWithImage.class,
@"image": MFLoadImageView.class,