diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 72b96ff3..db24662f 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -780,9 +780,9 @@ D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */, 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */, 017BEB372360C6AC0024EF95 /* RadioButtonLabel.swift */, + D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */, 017BEB47236230DB0024EF95 /* MoleculeViewProtocol.swift */, 017BEB49236235BA0024EF95 /* ModelMoleculeViewProtocol.swift */, - D2FB151A23A2B65B00C20E10 /* MoleculeContainer.swift */, ); path = Molecules; sourceTree = ""; diff --git a/MVMCoreUI/Atoms/Views/Checkbox.swift b/MVMCoreUI/Atoms/Views/Checkbox.swift index 370f5826..a206953a 100644 --- a/MVMCoreUI/Atoms/Views/Checkbox.swift +++ b/MVMCoreUI/Atoms/Views/Checkbox.swift @@ -11,11 +11,13 @@ import MVMCore /** This class expects its height and width to be equal. */ -@objcMembers open class Checkbox: UIControl, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol { +@objcMembers open class Checkbox: Control, MVMCoreUIViewConstrainingProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- + public var sizeObject: MFSizeObject? = MFSizeObject(standardSize: Checkbox.defaultHeightWidth, standardiPadPortraitSize: Checkbox.defaultHeightWidth + 6.0) + // Form Validation var isRequired = false var fieldKey: String? @@ -117,8 +119,8 @@ import MVMCore // MARK: - Constraints //-------------------------------------------------- - private var heightConstraint: NSLayoutConstraint? - private var widthConstraint: NSLayoutConstraint? + public var heightConstraint: NSLayoutConstraint? + public var widthConstraint: NSLayoutConstraint? /// Updates the height and width anchors of the Checkbox with the assigned value. public var heigthWidthConstant: CGFloat = Checkbox.defaultHeightWidth { @@ -138,7 +140,6 @@ import MVMCore accessibilityTraits = .button accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint") updateAccessibilityLabel() - setupView() } /// There is currently no intention on using xib files. @@ -147,7 +148,7 @@ import MVMCore fatalError("xib file is not implemented for Checkbox.") } - public convenience init() { + public convenience override init() { self.init(frame:.zero) } @@ -180,7 +181,8 @@ import MVMCore layer.borderColor = borderColor.cgColor } - open func setupView() { + open override func setupView() { + super.setupView() guard constraints.isEmpty else { return } @@ -349,18 +351,7 @@ import MVMCore open override func touchesEnded(_ touches: Set, with event: UIEvent?) { - sendActions(for: touchIsAcceptablyOutside(touches.first) ? .touchUpOutside : .touchUpInside) - } - - func touchIsAcceptablyOutside(_ 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(bounds.size.width + faultTolerance) - let heightLimt = CGFloat(bounds.size.height + faultTolerance) - - return x < -faultTolerance || y < -faultTolerance || x > widthLimit || y > heightLimt + sendActions(for: .touchUpInside) } override open func accessibilityActivate() -> Bool { @@ -376,7 +367,8 @@ import MVMCore return true } - open func reset() { + open override func reset() { + super.reset() isEnabled(true) shapeLayer?.removeAllAnimations() @@ -396,17 +388,24 @@ import MVMCore setupView() } - public func updateView(_ size: CGFloat) { + public override func updateView(_ size: CGFloat) { + super.updateView(size) + + if let dimension = sizeObject?.getValueBased(onSize: size) { + widthConstraint?.constant = dimension + heightConstraint?.constant = dimension + } layoutIfNeeded() } - public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + public override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) self.delegateObject = delegateObject FormValidator.setupValidation(molecule: self, delegate: delegateObject?.formValidationProtocol) guard let dictionary = json else { return } - + groupName = dictionary.optionalStringForKey("groupName") fieldValue = dictionary.optionalStringForKey("value") if let fieldKey = dictionary[KeyFieldKey] as? String { @@ -463,7 +462,7 @@ import MVMCore // MARK:- FormValidationProtocol extension Checkbox: FormValidationFormFieldProtocol { - + public func formFieldGroupName() -> String? { return groupName } diff --git a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift index 7ce1104e..6991adef 100644 --- a/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift +++ b/MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift @@ -7,7 +7,7 @@ // -@objcMembers open class CheckboxWithLabelView: ViewConstrainingView { +@objcMembers open class CheckboxWithLabelView: View { //-------------------------------------------------- // MARK: - Outlets //-------------------------------------------------- @@ -19,9 +19,7 @@ // MARK: - Properties //-------------------------------------------------- - var sizeObject: MFSizeObject? = MFSizeObject(standardSize: Checkbox.defaultHeightWidth, standardiPadPortraitSize: Checkbox.defaultHeightWidth + 6.0) - - var checkboxPosition: CheckboxPosition = .center + public var checkboxPosition: CheckboxPosition = .center public enum CheckboxPosition: String { case center @@ -32,13 +30,10 @@ //-------------------------------------------------- // MARK: - Constraints //-------------------------------------------------- - - var checkboxWidthConstraint: NSLayoutConstraint? - var checkboxHeightConstraint: NSLayoutConstraint? - var checkboxTopConstraint: NSLayoutConstraint? - var checkboxBottomConstraint: NSLayoutConstraint? - var checkboxCenterYConstraint: NSLayoutConstraint? - var centerLabelCheckboxConstraint: NSLayoutConstraint? + + public var checkboxTopConstraint: NSLayoutConstraint? + public var checkboxBottomConstraint: NSLayoutConstraint? + public var checkboxCenterYConstraint: NSLayoutConstraint? //-------------------------------------------------- // MARK: - Life Cycle @@ -49,32 +44,20 @@ guard subviews.isEmpty else { return } - translatesAutoresizingMaskIntoConstraints = false - addSubview(checkbox) addSubview(label) label.text = "" - let dimension = sizeObject?.getValueBasedOnApplicationWidth() ?? Checkbox.defaultHeightWidth - checkboxWidthConstraint = checkbox.heightAnchor.constraint(equalToConstant: dimension) - checkboxWidthConstraint?.isActive = true - checkboxHeightConstraint = checkbox.widthAnchor.constraint(equalToConstant: dimension) - checkboxHeightConstraint?.isActive = true - checkbox.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true - checkbox.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor).isActive = true - layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: checkbox.bottomAnchor).isActive = true - let checboxBottom = checkbox.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor) - checboxBottom.priority = UILayoutPriority(249) - checboxBottom.isActive = true - - // Allows various positions of checkbox. checkboxBottomConstraint = layoutMarginsGuide.bottomAnchor.constraint(equalTo: checkbox.bottomAnchor) + checkboxBottomConstraint?.isActive = true + checkboxTopConstraint = checkbox.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor) + checkboxTopConstraint?.isActive = true + checkboxCenterYConstraint = checkbox.centerYAnchor.constraint(equalTo: centerYAnchor) - centerLabelCheckboxConstraint = label.centerYAnchor.constraint(equalTo: checkbox.centerYAnchor) label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor).isActive = true @@ -82,7 +65,7 @@ layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor).isActive = true let bottomLabelConstraint = layoutMarginsGuide.bottomAnchor.constraint(equalTo: label.bottomAnchor) - bottomLabelConstraint.priority = UILayoutPriority(249) + bottomLabelConstraint.priority = .defaultLow bottomLabelConstraint.isActive = true alignCheckbox(.center) @@ -92,17 +75,12 @@ // MARK: - Initializers //-------------------------------------------------- - required public init?(coder: NSCoder) { - super.init(coder: coder) - fatalError("xib file is not implemented for CheckboxWithLabelView") - } - - override public init(frame: CGRect) { + public override init(frame: CGRect) { super.init(frame: frame) setupView() } - public convenience init() { + public convenience override init() { self.init(frame: .zero) } @@ -112,6 +90,11 @@ alignCheckbox(position) } + required public init?(coder: NSCoder) { + super.init(coder: coder) + fatalError("xib file is not implemented for CheckboxWithLabelView") + } + //-------------------------------------------------- // MARK: - Methods //-------------------------------------------------- @@ -125,64 +108,41 @@ checkboxBottomConstraint?.isActive = false checkboxTopConstraint?.isActive = false checkboxCenterYConstraint?.isActive = true - centerLabelCheckboxConstraint?.isActive = true case .top: checkboxBottomConstraint?.isActive = false checkboxTopConstraint?.isActive = true checkboxCenterYConstraint?.isActive = false - centerLabelCheckboxConstraint?.isActive = false case .bottom: checkboxBottomConstraint?.isActive = true checkboxTopConstraint?.isActive = false checkboxCenterYConstraint?.isActive = false - centerLabelCheckboxConstraint?.isActive = false } } } -/// MARK: - Molecular +// MARK: - Molecular extension CheckboxWithLabelView { - override open class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - return CGFloat(Checkbox.defaultHeightWidth) + open class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + return 200 } @objc override open func updateView(_ size: CGFloat) { super.updateView(size) label.updateView(size) - - if self.checkbox.responds(to: #selector(self.updateView(_:))) { - if let dimension = sizeObject?.getValueBased(onSize: size) { - checkboxWidthConstraint?.constant = dimension - checkboxHeightConstraint?.constant = dimension - checkbox.updateView(size) - } - } - + checkbox.updateView(size) layoutIfNeeded() } - override open func horizontalAlignment() -> UIStackView.Alignment { - return .leading - } - - open override func resetConstraints() { - super.resetConstraints() - - checkboxCenterYConstraint?.isActive = false - checkboxBottomConstraint?.isActive = false - checkboxTopConstraint?.isActive = false - centerLabelCheckboxConstraint?.isActive = false - } - open override func reset() { super.reset() label.text = "" checkbox.reset() + alignCheckbox(.center) } override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { diff --git a/MVMCoreUI/BaseClasses/Control.swift b/MVMCoreUI/BaseClasses/Control.swift index fdf8204f..5774ff5a 100644 --- a/MVMCoreUI/BaseClasses/Control.swift +++ b/MVMCoreUI/BaseClasses/Control.swift @@ -8,37 +8,64 @@ import UIKit -public class Control: UIControl { - var json: [AnyHashable: Any]? +@objcMembers open class Control: UIControl { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public var json: [AnyHashable: Any]? private var initialSetupPerformed = false + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + public override init(frame: CGRect) { super.init(frame: .zero) initialSetup() } - init() { + public init() { super.init(frame: .zero) initialSetup() } public required init?(coder: NSCoder) { super.init(coder: coder) - initialSetup() + fatalError("Control does not support xib.") } + //-------------------------------------------------- + // MARK: - Setup + //-------------------------------------------------- + public func initialSetup() { + if !initialSetupPerformed { initialSetupPerformed = true setupView() } } + + //-------------------------------------------------- + // MARK: - UITouch + //-------------------------------------------------- + + override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + + // If the control is smaller than 44pt by width or height, this will compensate. + let faultToleranceX: CGFloat = max((MinimumTappableArea - bounds.size.width) / 2.0, 0) + let faultToleranceY: CGFloat = max((MinimumTappableArea - bounds.size.height) / 2.0, 0) + let area = bounds.insetBy(dx: -faultToleranceX, dy: -faultToleranceY) + return area.contains(point) + } } +// MARK: - MVMCoreViewProtocol extension Control: MVMCoreViewProtocol { - public func updateView(_ size: CGFloat) { - } + + public func updateView(_ size: CGFloat) {} /// Will be called only once. public func setupView() { @@ -47,8 +74,9 @@ 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) { diff --git a/MVMCoreUI/Molecules/ModuleMolecule.swift b/MVMCoreUI/Molecules/ModuleMolecule.swift index 3c2277c9..51d9a53a 100644 --- a/MVMCoreUI/Molecules/ModuleMolecule.swift +++ b/MVMCoreUI/Molecules/ModuleMolecule.swift @@ -53,6 +53,9 @@ open class ModuleMoleculeModel: ContainerModelProtocol { open class ModuleMolecule: Container { var moduleMoleculeModel: ModuleMoleculeModel? { get { return model as? ModuleMoleculeModel } + public override func setupView() { + super.setupView() + containerModel = ModuleMoleculeModel() } open override func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [String : AnyHashable]?) { diff --git a/MVMCoreUI/Molecules/StandardHeaderView.swift b/MVMCoreUI/Molecules/StandardHeaderView.swift index a1264d81..cf44ef6a 100644 --- a/MVMCoreUI/Molecules/StandardHeaderView.swift +++ b/MVMCoreUI/Molecules/StandardHeaderView.swift @@ -51,6 +51,26 @@ public class StandardHeaderView: MoleculeContainer { line?.setWithJSON(seperatorModel.toJSON(), delegateObject: delegateObject, additionalData: additionalData) } } + +// open func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [String : AnyHashable]?) { +// //TODO: Need to create setWithModel in ViewConstraining View +// +// #warning("This below call should be repaced with super.setWithModel once we get rid of ViewConstrainingView.") +// //TODO: This below call should be repaced with super.setWithModel once we get rid of ViewConstrainingView. +// setUpWithModel(model, delegateObject, additionalData) +// +// // This molecule will by default handle margins. +// (molecule as? MVMCoreUIViewConstrainingProtocol)?.shouldSetHorizontalMargins?(false) +// (molecule as? MVMCoreUIViewConstrainingProtocol)?.shouldSetVerticalMargins?(false) +// +// guard let headerModel = model as? HeaderModel else { +// return +// } +// +// if let seperatorModel = headerModel.seperator as? LineModel { +// line?.setWithJSON(seperatorModel.toJSON(), delegateObject: delegateObject, additionalData: additionalData) +// } +// } open override func reset() { super.reset() diff --git a/MVMCoreUI/Utility/MVMCoreUIConstants.h b/MVMCoreUI/Utility/MVMCoreUIConstants.h index 778ede4f..e5587745 100644 --- a/MVMCoreUI/Utility/MVMCoreUIConstants.h +++ b/MVMCoreUI/Utility/MVMCoreUIConstants.h @@ -77,3 +77,7 @@ typedef NS_ENUM(NSInteger, CoreUIErrorCode) { ErrorCodeModuleMolecule = 100, ErrorCodeListMolecule = 101 }; + +#pragma mark - Apple Design Guidelines + +extern CGFloat const MinimumTappableArea; diff --git a/MVMCoreUI/Utility/MVMCoreUIConstants.m b/MVMCoreUI/Utility/MVMCoreUIConstants.m index 26636266..6211cfee 100644 --- a/MVMCoreUI/Utility/MVMCoreUIConstants.m +++ b/MVMCoreUI/Utility/MVMCoreUIConstants.m @@ -69,3 +69,7 @@ BOOL DisableAnimations = NO; // Hand Scroll Key NSString * const KeyHandScrollAnimation = @"handScrollAnimation"; NSString * const KeyHandScroll = @"hand_scroll"; + +#pragma mark - Apple Design Guidelines + +CGFloat const MinimumTappableArea = 44.0f;