From 35f7024f8d47504bae8ddd8b0b35304a66409eed Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 25 Sep 2019 14:19:49 -0400 Subject: [PATCH] Adding two new checkbox swift classes. --- MVMCoreUI.xcodeproj/project.pbxproj | 16 +- MVMCoreUI/Atoms/Views/CheckBox.swift | 178 ----------- MVMCoreUI/Atoms/Views/CheckBoxWithLabel.swift | 277 ----------------- MVMCoreUI/Atoms/Views/Checkbox.swift | 288 +++++++++++++++++ .../Atoms/Views/CheckboxWithLabelView.swift | 289 ++++++++++++++++++ .../MVMCoreUIMoleculeMappingObject.m | 4 +- 6 files changed, 587 insertions(+), 465 deletions(-) delete mode 100644 MVMCoreUI/Atoms/Views/CheckBox.swift delete mode 100644 MVMCoreUI/Atoms/Views/CheckBoxWithLabel.swift create mode 100644 MVMCoreUI/Atoms/Views/Checkbox.swift create mode 100644 MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index b2dd623a..9692652c 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -18,8 +18,8 @@ 01DF567021FA5AB300CC099B /* TextFieldListFormViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DF566F21FA5AB300CC099B /* TextFieldListFormViewController.swift */; }; 01E569D3223FFFA500327251 /* ThreeLayerViewController.swift in Headers */ = {isa = PBXBuildFile; fileRef = D2A5146A2214905000345BFB /* ThreeLayerViewController.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */; }; - 0A7BAFA1232BE61800FB8E22 /* CheckBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* CheckBox.swift */; }; - 0A7BAFA3232BE63400FB8E22 /* CheckBoxWithLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA2232BE63400FB8E22 /* CheckBoxWithLabel.swift */; }; + 0A1B4A96233BB18F005B3FB4 /* CheckboxWithLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */; }; + 0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */; }; 948DB67E2326DCD90011F916 /* MultiProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 948DB67D2326DCD90011F916 /* MultiProgress.swift */; }; B8200E152280C4CF007245F4 /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8200E142280C4CF007245F4 /* ProgressBar.swift */; }; B8200E192281DC1A007245F4 /* CornerLabels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8200E182281DC1A007245F4 /* CornerLabels.swift */; }; @@ -205,8 +205,8 @@ 01DF55DF21F8FAA800CC099B /* MFTextFieldListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MFTextFieldListView.swift; sourceTree = ""; }; 01DF566F21FA5AB300CC099B /* TextFieldListFormViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextFieldListFormViewController.swift; sourceTree = ""; }; 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionDetailWithImage.swift; sourceTree = ""; }; - 0A7BAFA0232BE61800FB8E22 /* CheckBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckBox.swift; sourceTree = ""; }; - 0A7BAFA2232BE63400FB8E22 /* CheckBoxWithLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckBoxWithLabel.swift; sourceTree = ""; }; + 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = ""; }; + 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxWithLabelView.swift; sourceTree = ""; }; 948DB67D2326DCD90011F916 /* MultiProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiProgress.swift; sourceTree = ""; }; B8200E142280C4CF007245F4 /* ProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBar.swift; sourceTree = ""; }; B8200E182281DC1A007245F4 /* CornerLabels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CornerLabels.swift; sourceTree = ""; }; @@ -726,8 +726,8 @@ DB891E822253FA8500022516 /* Label.swift */, 0198F7A02256A80A0066C936 /* MFRadioButton.h */, 0198F7A22256A80A0066C936 /* MFRadioButton.m */, - 0A7BAFA0232BE61800FB8E22 /* CheckBox.swift */, - 0A7BAFA2232BE63400FB8E22 /* CheckBoxWithLabel.swift */, + 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */, + 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */, ); path = Views; sourceTree = ""; @@ -1042,6 +1042,7 @@ D2D6CD4022E78C1A00D701B8 /* Scroller.swift in Sources */, D29DF13021E6851E003B2FB9 /* MVMCoreUITopAlertShortView.m in Sources */, D28B4F8B21FF967C00712C7A /* MVMCoreUIObject.m in Sources */, + 0A1B4A96233BB18F005B3FB4 /* CheckboxWithLabelView.swift in Sources */, D260D7B222D65BDD007E7233 /* MVMCoreUIPageControl.m in Sources */, D29DF26D21E6AA0B003B2FB9 /* FLAnimatedImageView.m in Sources */, D29DF2EF21ECEAE1003B2FB9 /* MFFonts.m in Sources */, @@ -1049,7 +1050,7 @@ D282AACB2243C61700C46919 /* ButtonView.swift in Sources */, D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */, 0105618F224BBE7700E1557D /* FormValidator+FormParams.swift in Sources */, - 0A7BAFA1232BE61800FB8E22 /* CheckBox.swift in Sources */, + 0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */, D22479962316AF6E003FCCF9 /* HeadlineBodyTextButton.swift in Sources */, D2E1FADD2268B25E00AEFD8C /* MoleculeTableViewCell.swift in Sources */, D29DF2AE21E7B3A4003B2FB9 /* MFTextView.m in Sources */, @@ -1077,7 +1078,6 @@ D22D1F47220496A30077CEC0 /* MVMCoreUISwitch.m in Sources */, D29DF28C21E7AC2B003B2FB9 /* ViewConstrainingView.m in Sources */, D29DF17B21E69E1F003B2FB9 /* PrimaryButton.m in Sources */, - 0A7BAFA3232BE63400FB8E22 /* CheckBoxWithLabel.swift in Sources */, D27CD4102339057800C1DC07 /* EyebrowHeadlineBodyLink.swift in Sources */, D29DF11D21E684A9003B2FB9 /* MVMCoreUISplitViewController.m in Sources */, 0198F79F225679880066C936 /* FormValidationProtocol.swift in Sources */, diff --git a/MVMCoreUI/Atoms/Views/CheckBox.swift b/MVMCoreUI/Atoms/Views/CheckBox.swift deleted file mode 100644 index ac6adf1a..00000000 --- a/MVMCoreUI/Atoms/Views/CheckBox.swift +++ /dev/null @@ -1,178 +0,0 @@ -// -// CheckBox.swift -// MVMCoreUI -// -// Created by Kevin Christiano on 9/13/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import MVMCore - - -class CheckBox: UIControl, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol { - //-------------------------------------------------- - // MARK: - Properties - //-------------------------------------------------- - - private var _lineColor: UIColor = .black - private var _lineWidth: CGFloat = 1.0 - - private var drawPercentage: Float = 0.0 - private var animationTimer: Timer? - private var checkLayer: CAShapeLayer? - - let startXOffset: Float = 42.0 / 124.0 - let startYOffset: Float = 66.0 / 124.0 - let pivotXOffset: Float = 58.0 / 124.0 - let pivotYOffset: Float = 80.0 / 124.0 - let endXOffset: Float = 83.0 / 124.0 - let endYOffset: Float = 46.0 / 124.0 - let pivotPercentage: Float = 0.34 - let endPercentage = 0.66 - let animationInterval: Float = 0.01 - - - //-------------------------------------------------- - // MARK: - Lifecycle - //-------------------------------------------------- - - override init(frame: CGRect) { - super.init(frame: frame) - - backgroundColor = .clear - drawPercentage = 1.0 - lineColor = .black - lineWidth = 1.0 - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - fatalError("Xib File is not implemented for CheckBox.") - } - - var lineWidth: CGFloat { - get { - return _lineWidth - } - set(lineWidth) { - self.lineWidth = lineWidth - guard let checkLayer = checkLayer else { return } - checkLayer.removeFromSuperlayer() - checkLayer = nil - updateCheckSelected(isSelected, animated: false) - } - } - - var lineColor: UIColor { - get { - return _lineColor - } - set(lineColor) { - _lineColor = lineColor - - if let checkLayer = checkLayer { - checkLayer.strokeColor = lineColor.cgColor - updateCheckSelected(isSelected, animated: false) - } - } - } - - //-------------------------------------------------- - // MARK: - Draw - //-------------------------------------------------- - - func drawCheck() { - - guard let checkLayer = checkLayer else { return } - - layoutIfNeeded() - let path = UIBezierPath() - path.move(to: CGPoint(x: lineWidth / 2, y: bounds.size.height * 0.55)) - path.addLine(to: CGPoint(x: bounds.size.width * 0.45, y: bounds.size.height * 0.85)) - path.addLine(to: CGPoint(x: bounds.size.width - lineWidth / 2, y: lineWidth / 2)) - - checkLayer = CAShapeLayer() - checkLayer.frame = bounds - layer.addSublayer(checkLayer) - checkLayer.strokeColor = lineColor.cgColor - checkLayer.fillColor = UIColor.clear.cgColor - checkLayer.path = path.cgPath - checkLayer.lineWidth = lineWidth - - CATransaction.begin() - CATransaction.setDisableActions(true) - checkLayer.strokeEnd = 0.0 - CATransaction.commit() - } - - //-------------------------------------------------- - // MARK: - Methods - //-------------------------------------------------- - - func updateCheckSelected(_ selected: Bool, animated: Bool) { - - DispatchQueue.main.async { - - self.isSelected = selected - - // animate this bar - self.drawCheck() - - var layer: CAShapeLayer? - if self.checkLayer?.presentation() != nil && animated { - layer = self.checkLayer!.presentation() - } else { - layer = self.checkLayer - } - - if animated { - let animateStrokeEnd = CABasicAnimation(keyPath: "strokeEnd") - animateStrokeEnd.fillMode = .both - animateStrokeEnd.isRemovedOnCompletion = false - animateStrokeEnd.duration = 0.3 - - animateStrokeEnd.fromValue = NSNumber(value: Float(layer?.strokeEnd ?? 0.0)) - - animateStrokeEnd.toValue = NSNumber(value: selected ? 1 : 0) - - animateStrokeEnd.timingFunction = CAMediaTimingFunction(name: .linear) - layer?.add(animateStrokeEnd, forKey: "strokeEndAnimation") - } else { - layer?.removeAllAnimations() - CATransaction.begin() - CATransaction.setDisableActions(true) - layer?.strokeEnd = selected ? 1.0 : 0.0 - - CATransaction.commit() - } - } - } - - override func layoutSubviews() { - drawCheck() - } - - private func defaultState() { - - } - - //-------------------------------------------------- - // MARK: - Molecular - //-------------------------------------------------- - - open func reset() { - - } - - open func setAsMolecule() { - - } - - func updateView(_ size: CGFloat) { - - } - - func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - - } -} diff --git a/MVMCoreUI/Atoms/Views/CheckBoxWithLabel.swift b/MVMCoreUI/Atoms/Views/CheckBoxWithLabel.swift deleted file mode 100644 index 972100d4..00000000 --- a/MVMCoreUI/Atoms/Views/CheckBoxWithLabel.swift +++ /dev/null @@ -1,277 +0,0 @@ -// -// CheckBoxWithLabel.swift -// MVMCoreUI -// -// Created by Kevin Christiano on 9/13/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - - -class CheckBoxWithLabel: ViewConstrainingView { - //-------------------------------------------------- - // MARK: - Properties - //-------------------------------------------------- - - weak var checkMark: CheckBox? - private(set) var isSelected = false - var sizeObject: MFSizeObject? - //?????? - var checkedColor: UIColor? - var unCheckedColor: UIColor? - // Label to the right of the check box. - weak var descriptionLabel: Label? - // Setter for the descriptionLabel.text. Also sets the accessibility text. - var descriptionText: String? - var descriptionAttributedText: NSAttributedString? - // A block that is called when the switch is selected. - var switchSelected: ((_ selected: Bool) -> ())? - - //-------------------------------------------------- - // MARK: - Life Cycle - //-------------------------------------------------- - - // TODO: MVMCoreUICheckBox.m - - func needsToBeConstrained() -> Bool { - return true - } - - func alignment() -> UIStackView.Alignment { - return .leading - } - - func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - - FormValidator.setupValidation(withMolecule: self, delegate: delegateObject?.formValidationProtocol) - delegate = delegateObject - fieldKey = json?.string(for: KeyFieldKey) - isRequired = json?.bool(forKey: KeyRequired) - - let checkedColorHex = json?.string("checkedColor") - let unCheckedColorHex = json?.string("unCheckedColor") - - let checkedColor = checkedColorHex != nil ? UIColor.mfGet(forHex: checkedColorHex) : UIColor.clear - let unCheckedColor = unCheckedColorHex != nil ? UIColor.mfGet(forHex: unCheckedColorHex) : UIColor.clear - - setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, label: json?.dict(KeyLabel), delegateObject: delegateObject, additionalData: additionalData) - } - - class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - return CGFloat(CheckBoxHeightWidth) - } - - // MARK: - convenient class methods - class func mf() -> Self { - let checkBox = self.init(frame: CGRect.zero) - checkBox.translatesAutoresizingMaskIntoConstraints = false - return checkBox - } - - class func mfCheckBoxWithRoundedRect() -> Self? { - let checkBox = self.init(roundRect: true) - checkBox.translatesAutoresizingMaskIntoConstraints = false - return checkBox - } - - class func mfCheckBox(withCheckedColor checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, text: String?) -> Self { - let checkBox = self.init(checkedColor: checkedColor, unCheck: unCheckedColor, text: text) - checkBox?.translatesAutoresizingMaskIntoConstraints = false - return checkBox - } - - class func mfCheckBox(withCheckedColor checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, atributedText attributedText: NSAttributedString?) -> Self { - let checkBox = self.init(checkedColor: checkedColor, unCheck: unCheckedColor, attributedText: attributedText) - checkBox?.translatesAutoresizingMaskIntoConstraints = false - return checkBox - } - - // MARK: - FormValidationProtocol - func isValidField() -> Bool { - if isRequired { - return isSelected() - } - return true - } - - func formFieldName() -> String? { - return fieldKey - } - - func formFieldValue() -> Any? { - return NSNumber(value: isSelected()) - } - - // MARK: - inits - init() { - super.init() - setupView() - } - - init(checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, text: String?) { - super.init() - setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: text) - addAccessibleProperties() - } - - init(checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, attributedText: NSAttributedString?) { - super.init() - setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: nil) - descriptionAttributedText = attributedText - addAccessibleProperties() - } - - init?(checkMarkView: MVMCoreUICheckMarkView?, checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, text: String?) { - super.init() - checkMark = checkMarkView - setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: text) - addAccessibleProperties() - } - - init?(checkMarkView: MVMCoreUICheckMarkView?, checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, attributedText: NSAttributedString?) { - super.init() - checkMark = checkMarkView - setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: nil) - descriptionAttributedText = attributedText - addAccessibleProperties() - } - - init(roundRect isRoundRect: Bool) { - super.init() - isRoundRectCheckMark = isRoundRect - setup(withCheckedColor: UIColor.white, unCheck: UIColor.white, text: nil) - addAccessibleProperties() - setCheckMarkLayer() - } - - //default inits - required init?(coder: NSCoder) { - super.init(coder: coder) - setupView() - setup(withCheckedColor: UIColor.white, unCheck: UIColor.white, text: nil) - addAccessibleProperties() - } - - init(frame: CGRect) { - super.init(frame: frame) - setupView() - setup(withCheckedColor: UIColor.white, unCheck: UIColor.white, text: nil) - addAccessibleProperties() - } - - func awakeFromNib() { - super.awakeFromNib() - setup(withCheckedColor: UIColor.white, unCheck: UIColor.white, text: nil) - } - - func setupView() { - - let containterView = MVMCoreUICommonViewsUtility.commonView() - containterView?.isUserInteractionEnabled = false - if !sizeObject { - sizeObject = MFSizeObject(standardSize: CheckBoxHeightWidth, standardiPadPortraitSize: Int(CheckBoxHeightWidth) + 6) - } - - //checked circle - if !self.checkedSquare { - let checkedSquare = MVMCoreUICommonViewsUtility.commonView() - checkedSquare?.backgroundColor = UIColor.white - if let checkedSquare = checkedSquare { - containterView?.addSubview(checkedSquare) - } - let size = sizeObject.getValueBasedOnApplicationWidth() - let constraints = NSLayoutConstraint.constraintPinView(checkedSquare, heightConstraint: true, heightConstant: size, widthConstraint: true, widthConstant: size) - checkboxWidth = constraints[ConstraintWidth] - checkboxHeight = constraints[ConstraintHeight] - NSLayoutConstraint.constraintPinSubview(checkedSquare, pinTop: true, pinBottom: true, pinLeft: true, pinRight: false) - - checkboxRightPinConstraint = checkedSquare?.trailingAnchor.constraintEqual(to: containterView?.trailingAnchor) - - NSLayoutConstraint.constraintPinSubview(checkedSquare, pinCenterX: false, pinCenterY: true) - self.checkedSquare = checkedSquare - self.checkBoxBorder = UIColor.black - } - - // TODO: OBJC CODE - - //check mark - if (!self.checkMark) { - MVMCoreUICheckMarkView *checkMark = [[MVMCoreUICheckMarkView alloc] initWithFrame:self.frame]; - checkMark.lineWidth = 2.0; - self.checkMark = checkMark; - self.checkMark.translatesAutoresizingMaskIntoConstraints = NO; - [self.checkedSquare addSubview:self.checkMark]; - [self.checkMark.widthAnchor constraintEqualToAnchor:self.checkedSquare.widthAnchor multiplier:.4].active = YES; - [self.checkMark.heightAnchor constraintEqualToAnchor:self.checkedSquare.heightAnchor multiplier:.4].active = YES; - [self.checkMark.centerXAnchor constraintEqualToAnchor:self.checkedSquare.centerXAnchor].active = YES; - [self.checkMark.centerYAnchor constraintEqualToAnchor:self.checkedSquare.centerYAnchor].active = YES; - } - - //label - if (!self.descriptionLabel) { - Label *descriptionLabel = [Label commonLabelB2:YES]; - [containterView addSubview:descriptionLabel]; - [NSLayoutConstraint constraintPinSubview:descriptionLabel pinCenterX:NO pinCenterY:YES]; - [NSLayoutConstraint constraintPinSubview:descriptionLabel pinTop:NO pinBottom:NO pinLeft:NO pinRight:YES]; - - self.descriptionLabelLeadingConstraint = [NSLayoutConstraint constraintWithItem:descriptionLabel attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.checkedSquare attribute:NSLayoutAttributeTrailing multiplier:1 constant:11]; - self.descriptionLabelLeadingConstraint.active = YES; - - self.descriptionLabelLeadingConstraint.active = YES; - self.descriptionLabel = descriptionLabel; - [self setSelected:NO]; - } - - if (!self.containerView) { - [self addSubview:containterView]; - self.containerView = containterView; - [NSLayoutConstraint constraintPinSubviewToSuperview:containterView]; - } - } - - func setup(withCheckedColor checkedColor: UIColor?, unCheck unCheckedColor: UIColor?) { - if checkedColor != nil { - self.checkedColor = checkedColor - } - if unCheckedColor != nil { - self.unCheckedColor = unCheckedColor - } - } - - func setup(withCheckedColor checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, text: String?) { - setup(withCheckedColor: checkedColor, unCheck: unCheckedColor) - descriptionText = text ?? "" - } - - func setup(withCheckedColor checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, label labelJson: [AnyHashable : Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable : Any]?) { - setup(withCheckedColor: checkedColor, unCheck: unCheckedColor) - descriptionLabel.setWithJSON(labelJson, delegateObject: delegateObject, additionalData: additionalData) - } - - @objc func updateView(_ size: CGFloat) { - MVMCoreDispatchUtility.performBlock(onMainThread: { - self.descriptionLabel.updateView(size) - if self.checkMark.responds(to: #selector(updateView(_:))) { - let width = self.sizeObject.getValueBased(onSize: size) - self.checkboxWidth.constant = width - self.checkboxHeight.constant = width - self.checkMark.updateView(size) - } - }) - } - - func setCheckMarkLayer() { - checkedSquare.layer.cornerRadius = isRoundRectCheckMark ? 5.0 : 0 - } - - //-------------------------------------------------- - // MARK: - Molecular - //-------------------------------------------------- - - override func updateView(_ size: CGFloat) { - - } - - override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { - - } -} diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift new file mode 100644 index 00000000..08f6227f --- /dev/null +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -0,0 +1,288 @@ +// +// Checkbox.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 9/13/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import MVMCore + + +@objcMembers open class Checkbox: UIControl, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public static let defaultHeightWidth: CGFloat = 18.0 + /* + //Offsets based on the 124x124 example checkmark + let startXOffset: Float = 42.0 / 124.0 + let startYOffset: Float = 66.0 / 124.0 + let pivotXOffset: Float = 58.0 / 124.0 + let pivotYOffset: Float = 80.0 / 124.0 + let endXOffset: Float = 83.0 / 124.0 + let endYOffset: Float = 46.0 / 124.0 + let pivotPercentage: Float = 0.34 + let endPercentage = 1.0 - pivotPercentage + let animationInterval: Float = 0.01 + */ + + public var checkedColor: UIColor = .black + public var unCheckedColor: UIColor = .mfPaleGrey() + + public var hasRoundBorder = false + + private var shapeLayer: CAShapeLayer? + private var _lineColor: UIColor = .black + private var _borderColor: UIColor = .black + private var _lineWidth: CGFloat = 1.0 + private var _cornerRadius: CGFloat = 5.0 + + public func setCheckMarkLayer() { + checkedSquare.layer.cornerRadius = checkbox.isRoundRectCheckMark ? 5.0 : 0 + } + + public var lineWidth: CGFloat { + get { return _lineWidth } + set (newLineWidth) { + _lineWidth = newLineWidth + if shapeLayer != nil { + shapeLayer?.removeFromSuperlayer() + shapeLayer = nil + updateCheckSelected(isSelected, animated: false) + } + } + } + + public var lineColor: UIColor { + get { return _lineColor } + set (newLineColor) { + _lineColor = newLineColor + if let shapeLayer = shapeLayer { + shapeLayer.strokeColor = lineColor.cgColor + updateCheckSelected(isSelected, animated: false) + } + } + } + + public var borderColor: UIColor { + get { return _borderColor } + set (newBorderColor) { + _borderColor = newBorderColor + if let shapeLayer = shapeLayer { + shapeLayer.strokeColor = borderColor.cgColor + updateCheckSelected(isSelected, animated: false) + } + } + } + + override open var isSelected: Bool { + didSet { + if isSelected { + layer.addSublayer(shapeLayer!) + shapeLayer?.strokeEnd = 1 + shapeLayer?.removeAllAnimations() + shapeLayer?.add(checkedAnimation, forKey: "strokeEnd") + } else { + shapeLayer?.strokeEnd = 0 + shapeLayer?.removeAllAnimations() + shapeLayer?.add(uncheckedAnimation, forKey: "strokeEnd") + } + } + } + + lazy private var checkedAnimation: CABasicAnimation = { + let check = CABasicAnimation(keyPath: "strokeEnd") + check.timingFunction = CAMediaTimingFunction(name: .linear) + check.fillMode = .both + check.duration = 0.33 + check.fromValue = 0 + check.toValue = 1 + return check + }() + + lazy private var uncheckedAnimation: CABasicAnimation = { + let unCheck = CABasicAnimation(keyPath: "strokeEnd") + unCheck.fillMode = .both + unCheck.duration = 0.33 + unCheck.fromValue = 0 + unCheck.toValue = 1 + return unCheck + }() + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + + override public init(frame: CGRect) { + super.init(frame: frame) + setupView() + } + + public convenience init() { + self.init(frame:.zero) + } + + /// There is currently no intention on using xib files. + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + fatalError("xib file is not implemented for CheckBox.") + } + + public convenience init(checkedColor: UIColor, uncheckColor: UIColor, isChecked: Bool) { + self.init(frame: .zero) + isSelected = isChecked + // TODO: define the rest.... + } + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + + override open func layoutSubviews() { + super.layoutSubviews() + drawCheck() + } + + public func setupView() { + + translatesAutoresizingMaskIntoConstraints = false + backgroundColor = .white + lineColor = .black + lineWidth = 1.0 + + let path = UIBezierPath() + path.move(to: CGPoint(x: lineWidth / 2, y: bounds.size.height * 0.55)) + path.addLine(to: CGPoint(x: bounds.size.width * 0.45, y: bounds.size.height * 0.85)) + path.addLine(to: CGPoint(x: bounds.size.width - lineWidth / 2, y: lineWidth / 2)) + + let shapeLayer = CAShapeLayer() + self.shapeLayer = shapeLayer + shapeLayer.frame = bounds + layer.addSublayer(shapeLayer) + shapeLayer.strokeColor = lineColor.cgColor + shapeLayer.fillColor = UIColor.clear.cgColor + shapeLayer.path = path.cgPath + shapeLayer.lineJoin = .bevel + shapeLayer.lineWidth = lineWidth + } + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + + private func drawCheck() { + + if shapeLayer == nil { + + layoutIfNeeded() + + let path = UIBezierPath() + path.move(to: CGPoint(x: lineWidth / 2, y: bounds.size.height * 0.55)) + path.addLine(to: CGPoint(x: bounds.size.width * 0.45, y: bounds.size.height * 0.85)) + path.addLine(to: CGPoint(x: bounds.size.width - lineWidth / 2, y: lineWidth / 2)) + + shapeLayer = CAShapeLayer() + shapeLayer?.frame = bounds + layer.addSublayer(shapeLayer!) + shapeLayer?.strokeColor = lineColor.cgColor + shapeLayer?.fillColor = UIColor.clear.cgColor + shapeLayer?.path = path.cgPath + shapeLayer?.lineJoin = .bevel + shapeLayer?.lineWidth = lineWidth + + CATransaction.begin() + CATransaction.setDisableActions(true) + shapeLayer?.strokeEnd = 0.0 + CATransaction.commit() + } + } + + public func updateCheckSelected(_ selected: Bool, animated: Bool) { + + DispatchQueue.main.async { + + self.isSelected = selected + + self.drawCheck() + + var layer: CAShapeLayer? + if let presentation = self.shapeLayer?.presentation(), animated { + layer = presentation + } else { + layer = self.shapeLayer + } + + if animated { + let animateStrokeEnd = CABasicAnimation(keyPath: "strokeEnd") + animateStrokeEnd.fillMode = .both + animateStrokeEnd.isRemovedOnCompletion = false + animateStrokeEnd.duration = 0.33 + animateStrokeEnd.fromValue = layer?.strokeEnd ?? 0.0 + animateStrokeEnd.toValue = selected ? 1 : 0 + animateStrokeEnd.timingFunction = CAMediaTimingFunction(name: .linear) + layer?.add(animateStrokeEnd, forKey: "strokeEndAnimation") + } else { + layer?.removeAllAnimations() + CATransaction.begin() + CATransaction.setDisableActions(true) + layer?.strokeEnd = selected ? 1 : 0 + CATransaction.commit() + } + } + } + + //-------------------------------------------------- + // MARK: - Molecular + //-------------------------------------------------- + + open func needsToBeConstrained() -> Bool { + return true + } + + open func reset() { + setupView() + } + + open func setAsMolecule() { + setupView() + } + + public func updateView(_ size: CGFloat) { + + } + + public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + + guard let dictionary = json else { return } + + if let borderColor = dictionary["borderColor"] as? String { + layer.borderColor = UIColor.mfGet(forHex: borderColor).cgColor + } + + if let checkColor = dictionary["lineColor"] as? String { + _lineColor = UIColor.mfGet(forHex: checkColor) + } + + if let checkColor = dictionary["checkedColor"] as? String { + checkedColor = UIColor.mfGet(forHex: checkColor) + } + + if let unCheckedColor = dictionary["unCheckedColor"] as? String { + uncheckedColor = UIColor.mfGet(forHex: unCheckedColor) + } + + if let backroundColor = dictionary["backroundColor"] as? String { + self.backgroundColor = UIColor.mfGet(forHex: backroundColor) + } + + if let borderWidth = dictionary["borderWidth"] as? CGFloat { + _lineWidth = borderWidth + } + + if let cornerRadius = dictionary["cornerRadius"] as? CGFloat { + _cornerRadius = cornerRadius + } + } +} diff --git a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift new file mode 100644 index 00000000..609937ee --- /dev/null +++ b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift @@ -0,0 +1,289 @@ +// +// CheckboxWithLabelView.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 9/13/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + + +@objcMembers open class CheckboxWithLabelView: ViewConstrainingView { + //-------------------------------------------------- + // MARK: - Outlets + //-------------------------------------------------- + + let checkbox = Checkbox() + let label = Label.commonLabelB2(true) + + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + var sizeObject: MFSizeObject? = MFSizeObject(standardSize: Checkbox.defaultHeightWidth, standardiPadPortraitSize: Checkbox.defaultHeightWidth + 6.0) + + var checkboxWidthConstraint: NSLayoutConstraint? + var checkboxHeightConstraint: NSLayoutConstraint? + + + // Setter for the descriptionLabel.text. Also sets the accessibility text. + var labelText: String? + var labelAttributedText: NSAttributedString? + + // A block that is called when the switch is selected. + public var checkBoxAction: ((_ selected: Bool) -> ())? + + func setDescriptionAttributedText(_ descriptionAttributedText: NSAttributedString?) { + descriptionLabel?.text = nil + descriptionLabel?.attributedText = descriptionAttributedText + self.valueForAccessibilityText = descriptionAttributedText?.string + } + + var descriptionText: String { + get { return super.descriptionText } + set(descriptionText) { + descriptionLabel?.attributedText = nil + descriptionLabel.text = descriptionText + self.valueForAccessibilityText = descriptionText + } + } + + var isRequired = false + var fieldKey: String? + var delegate: DelegateObject? + + + //-------------------------------------------------- + // MARK: - Life Cycle + //-------------------------------------------------- + + override open func setupView() { + super.setupView() + + guard subviews.isEmpty else { return } + + translatesAutoresizingMaskIntoConstraints = false + + addSubview(checkbox) + + let dimension = sizeObject?.getValueBasedOnApplicationWidth() ?? Checkbox.defaultHeightWidth + + checkboxWidthConstraint = checkbox.heightAnchor.constraint(equalToConstant: dimension) + checkboxWidthConstraint?.isActive = true + checkboxHeightConstraint = checkbox.widthAnchor.constraint(equalToConstant: dimension) + checkboxHeightConstraint?.isActive = true + + NSLayoutConstraint.constraintPinSubview(checkbox, pinTop: true, pinBottom: true, pinLeft: true, pinRight: false) + checkbox.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true + checkbox.lineWidth = 2.0 + addSubview(label) + + NSLayoutConstraint.constraintPinSubview(label, pinCenterX: false, pinCenterY: true) + NSLayoutConstraint.constraintPinSubview(label, pinTop: false, pinBottom: false, pinLeft: false, pinRight: true) + + label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo).isActive = true + } + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + + //default inits + required public init?(coder: NSCoder) { + super.init(coder: coder) + fatalError("xib file is not implemented for CheckboxWithLabelView") + } + + override init(frame: CGRect) { + super.init(frame: frame) + setupView() + addAccessibleProperties() + } + + convenience init() { + self.init(frame: .zero) + } + + init(checkedColor: UIColor?, unCheckedColor: UIColor?, text: String?) { + super.init(frame: .zero) + checkbox.checkedColor = checkedColor + checkbox.unCheckedColor = unCheckedColor + label.text = text + addAccessibleProperties() + } + + init(checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, attributedText: NSAttributedString?) { + super.init(frame: .zero) + setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: nil) + descriptionAttributedText = attributedText + addAccessibleProperties() + } + + init?(checkMarkView: MVMCoreUICheckMarkView?, checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, text: String?) { + super.init(frame: .zero) + checkMark = checkMarkView + setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: text) + addAccessibleProperties() + } + + init?(checkMarkView: MVMCoreUICheckMarkView?, checkedColor: UIColor?, unCheck unCheckedColor: UIColor?, attributedText: NSAttributedString?) { + super.init(frame: .zero) + checkMark = checkMarkView + setup(withCheckedColor: checkedColor, unCheck: unCheckedColor, text: nil) + descriptionAttributedText = attributedText + addAccessibleProperties() + } + + init(roundRect isRoundRect: Bool) { + super.init(frame: .zero) + + isRoundRectCheckMark = isRoundRect + setup(withCheckedColor: .white, unCheck: .white, text: nil) + addAccessibleProperties() + setCheckMarkLayer() + } + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + + override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + return CGFloat(CheckBoxHeightWidth) + } + + // MARK: - control methods + func setSelected(_ selected: Bool) { + setSelected(selected, animated: true) + checkbox?.is + } + + func setSelected(_ selected: Bool, animated: Bool) { + setSelected(selected, animated: animated, runBlock: true) + } + + func setSelected(_ selected: Bool, animated: Bool, runBlock: Bool) { + addAccessibilityLabel(selected) + + isSelected = selected + if (switchSelected != nil) && runBlock { + switchSelected(selected) + } + + if selected { + UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { + self.checkedSquare.backgroundColor = self.checkedColor + }) + checkMark.updateCheckSelected(true, animated: animated) + } else { + UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: { + self.checkedSquare.backgroundColor = self.unCheckedColor + }) + checkMark?.updateCheckSelected(false, animated: animated) + } + + if delegate && delegate.responds(to: #selector(formValidationProtocol)) && delegate.perform(#selector(formValidationProtocol)).responds(to: #selector(Unmanaged.formValidatorModel)) { + let formValidator = delegate.perform(#selector(formValidationProtocol)).perform(#selector(Unmanaged.formValidatorModel)) as? FormValidator + formValidator?.enableByValidation() + } + } + + //-------------------------------------------------- + // MARK: - UITouch + //-------------------------------------------------- + + func touchesEnded(_ touches: Set, with event: UIEvent) { + + if touchIsOutside(touches.first) { + sendActions(for: .touchUpOutside) + } else { + self.selected = !isSelected() + sendActions(for: .touchUpInside) + } + } + + func touchIsOutside(_ touch: UITouch?) -> Bool { + let endLocation = touch?.location(in: self) + let x = endLocation?.x ?? 0.0 + let y = endLocation?.y ?? 0.0 + let faultTolerance: CGFloat = 20.0 + let widthLimit = CGFloat(frame.size.width + faultTolerance) + let heightLimt = CGFloat(frame.size.height + faultTolerance) + + return x < -faultTolerance || y < -faultTolerance || x > widthLimit || y > heightLimt + } +} + +// MARK: - Molecular +extension CheckboxWithLabelView { + + @objc override open func updateView(_ size: CGFloat) { + + DispatchQueue.main.async { + self.label.updateView(size) + if self.checkbox.responds(to: #selector(self.updateView(_:))) { + if let dimension = self.sizeObject?.getValueBased(onSize: size) { + self.checkboxWidthConstraint?.constant = dimension + self.checkboxHeightConstraint?.constant = dimension + self.checkbox.updateView(size) + } + } + } + } + + override open func alignment() -> UIStackView.Alignment { + return .leading + } + + override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + delegate = delegateObject + FormValidator.setupValidation(molecule: self, delegate: delegateObject?.formValidationProtocol) + + guard let dictionary = json else { return } + + fieldKey = dictionary.string(for: KeyFieldKey) + isRequired = dictionary.bool(forKey: KeyRequired) + + checkbox.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + label.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + } +} + +// MARK:- FormValidationProtocol +extension CheckboxWithLabelView: FormValidationProtocol { + + public func isValidField() -> Bool { + return isRequired ? checkbox.isSelected : true + } + + public func formFieldName() -> String? { + return fieldKey + } + + public func formFieldValue() -> Any? { + return NSNumber(value: checkbox.isSelected) + } + + +} + +// MARK:- Accessibility +extension CheckboxWithLabelView { + + func addAccessibleProperties() { + accessibilityTraits = .none + accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint") + } + + func addAccessibilityLabel(_ selected: Bool) { + let state = selected ? MVMCoreUIUtility.hardcodedString(withKey: "checkbox_checked_state") : MVMCoreUIUtility.hardcodedString(withKey: "checkbox_unchecked_state") + let description = accessibilityText.length ? accessibilityText : "" + accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state"), description, state) + } + + func hideDescriptionLabelAndPinCheckboxToRight() { + + descriptionLabel?.hidden = true + checkboxRightPinConstraint.active = true + descriptionLabelLeadingConstraint.constant = 0 + } +} diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 945eb7bf..aeaf6b8d 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -38,11 +38,11 @@ @"caretButton": CaretButton.class, @"textField" : MFTextField.class, @"digitTextField" : MFDigitTextField.class, - @"checkbox" : MVMCoreUICheckBox.class, + @"checkbox" : Checkbox.class, @"cornerLabels" : CornerLabels.class, @"progressBar": ProgressBar.class, @"multiProgressBar": MultiProgress.class, - @"checkbox": MVMCoreUICheckBox.class, + @"checkboxWithLabelView": CheckboxWithLabelView.class, @"listItem": MoleculeTableViewCell.class, @"accordionListItem": AccordionMoleculeTableViewCell.class, @"switch": MVMCoreUISwitch.class,