WIP, putting Switch together.

This commit is contained in:
Kevin G Christiano 2019-12-09 13:37:33 -05:00
parent a00fdbad60
commit cbb016406a
3 changed files with 212 additions and 186 deletions

View File

@ -7,17 +7,16 @@
//
import MVMCore
import MVMCoreUI
import UIKit
typealias ValueChangeBlock = () -> ()
public typealias ValueChangeBlock = () -> ()
open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtocol, FormValidationFormFieldProtocol {
@objcMembers open class Switch: Control, MVMCoreUIViewConstrainingProtocol, FormValidationFormFieldProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public var isOn = false
public var trackTintColor: (on: UIColor?, off: UIColor?)?
public var thumbTintColor: (on: UIColor?, off: UIColor?)?
@ -25,18 +24,40 @@ open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtoc
var valueChangedBlock: ValueChangeBlock?
var actionBlock: ValueChangeBlock?
static let width: CGFloat = 42
static let height: CGFloat = 22
static let thumbWidth: CGFloat = 20
static let thumbHeight: CGFloat = 20
static let trackSize = CGSize(width: 46, height: 24)
static let thumbSize = CGSize(width: 22, height: 22)
static let shakeIntensity: CGFloat = 2
private weak var baseView: UIView?
private weak var thumbView: UIView?
private var thumbView = View()
private var valueShouldChange = false
private var canChangeValue = false
//--------------------------------------------------
// MARK: - Computed Properties
//--------------------------------------------------
open override var isEnabled: Bool {
didSet {
if isEnabled {
} else {
}
}
}
open var isOn: Bool = false {
didSet {
if isOn {
} else {
}
}
}
//--------------------------------------------------
// MARK: - Delegate
//--------------------------------------------------
@ -47,36 +68,12 @@ open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtoc
// MARK: - Constraints
//--------------------------------------------------
private var thumbLeftConstraint: NSLayoutConstraint?
private var thumbRightConstraint: NSLayoutConstraint?
private var thumbLeadingConstraint: NSLayoutConstraint?
private var thumbTrailingConstraint: NSLayoutConstraint?
private var thumbHeightConstraint: NSLayoutConstraint?
private var thumbWidthConstraint: NSLayoutConstraint?
private weak var height: NSLayoutConstraint?
private weak var width: NSLayoutConstraint?
//--------------------------------------------------
// MARK: - Lifecycle
//--------------------------------------------------
public func updateView(_ size: CGFloat) {
height?.constant = MVMCoreUISwitch.getHeight()
width?.constant = MVMCoreUISwitch.getWidth()
baseView?.layer.cornerRadius = MVMCoreUISwitch.getHeight() / 2.0
thumbView?.layer.cornerRadius = MVMCoreUISwitch.getthumbHeight() * 0.5
thumbHeightConstraint?.constant = MVMCoreUISwitch.getthumbHeight()
thumbWidthConstraint?.constant = MVMCoreUISwitch.getthumbWidth()
}
public func setupView() {
trackTintColor?.on = .mfSwitchOnTint()
trackTintColor?.off = .mfSwitchOffTint()
canChangeValue = true
shouldTouchToSwitch = true
setUpViewWithState(isOn)
accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "MVMCoreUISwitch_buttonlabel")
}
private var heightConstraint: NSLayoutConstraint?
private var widthConstraint: NSLayoutConstraint?
//--------------------------------------------------
// MARK: - Initializers
@ -87,100 +84,101 @@ open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtoc
setupView()
}
public convenience init() {
public convenience override init() {
self.init(frame: frame)
}
public convenience init(isOn: Bool, changeBlock: ValueChangeBlock?) {
self.init(frame: frame)
self.isOn = isOn
valueChangedBlock = changeBlock
}
public convenience init(changeBlock: ValueChangeBlock?) {
self.init(frame: frame)
valueChangedBlock = changeBlock
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
fatalError("Switch does not support xib")
fatalError("Switch does not support xib.")
}
//--------------------------------------------------
// MARK: - Lifecycle
//--------------------------------------------------
public override func updateView(_ size: CGFloat) {
super.updateView(size)
heightConstraint?.constant = Switch.getTrackHeight()
widthConstraint?.constant = Switch.getTrackWidth()
layer.cornerRadius = Switch.getTrackHeight() / 2.0
thumbView.layer.cornerRadius = Switch.getThumbHeight() / 2.0
thumbHeightConstraint?.constant = Switch.getThumbHeight()
thumbWidthConstraint?.constant = Switch.getThumbWidth()
}
public override func setupView() {
super.setupView()
guard !subviews.isEmpty else { return }
trackTintColor?.on = .mfSwitchOnTint() // Green
trackTintColor?.off = .mfSwitchOffTint() // Black
thumbTintColor?.on = .white
thumbTintColor?.off = .white
canChangeValue = true
shouldTouchToSwitch = true
valueShouldChange = true
heightConstraint = heightAnchor.constraint(equalToConstant: Switch.trackSize.height)
heightConstraint?.isActive = true
widthConstraint = widthAnchor.constraint(equalToConstant: Switch.trackSize.width)
widthConstraint?.isActive = true
layer.cornerRadius = Switch.trackSize.height / 2.0
let thumbView = MVMCoreUICommonViewsUtility.commonView()
thumbView.backgroundColor = .white
thumbView.layer.cornerRadius = Switch.getThumbHeight() / 2.0
addSubview(thumbView)
thumbView.heightAnchor.constraint(equalToConstant: Switch.thumbSize.height).isActive = true
thumbView.widthAnchor.constraint(equalToConstant: Switch.thumbSize.width).isActive = true
thumbView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
thumbTrailingConstraint = trailingAnchor.constraint(equalTo: thumbView.trailingAnchor, constant: 1)
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")
}
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
public class func mvmSwitchDefault() -> Self {
let mySwitch = self.init(frame: .zero)
mySwitch?.translatesAutoresizingMaskIntoConstraints = false
return mySwitch
}
public class func mvmSwitchDefault(with block: ValueChangeBlock?) -> Self {
let mySwitch = self.init(frame: .zero)
mySwitch?.valueChangedBlock = block
return mySwitch
}
public func setUpViewWithState(_ on: Bool) {
guard baseView == nil else { return }
isUserInteractionEnabled = true
valueShouldChange = true
let baseView = MVMCoreUICommonViewsUtility.commonView()
if let baseView = baseView {
addSubview(baseView)
}
NSLayoutConstraint.constraintPinSubview(toSuperview: baseView)
let constraints = NSLayoutConstraint.constraintPinView(baseView, heightConstraint: true, heightConstant: MVMCoreUISwitch.getHeight(), widthConstraint: true, widthConstant: MVMCoreUISwitch.getWidth())
self.height = constraints.object(forKey: ConstraintHeight, ofType: NSLayoutConstraint.self)
self.width = constraints.object(forKey: ConstraintWidth, ofType: NSLayoutConstraint.self)
baseView?.layer.cornerRadius = MVMCoreUISwitch.getHeight() / 2.0
onthumbTintColor = UIColor.white
offthumbTintColor = UIColor.white
let thumbView = MVMCoreUICommonViewsUtility.commonView()
thumbView?.backgroundColor = UIColor.white
thumbView?.layer.cornerRadius = MVMCoreUISwitch.getthumbHeight() * 0.5
if let thumbView = thumbView {
baseView?.addSubview(thumbView)
}
let heightWidth = NSLayoutConstraint.constraintPinView(thumbView, heightConstraint: true, heightConstant: MVMCoreUISwitch.getthumbHeight(), widthConstraint: true, widthConstant: MVMCoreUISwitch.getthumbWidth())
let height = heightWidth.object(forKey: ConstraintHeight, ofType: NSLayoutConstraint.self)
let width = heightWidth.object(forKey: ConstraintWidth, ofType: NSLayoutConstraint.self)
thumbHeightConstraint = height
thumbWidthConstraint = width
let leadingTrailingDic = NSLayoutConstraint.constraintPinSubview(thumbView, pinTop: false, topConstant: 0, pinBottom: false, bottomConstant: 0, pinLeft: true, leftConstant: 1, pinRight: true, rightConstant: 1)
let leadingTrailingDic = NSLayoutConstraint.constraintPinSubview(thumbView, pinTop: false, topConstant: 0, pinBottom: false, bottomConstant: 0, pinLeft: true, leftConstant: 1, pinRight: true, rightConstant: 1)
let `left` = leadingTrailingDic.object(forKey: ConstraintLeading, ofType: NSLayoutConstraint.self)
let `right` = leadingTrailingDic.object(forKey: ConstraintTrailing, ofType: NSLayoutConstraint.self)
NSLayoutConstraint.constraintPinSubview(thumbView, pinCenterX: false, centerXConstant: 0, pinCenterY: true, centerYConstant: 0)
`right`?.constant = 15
thumbLeftConstraint = `left`
thumbRightConstraint = `right`
baseView.bringSubviewToFront(thumbView)
//baseView = baseView // Skipping redundant initializing to itself
//thumbView = thumbView // Skipping redundant initializing to itself
baseView.isUserInteractionEnabled = false
thumbView.isUserInteractionEnabled = false
shouldTouchToSwitch = false
setState(on, animated: false)
shouldTouchToSwitch = true
}
@objc func addCustomAction() {
actionBlock?()
}
public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return Switch.getSwitchHeight()
}
// MARK: - UIResponder overide
//--------------------------------------------------
// MARK: - UIResponder
//--------------------------------------------------
public func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
thumbEnlargeAnimation()
@ -189,7 +187,7 @@ open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtoc
public func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
if shouldTouchToSwitch && valueShouldChange{
if shouldTouchToSwitch && valueShouldChange {
changeValue()
}
@ -247,12 +245,11 @@ open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtoc
if animated {
UIView.animate(withDuration: 0.1, animations: {
self.thumbWidthConstraint?.constant = MVMCoreUISwitch.getthumbWidth()
self.thumbWidthConstraint?.constant = Switch.getThumbWidth()
self.layoutIfNeeded()
}) { finished in
}
}) { finished in }
} else {
thumbWidthConstraint?.constant = MVMCoreUISwitch.getthumbWidth()
thumbWidthConstraint?.constant = Switch.getThumbWidth()
layoutIfNeeded()
}
}
@ -261,16 +258,18 @@ open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtoc
UIView.animate(withDuration: 0.1, animations: {
if toOn {
self.thumbLeftConstraint?.priority = UILayoutPriority(1)
self.thumbRightConstraint?.priority = UILayoutPriority(999)
self.thumbRightConstraint?.constant = 1
self.thumbLeadingConstraint?.priority = UILayoutPriority(1)
self.thumbTrailingConstraint?.priority = UILayoutPriority(999)
self.thumbTrailingConstraint?.constant = 1
} else {
self.thumbLeftConstraint?.priority = UILayoutPriority(999)
self.thumbRightConstraint?.priority = UILayoutPriority(1)
self.thumbLeftConstraint?.constant = 1
self.thumbLeadingConstraint?.priority = UILayoutPriority(999)
self.thumbTrailingConstraint?.priority = UILayoutPriority(1)
self.thumbLeadingConstraint?.constant = 1
}
self.setBaseColorToOn(toOn, animated: true)
self.thumbWidthConstraint?.constant = MVMCoreUISwitch.getThumbWidth() + PaddingOne
self.thumbWidthConstraint?.constant = Switch.getThumbWidth() + PaddingOne
self.valueShouldChange = toOn != self.isOn
self.layoutIfNeeded()
})
@ -284,38 +283,41 @@ open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtoc
UIView.setAnimationCurve(.easeIn)
if toOn {
thumbView?.backgroundColor = thumbTintColor!.on
baseView?.backgroundColor = trackTintColor!.on
thumbView.backgroundColor = thumbTintColor?.on
backgroundColor = trackTintColor?.on
} else {
thumbView?.backgroundColor = thumbTintColor!.off
baseView?.backgroundColor = trackTintColor!.off
thumbView.backgroundColor = thumbTintColor?.off
backgroundColor = trackTintColor?.off
}
UIView.commitAnimations()
} else if isOn {
thumbView?.backgroundColor = thumbTintColor!.on
baseView?.backgroundColor = trackTintColor!.on
thumbView.backgroundColor = thumbTintColor?.on
backgroundColor = trackTintColor?.on
} else {
thumbView?.backgroundColor = thumbTintColor!.off
baseView?.backgroundColor = trackTintColor!.off
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.thumbRightConstraint?.constant = Switch.shakeIntensity
self.thumbTrailingConstraint?.constant = Switch.shakeIntensity
} else {
self.thumbLeftConstraint?.constant = Switch.shakeIntensity
self.thumbLeadingConstraint?.constant = Switch.shakeIntensity
}
self.layoutIfNeeded()
}) { finished in
}
}, completion: nil)
UIView.animate(withDuration: 0.2, delay: 0.1, options: [], animations: {
if toOn {
self.thumbRightConstraint?.constant = 1
self.thumbTrailingConstraint?.constant = 1
} else {
self.thumbLeftConstraint?.constant = 1
self.thumbLeadingConstraint?.constant = 1
}
self.layoutIfNeeded()
}) { finished in
@ -328,10 +330,10 @@ open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtoc
valueChangedBlock?()
if delegate && delegate.responds(to: #selector(formValidationProtocol)) && delegate.perform(#selector(formValidationProtocol)).responds(to: #selector(Unmanaged<AnyObject>.formValidatorModel)) {
let formValidator = delegate.perform(#selector(formValidationProtocol)).perform(#selector(Unmanaged<AnyObject>.formValidatorModel)) as? FormValidator
formValidator?.enableByValidation()
}
// if delegate && delegate.responds(to: #selector(formValidationProtocol)) && delegate.perform(#selector(formValidationProtocol)).responds(to: #selector(Unmanaged<AnyObject>.formValidatorModel)) {
let formValidator = delegate.perform(#selector(formValidationProtocol)).perform(#selector(Unmanaged<AnyObject>.formValidatorModel)) as? FormValidator
formValidator?.enableByValidation()
// }
}
public func setState(_ state: Bool, withoutBlockAnimated animated: Bool) {
@ -345,13 +347,13 @@ open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtoc
}
if isOn {
thumbLeftConstraint?.priority = UILayoutPriority(1)
thumbRightConstraint?.priority = UILayoutPriority(999)
thumbRightConstraint?.constant = 1
thumbLeadingConstraint?.priority = UILayoutPriority(1)
thumbTrailingConstraint?.priority = UILayoutPriority(999)
thumbTrailingConstraint?.constant = 1
} else {
thumbRightConstraint?.priority = UILayoutPriority(1)
thumbLeftConstraint?.priority = UILayoutPriority(999)
thumbLeftConstraint?.constant = 1
thumbTrailingConstraint?.priority = UILayoutPriority(1)
thumbLeadingConstraint?.priority = UILayoutPriority(999)
thumbLeadingConstraint?.constant = 1
}
setBaseColorToOn(isOn, animated: animated)
@ -361,77 +363,82 @@ open class Switch: Control, MVMCoreViewProtocol, MVMCoreUIViewConstrainingProtoc
layoutIfNeeded()
}
@discardableResult
public func changeValue() -> Bool {
isOn ^= true
isOn.toggle()
shouldTouchToSwitch = false
setState(isOn, animated: true)
shouldTouchToSwitch = true
sendActions(for: .valueChanged)
return isOn
}
// MARK: - helper
func touchIsOutSide(_ touches: Set<UITouch>?) -> Bool {
let location = touches?.first?.location(in: self)
let x = Float(location?.x ?? 0.0)
let y = Float(location?.y ?? 0.0)
return x < 0 || x > frame.size.width || y < 0 || y > frame.size.height
return isOn
}
func touchMoves(toLeft touches: Set<UITouch>?) -> Bool {
let location = touches?.first?.location(in: self)
let x = Float(location?.x ?? 0.0)
let x = CGFloat(location?.x ?? 0.0)
return x < frame.size.width / 2.0
}
class func getSwitchWidth() -> CGFloat {
return (MFSizeObject(standardSize: Switch.width, standardiPadPortraitSize: Double(SwitchWidth) * 1.5)).getValueBasedOnApplicationWidth()
override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let faultTolerance: CGFloat = 20.0
let area = bounds.insetBy(dx: -faultTolerance, dy: -faultTolerance)
return area.contains(point)
}
class func getSwitchHeight() -> CGFloat {
return (MFSizeObject(standardSize: Switch.height, standardiPadPortraitSize: Double(SwitchHeight) * 1.5)).getValueBasedOnApplicationWidth()
open override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
sendActions(for: .touchUpInside)
}
class func getSwitchthumbWidth() -> CGFloat {
return (MFSizeObject(standardSize: Switch.thumbWidth, standardiPadPortraitSize: Double(SwitchthumbWidth) * 1.5)).getValueBasedOnApplicationWidth()
class func getTrackWidth() -> CGFloat {
return (MFSizeObject(standardSize: Switch.trackSize.width, standardiPadPortraitSize: CGFloat(Switch.trackSize.width * 1.5)))!.getValueBasedOnApplicationWidth()
}
class func getSwitchthumbHeight() -> CGFloat {
return (MFSizeObject(standardSize: Switch.thumbHeight, standardiPadPortraitSize: Double(SwitchthumbHeight) * 1.5)).getValueBasedOnApplicationWidth()
class func getTrackHeight() -> CGFloat {
return (MFSizeObject(standardSize: Switch.trackSize.height, standardiPadPortraitSize: CGFloat(Switch.trackSize.height * 1.5) ))!.getValueBasedOnApplicationWidth()
}
class func getThumbWidth() -> CGFloat {
return (MFSizeObject(standardSize: Switch.thumbSize.width, standardiPadPortraitSize: CGFloat(Switch.thumbSize.width * 1.5)))!.getValueBasedOnApplicationWidth()
}
class func getThumbHeight() -> CGFloat {
return (MFSizeObject(standardSize: Switch.thumbSize.height, standardiPadPortraitSize: CGFloat(Switch.thumbSize.height * 1.5)))!.getValueBasedOnApplicationWidth()
}
}
// MARK: - Accessibility
extension Switch {
func formFieldGroupName() -> String? {
return json.string("groupName")
public func formFieldGroupName() -> String? {
return json?["groupName"] as? String
}
}
// MARK: FormValidationProtocol
extension Switch {
func isValidField() -> Bool {
return isOn && json.bool(forKey: "required")
public func isValidField() -> Bool {
return isOn && json?["required"] as? Bool ?? false
}
func formFieldName() -> String? {
return json.string(KeyFieldKey)
public func formFieldName() -> String? {
return json?[KeyFieldKey] as? String ?? ""
}
func formFieldValue() -> Any? {
public func formFieldValue() -> Any? {
return NSNumber(value: isOn)
}
}
// MARK: - MVMCoreUIMoleculeViewProtocol
extension Switch: MVMCoreUIMoleculeViewProtocol {
extension Switch {
public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
public override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
self.json = json
delegate = delegateObject
@ -455,14 +462,18 @@ extension Switch: MVMCoreUIMoleculeViewProtocol {
thumbTintColor?.off = UIColor.mfGet(forHex: color)
}
setState(json?.bool(forKey: "state"), animated: false)
setState(dictionary["state"] as? Bool ?? false, animated: false)
let actionMap = json?.dict("actionMap")
if actionMap != nil {
if let _ = dictionary["actionMap"] as? [String: Any] {
addTarget(self, action: #selector(addCustomAction), for: .touchUpInside)
}
}
public class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return Switch.getTrackHeight()
}
public func needsToBeConstrained() -> Bool {
return true
}

View File

@ -8,12 +8,19 @@
import UIKit
public class Control: UIControl {
open class Control: UIControl {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
var json: [AnyHashable: Any]?
private var initialSetupPerformed = false
//--------------------------------------------------
// MARK: - Initialiers
//--------------------------------------------------
public override init(frame: CGRect) {
super.init(frame: .zero)
initialSetup()
@ -29,7 +36,12 @@ public class Control: UIControl {
initialSetup()
}
//--------------------------------------------------
// MARK: - Setup
//--------------------------------------------------
public func initialSetup() {
if !initialSetupPerformed {
initialSetupPerformed = true
setupView()
@ -37,6 +49,7 @@ public class Control: UIControl {
}
}
// MARK: MVMCoreViewProtocol
extension Control: MVMCoreViewProtocol {
public func updateView(_ size: CGFloat) { }
@ -48,8 +61,10 @@ extension Control: MVMCoreViewProtocol {
}
}
// MARK: MVMCoreUIMoleculeViewProtocol
extension Control: MVMCoreUIMoleculeViewProtocol {
public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
self.json = json
if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) {

View File

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