From 8da88c4c87719c0f697e411eecbf943277cb4788 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Thu, 25 Apr 2019 11:41:06 -0400 Subject: [PATCH 01/45] Moving interaction functionality to label. --- MVMCoreUI/Atoms/Views/Label.swift | 74 ++++++++++- .../Atoms/Views/LabelWithInternalButton.swift | 125 +++++------------- 2 files changed, 106 insertions(+), 93 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 8ca484e3..dc3b4597 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -9,12 +9,19 @@ import MVMCore +public typealias ActionBlock = () -> Void @objc open class Label: UILabel, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol { //------------------------------------------------------ // MARK: - Properties //------------------------------------------------------ + var actionBlock: ActionBlock? + var actionText: String? + var frontText: String? + + var makeWholeViewClickable = false + // Set this property if you want updateView to update the font based on this standard and the size passed in. @objc public var standardFontSize: CGFloat = 0.0 @@ -303,6 +310,71 @@ import MVMCore } } +// MARK: - UIControl Override extension Label { - + + override open func touchesEnded(_ touches: Set, with event: UIEvent?) { + + if areTouches(inActionString: touches) { + if let action = actionBlock { + action() + } + } + } + + private func areTouches(inActionString touches: Set?) -> Bool { + + if UIAccessibility.isVoiceOverRunning || makeWholeViewClickable { + return true + } + + let location: CGPoint? = touches?.first?.location(in: self) + let index: Int = NSRange(location: frontText?.count ?? 0, length: actionText?.count ?? 0).location + let rangeArray = getRangeArrayOfWords(in: actionText, withInitalIndex: index) + let rectArray = getRectArray(from: rangeArray) as? [NSValue] ?? [] + + for rect in rectArray { + let wordRect: CGRect = rect.cgRectValue + + if let position = location, wordRect.contains(position) { + return true + + } else if wordRect.origin == .zero && wordRect.size.height == 0 && wordRect.size.width == 0 { + // Incase word rect is not found for any reason, make the whole label to be clicable to avoid non functioning link in production. + return true + } + } + + return false + } + + private func getRangeArrayOfWords(in string: String?, withInitalIndex index: Int) -> [Any]? { + + var index = index + let words = string?.components(separatedBy: " ") ?? [] + var rangeArray = [AnyHashable]() + + for word in words { + let finalWord = word + " " + let length: Int = finalWord.count + let wordRange = NSRange(location: index, length: length) + let rangeValue = NSValue(range: wordRange) + rangeArray.append(rangeValue) + index += length + } + + return rangeArray + } + + private func getRectArray(from rangeArray: [Any]?) -> [Any]? { + + var rectArray = [AnyHashable]() + + for range in rangeArray as? [NSValue] ?? [] { + let rectValue = NSValue(cgRect: boundingRect(forCharacterRange: range.rangeValue)) + rectArray.append(rectValue) + } + + return rectArray + } } diff --git a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift index 071e0242..883819e3 100644 --- a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift +++ b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift @@ -9,7 +9,6 @@ import MVMCore -public typealias ActionBlock = () -> Void private typealias ActionableStringTuple = (front: String?, action: String?, end: String?) public typealias ActionObjectDelegate = NSObjectProtocol & MVMCoreActionDelegateProtocol public typealias ButtonObjectDelegate = NSObjectProtocol & ButtonDelegateProtocol @@ -21,7 +20,6 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt // MARK: - Properties //------------------------------------------------------ - public var actionBlock: ActionBlock? public weak var label: Label? public var attributedText: NSAttributedString? { @@ -66,7 +64,11 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt return NSRange(location: frontText?.count ?? 0, length: actionText?.count ?? 0) } - public var makeWholeViewClickable = false + public var makeWholeViewClickable = false { + willSet(newBool) { + label?.makeWholeViewClickable = newBool + } + } override open var isEnabled: Bool { didSet { @@ -74,10 +76,25 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt } } - public var frontText: String? - public var actionText: String? + public var frontText: String? { + willSet(newFrontText) { + label?.frontText = newFrontText + } + } + public var actionText: String? { + willSet(newActionText) { + label?.actionText = newActionText + } + } + public var backText: String? + public var actionBlock: ActionBlock? { + willSet(newActionBlock) { + label?.actionBlock = newActionBlock + } + } + private var text: String? { willSet(newText) { attributedText = NSAttributedString(string: newText ?? "") @@ -169,10 +186,9 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt private func setup() { if self.label == nil { - let label = Label(frame: CGRect.zero) + let label = Label(frame: .zero) - backgroundColor = .clear - label.isUserInteractionEnabled = false + label.isUserInteractionEnabled = true label.setContentCompressionResistancePriority(.required, for: .vertical) addSubview(label) NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[label]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["label": label])) @@ -237,10 +253,8 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt } if let b2Font = MFStyler.fontB2(), - let actions = actionMap, - actions.keys.count > 0, - let actionString = actions.optionalStringForKey(KeyTitle), - !actionString.isEmpty { + let actions = actionMap, actions.keys.count > 0, + let actionString = actions.optionalStringForKey(KeyTitle), !actionString.isEmpty { let actionStringOnLine = actionString + (addNewLine ? "\n" : " ") actionText = actionStringOnLine @@ -270,50 +284,13 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt } //------------------------------------------------------ - // MARK: - UIControl Override + // MARK: - UIControl //------------------------------------------------------ - override open func touchesEnded(_ touches: Set, with event: UIEvent?) { - - if areTouches(inActionString: touches) { - sendActions(for: .touchUpInside) - if let action = actionBlock { - action() - } - } else { - sendActions(for: .touchUpOutside) - } - } - - private func areTouches(inActionString touches: Set?) -> Bool { - - if UIAccessibility.isVoiceOverRunning || makeWholeViewClickable { - return true - } - - let location: CGPoint? = touches?.first?.location(in: label) - let actionString = actionText - let index: Int = actionRange.location - let rangeArray = getRangeArrayOfWords(in: actionString, withInitalIndex: index) - let rectArray = getRectArray(fromRangeArray: rangeArray) - var result = false - - for aValueOfRect in rectArray as? [NSValue] ?? [] { - let wordRect: CGRect = aValueOfRect.cgRectValue - - if let position = location, wordRect.contains(position) { - result = true - break - } else if wordRect.origin.x == 0 && wordRect.origin.y == 0 && wordRect.size.height == 0 && wordRect.size.width == 0 { - // Incase word rect is not found for any reason, make the whole label to be clicable to avoid non functioning link in production. - result = true - break - } - } - - return result - } - +// override open func sendAction(_ action: Selector, to target: Any?, for event: UIEvent?) { +// <#code#> +// } + //------------------------------------------------------ // MARK: - Helper //------------------------------------------------------ @@ -331,40 +308,6 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt return "\(frontText ?? "")\(actionText ?? "")\(backText ?? "")" } - private func getRangeArrayOfWords(in string: String?, withInitalIndex index: Int) -> [Any]? { - - var index = index - let words = string?.components(separatedBy: " ") - var rangeArray = [AnyHashable]() - - for subString in words ?? [] { - let finalSubString = subString + " " - let wordIndex: Int = index - let length: Int = finalSubString.count - let subStringRange = NSRange(location: wordIndex, length: length) - let rangeValue = NSValue(range: subStringRange) - rangeArray.append(rangeValue) - index += length - } - - return rangeArray - } - - private func getRectArray(fromRangeArray rangeArray: [Any]?) -> [Any]? { - - var rectArray = [AnyHashable]() - - for aValueOfRange in rangeArray as? [NSValue] ?? [] { - let wordRange: NSRange = aValueOfRange.rangeValue - if let rect: CGRect = label?.boundingRect(forCharacterRange: wordRange) { - let rectValue = NSValue(cgRect: rect) - rectArray.append(rectValue) - } - } - - return rectArray - } - @objc public func setCurlyBracedText(_ text: String) { setText(text, startTag: "{", endTag: "}") @@ -375,10 +318,8 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt let actionableTuple: ActionableStringTuple = rangeOfText(text, startTag: startTag, endTag: endTag) if let text = text, - let leftTag = startTag, - text.contains(leftTag), - let rightTag = endTag, - text.contains(rightTag), + let leftTag = startTag, text.contains(leftTag), + let rightTag = endTag, text.contains(rightTag), let front = actionableTuple.front, let middle = actionableTuple.action, let end = actionableTuple.end { From f6f2cd2068894da0cf8d4cf9ffe644adaecccb33 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Mon, 29 Apr 2019 16:14:13 -0400 Subject: [PATCH 02/45] Carry over caret fixes. --- MVMCoreUI/Atoms/Buttons/CaretButton.swift | 26 ++++++++++------------- MVMCoreUI/Atoms/Views/CaretView.swift | 14 ++++++------ 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/MVMCoreUI/Atoms/Buttons/CaretButton.swift b/MVMCoreUI/Atoms/Buttons/CaretButton.swift index 6aa4a754..5a179c8c 100644 --- a/MVMCoreUI/Atoms/Buttons/CaretButton.swift +++ b/MVMCoreUI/Atoms/Buttons/CaretButton.swift @@ -13,8 +13,9 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol { // MARK: - Constants //------------------------------------------------------ - private let CaretViewHeight: Float = 10.8 - private let CaretViewWidth: Float = 6.6 + private let CARET_VIEW_HEIGHT: Float = 10.5 + private let CARET_VIEW_WIDTH: Float = 6.5 + //------------------------------------------------------ // MARK: - Properties @@ -25,17 +26,11 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol { @objc public var rightViewWidth: NSNumber? @objc public var enabledColor: UIColor = .black { - didSet { - setTitleColor(enabledColor, for: .normal) - changeCaretColor() - } + didSet { changeCaretColor() } } @objc public var disabledColor: UIColor = .mfSilver() { - didSet { - setTitleColor(disabledColor, for: .disabled) - changeCaretColor() - } + didSet { changeCaretColor() } } private var caretSpacingConstraint: NSLayoutConstraint? @@ -61,10 +56,11 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol { //------------------------------------------------------ private func changeCaretColor() { - + setTitleColor(enabledColor, for: .normal) + setTitleColor(disabledColor, for: .disabled) + if let rightCaretView = rightView as? CaretView { - let lineColor = isEnabled ? enabledColor : disabledColor - rightCaretView.setLineColor(lineColor) + rightCaretView.setLineColor(isEnabled ? enabledColor : disabledColor) } } @@ -72,8 +68,8 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol { rightView?.removeFromSuperview() - let width = CGFloat(rightViewWidth?.floatValue ?? CaretViewWidth) - let height = CGFloat(rightViewHeight?.floatValue ?? CaretViewHeight) + let width = CGFloat(rightViewWidth?.floatValue ?? CARET_VIEW_WIDTH) + let height = CGFloat(rightViewHeight?.floatValue ?? CARET_VIEW_HEIGHT) let edgeInsets: UIEdgeInsets = contentEdgeInsets contentEdgeInsets = UIEdgeInsets(top: edgeInsets.top, left: edgeInsets.left, bottom: edgeInsets.bottom, right: 4 + width) diff --git a/MVMCoreUI/Atoms/Views/CaretView.swift b/MVMCoreUI/Atoms/Views/CaretView.swift index eb3a4c4b..51ab783a 100644 --- a/MVMCoreUI/Atoms/Views/CaretView.swift +++ b/MVMCoreUI/Atoms/Views/CaretView.swift @@ -21,7 +21,7 @@ open class CaretView: MFView { //------------------------------------------------------ @objc public init() { - super.init(frame: CGRect.zero) + super.init(frame: .zero) } @objc public override init(frame: CGRect) { @@ -98,25 +98,25 @@ open class CaretView: MFView { open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) // Configure class properties with JSON values - guard let jsonDictionary = json else { return } + guard let dictionary = json else { return } - if let backgroundColorHex = jsonDictionary[KeyBackgroundColor] as? String { + if let backgroundColorHex = dictionary[KeyBackgroundColor] as? String { backgroundColor = UIColor.mfGet(forHex: backgroundColorHex) } - if let strokeColorHex = jsonDictionary["strokeColor"] as? String { + if let strokeColorHex = dictionary["strokeColor"] as? String { strokeColor = UIColor.mfGet(forHex: strokeColorHex) } - if let isHiddenValue = jsonDictionary[KeyIsHidden] as? Bool { + if let isHiddenValue = dictionary[KeyIsHidden] as? Bool { isHidden = isHiddenValue } - if let isOpaqueValue = jsonDictionary[KeyIsOpaque] as? Bool { + if let isOpaqueValue = dictionary[KeyIsOpaque] as? Bool { isOpaque = isOpaqueValue } - if let lineWidthValue = jsonDictionary["lineWidth"] as? CGFloat { + if let lineWidthValue = dictionary["lineWidth"] as? CGFloat { lineWidth = lineWidthValue } } From 2a05328f1a108fbb2103da224fdb2c1f1cc21cf5 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Tue, 30 Apr 2019 12:11:36 -0400 Subject: [PATCH 03/45] LabelWithInternalButton is in a functional place. Further work required for Label. --- MVMCoreUI/Atoms/Views/Label.swift | 26 +-- .../Atoms/Views/LabelWithInternalButton.swift | 180 ++++++++---------- 2 files changed, 88 insertions(+), 118 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index dc3b4597..8b4ca7fd 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -11,7 +11,7 @@ import MVMCore public typealias ActionBlock = () -> Void -@objc open class Label: UILabel, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol { +@objcMembers open class Label: UILabel, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol { //------------------------------------------------------ // MARK: - Properties //------------------------------------------------------ @@ -23,17 +23,17 @@ public typealias ActionBlock = () -> Void var makeWholeViewClickable = false // Set this property if you want updateView to update the font based on this standard and the size passed in. - @objc public var standardFontSize: CGFloat = 0.0 + public var standardFontSize: CGFloat = 0.0 // Set this to use a custom sizing object during updateView instead of the standard. - @objc public var sizeObject: MFSizeObject? + public var sizeObject: MFSizeObject? - @objc public var scaleSize: NSNumber? + public var scaleSize: NSNumber? // Used for scaling the font in updateView. private var originalAttributedString: NSAttributedString? - @objc public var hasText: Bool { + public var hasText: Bool { guard let text = text, let attributedText = attributedText else { return false } return !text.isEmpty || !attributedText.string.isEmpty } @@ -151,7 +151,7 @@ public typealias ActionBlock = () -> Void guard let label = label else { return } label.text = json?.optionalStringForKey(KeyText) - + setLabel(label, withHTML: json?.optionalStringForKey("html")) if let textColorHex = json?.optionalStringForKey(KeyTextColor), !textColorHex.isEmpty { @@ -176,21 +176,21 @@ public typealias ActionBlock = () -> Void let attributedString = NSMutableAttributedString(string: labelText, attributes: [NSAttributedString.Key.font: label.font as UIFont, NSAttributedString.Key.foregroundColor: label.textColor as UIColor]) for case let attribute as [String: Any] in attributes { - + guard let attributeType = attribute.optionalStringForKey(KeyType), let location = attribute["location"] as? Int, let length = attribute["length"] as? Int else { continue } - + let range = NSRange(location: location, length: length) - + switch attributeType { case "underline": attributedString.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: range) - + case "strikethrough": attributedString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.thick.rawValue, range: range) - + case "color": if let colorHex = attribute.optionalStringForKey(KeyTextColor), !colorHex.isEmpty { attributedString.removeAttribute(.foregroundColor, range: range) @@ -199,13 +199,13 @@ public typealias ActionBlock = () -> Void case "font": let fontSize = attribute["size"] as? CGFloat var font: UIFont? - + if let fontName = attribute.optionalStringForKey("name") { font = MFFonts.mfFont(withName: fontName, size: fontSize ?? label.font.pointSize) } else if let fontSize = fontSize { font = label.font.withSize(fontSize) } - + if let font = font { attributedString.removeAttribute(.font, range: range) attributedString.addAttribute(.font, value: font, range: range) diff --git a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift index 883819e3..375945a1 100644 --- a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift +++ b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift @@ -9,7 +9,6 @@ import MVMCore -private typealias ActionableStringTuple = (front: String?, action: String?, end: String?) public typealias ActionObjectDelegate = NSObjectProtocol & MVMCoreActionDelegateProtocol public typealias ButtonObjectDelegate = NSObjectProtocol & ButtonDelegateProtocol public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProtocol & MVMCoreLoadDelegateProtocol & MVMCorePresentationDelegateProtocol & NSObjectProtocol @@ -24,7 +23,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt public var attributedText: NSAttributedString? { willSet(newAttributedText) { - if let newAttribText = newAttributedText, !newAttribText.string.isEmpty { + if let newAttribText = newAttributedText { let mutableAttributedText = NSMutableAttributedString(attributedString: newAttribText) let paragraphStyle = NSMutableParagraphStyle() @@ -60,6 +59,12 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt } } + public var actionBlock: ActionBlock? { + willSet(newActionBlock) { + label?.actionBlock = newActionBlock + } + } + private var actionRange: NSRange { return NSRange(location: frontText?.count ?? 0, length: actionText?.count ?? 0) } @@ -81,6 +86,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt label?.frontText = newFrontText } } + public var actionText: String? { willSet(newActionText) { label?.actionText = newActionText @@ -89,15 +95,14 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt public var backText: String? - public var actionBlock: ActionBlock? { - willSet(newActionBlock) { - label?.actionBlock = newActionBlock - } - } + private var internalText: String = "" - private var text: String? { - willSet(newText) { - attributedText = NSAttributedString(string: newText ?? "") + public var text: String? { + get { return internalText } + set { + guard let text = newValue else { return } + internalText = text + attributedText = NSAttributedString(string: text) setAlternateNormalTextAttributes([NSAttributedString.Key.font: normalTextFont as Any]) setAlternateActionTextAttributes([NSAttributedString.Key.font: actionTextFont as Any]) setAlternateNormalTextAttributes([NSAttributedString.Key.foregroundColor: normalTextColor as Any]) @@ -111,29 +116,33 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt public init() { super.init(frame: .zero) - setup() + createLabel() + setLabelAttributes() } required public init?(coder: NSCoder) { super.init(coder: coder) - setup() + createLabel() + setLabelAttributes() } override public init(frame: CGRect) { super.init(frame: frame) - setup() + createLabel() + setLabelAttributes() } - // MARK: - legacy + // legacy public init(frontText: String?, actionText: String?, backText: String?, actionBlock block: ActionBlock?) { super.init(frame: .zero) + createLabel() self.frontText = frontText self.actionText = actionText self.backText = backText actionBlock = block text = getTextFromStringComponents() - setup() + setLabelAttributes() } public convenience init(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { @@ -145,32 +154,22 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt } public convenience init(frontText: String?, backText: String?, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { - self.init(frontText: frontText, - actionText: actionMap?.optionalStringForKey(KeyTitle), - backText: backText, - actionMap: actionMap, - additionalData: additionalData, - delegateObject: delegateObject) + self.init(frontText: frontText, actionText: actionMap?.optionalStringForKey(KeyTitle), backText: backText, actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) } public init(frontText: String?, actionText: String?, backText: String?, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { - super.init(frame: CGRect.zero) + super.init(frame: .zero) setFrontText(frontText, actionText: actionText, actionMap: actionMap, backText: backText, additionalData: additionalData, delegateObject: delegateObject) } // Convenience Initializer which assumes that the clickable text will be embedded in curly braces {}. public convenience init(clickableTextEmbeddedInCurlyBraces fullText: String?, actionMapForClickableText actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { - self.init(text: fullText, - startTag: "{", - endTag: "}", - actionMap: actionMap, - additionalData: additionalData, - delegateObject: delegateObject) + self.init(text: fullText, startTag: "{", endTag: "}", actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) } public init(text fullText: String?, startTag: String?, endTag: String?, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { - super.init(frame: CGRect.zero) + super.init(frame: .zero) setText(fullText, startTag: startTag, endTag: endTag) @@ -183,7 +182,8 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt // MARK: - Configuration //------------------------------------------------------ - private func setup() { + /// Creates the Label that will be interacted with. + private func createLabel() { if self.label == nil { let label = Label(frame: .zero) @@ -191,12 +191,15 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt label.isUserInteractionEnabled = true label.setContentCompressionResistancePriority(.required, for: .vertical) addSubview(label) - NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[label]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["label": label])) - NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[label]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["label": label])) + NSLayoutConstraint.constraintPinSubview(label, pinTop: true, pinBottom: true, pinLeft: true, pinRight: true) self.label = label label.sizeToFit() } + } + + /// Sets up label. Best to call sometime after setting the text. + private func setLabelAttributes() { // Adding the underline setAlternateActionTextAttributes([NSAttributedString.Key.underlineStyle: NSNumber(value: NSUnderlineStyle.single.rawValue)]) @@ -204,16 +207,15 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt self.label?.attributedText = attributedText self.label?.accessibilityTraits = .button } - + @objc public func setActionMap(_ actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { - weak var weakSelf: LabelWithInternalButton? = self - weak var weakButtonDelegate: ButtonDelegateProtocol? = (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate - - actionBlock = { + actionBlock = { [weak self, weak delegateObject] in var performAction = true - if let wSelf = weakSelf, let wButtonDelegate = weakButtonDelegate, wButtonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { + if let wSelf = self, + let wButtonDelegate = (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate, + wButtonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { performAction = wButtonDelegate.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? false } @@ -229,12 +231,13 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt @objc public func setFrontText(_ frontText: String?, actionText: String?, actionMap: [AnyHashable: Any]?, backText: String?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { + createLabel() self.frontText = frontText setActionMap(actionMap, additionalData: additionalData, delegateObject: delegateObject) self.actionText = actionText self.backText = backText text = getTextFromStringComponents() - setup() + setLabelAttributes() } @objc public func setFrontText(_ frontText: String?, actionMap: [AnyHashable: Any]?, backText: String?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { @@ -283,14 +286,6 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt label?.attributedText = attributedText } - //------------------------------------------------------ - // MARK: - UIControl - //------------------------------------------------------ - -// override open func sendAction(_ action: Selector, to target: Any?, for event: UIEvent?) { -// <#code#> -// } - //------------------------------------------------------ // MARK: - Helper //------------------------------------------------------ @@ -315,45 +310,25 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt private func setText(_ text: String?, startTag: String?, endTag: String?) { - let actionableTuple: ActionableStringTuple = rangeOfText(text, startTag: startTag, endTag: endTag) + createLabel() + frontText = text - if let text = text, - let leftTag = startTag, text.contains(leftTag), - let rightTag = endTag, text.contains(rightTag), - let front = actionableTuple.front, - let middle = actionableTuple.action, - let end = actionableTuple.end { - - frontText = front.trimmingCharacters(in: .whitespaces) - actionText = middle.trimmingCharacters(in: .whitespaces) - backText = end.trimmingCharacters(in: .whitespaces) - } else { - frontText = text - } - - self.text = getTextFromStringComponents() - setup() - } - - private func rangeOfText(_ text: String?, startTag: String?, endTag: String?) -> ActionableStringTuple { - - var actionableTuple: ActionableStringTuple = (front: nil, action: nil, end: nil) - - guard let text = text else { return actionableTuple } - - if let leftTag = startTag, text.contains(leftTag) { - - let firstHalf = text.components(separatedBy: leftTag) - actionableTuple.front = firstHalf.first - - if let rightTag = endTag, text.contains(rightTag) { - let secondHalf = firstHalf[1].components(separatedBy: rightTag) - actionableTuple.action = secondHalf[0] - actionableTuple.end = secondHalf[1] + if let text = text { + var initialSegments = [String]() + if let leftTag = startTag, text.contains(leftTag) { + initialSegments = text.components(separatedBy: leftTag) + frontText = initialSegments[0].trimmingCharacters(in: .whitespaces) + + if let rightTag = endTag, text.contains(rightTag) { + let secondPart = initialSegments[1].components(separatedBy: rightTag) + actionText = secondPart[0].trimmingCharacters(in: .whitespaces) + backText = secondPart[1].trimmingCharacters(in: .whitespaces) + } } } - return actionableTuple + self.text = getTextFromStringComponents() + setLabelAttributes() } // Reset the text and action map @@ -418,14 +393,16 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt @objc public func setActionTextString(_ actionText: String?) { + createLabel() self.actionText = actionText text = getTextFromStringComponents() - setup() + setLabelAttributes() } /// Used to just reset the texts and actions if already initialized @objc public func reset(withActionMap actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { + createLabel() frontText = actionMap?.optionalStringForKey(KeyTitlePrefix) actionText = actionMap?.optionalStringForKey(KeyTitle) backText = actionMap?.optionalStringForKey(KeyTitlePostfix) @@ -435,7 +412,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt } text = getTextFromStringComponents() - setup() + setLabelAttributes() } //------------------------------------------------------ @@ -473,7 +450,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt @available(*, deprecated) public init(frontText: String?, actionText: String?, backText: String?, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, actionDelegate delegate: ActionObjectDelegate?, buttonDelegate: ButtonObjectDelegate?) { - super.init(frame: CGRect.zero) + super.init(frame: .zero) setFrontText(frontText, actionText: actionText, actionMap: actionMap, backText: backText, additionalData: additionalData, delegate: delegate, buttonDelegate: buttonDelegate) } @@ -517,29 +494,23 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt setText(fullText, startTag: startTag, endTag: endTag) - weak var weakDelegate: ActionObjectDelegate? = delegate - actionBlock = { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegate: weakDelegate as? CoreObjectActionLoadPresentDelegate) + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegate: delegate as? CoreObjectActionLoadPresentDelegate) } } @available(*, deprecated) private func setActionMap(_ actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, actionDelegate delegate: ActionObjectDelegate?, buttonDelegate: ButtonObjectDelegate?) { - weak var weakSelf: LabelWithInternalButton? = self - weak var weakDelegate: ActionObjectDelegate? = delegate - weak var weakButtonDelegate: ButtonObjectDelegate? = buttonDelegate - - actionBlock = { + actionBlock = { [weak self] in var performAction = true - if let wSelf = weakSelf, let wButtonDelegate = weakButtonDelegate, wButtonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { + if let wSelf = self, let wButtonDelegate = buttonDelegate, wButtonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { performAction = wButtonDelegate.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? false } - if let wDelegate = weakDelegate as? CoreObjectActionLoadPresentDelegate, performAction { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegate: wDelegate) + if performAction { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegate: delegate as? CoreObjectActionLoadPresentDelegate) } } } @@ -552,12 +523,13 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt @available(*, deprecated) @objc public func setFrontText(_ frontText: String?, actionText: String?, actionMap: [AnyHashable: Any]?, backText: String?, additionalData: [AnyHashable: Any]?, delegate: ActionObjectDelegate?, buttonDelegate: ButtonObjectDelegate?) { + createLabel() self.frontText = frontText setActionMap(actionMap, additionalData: additionalData, actionDelegate: delegate, buttonDelegate: buttonDelegate) self.actionText = actionText self.backText = backText text = getTextFromStringComponents() - setup() + setLabelAttributes() } @available(*, deprecated) @@ -578,10 +550,8 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt } if let b2Font = MFStyler.fontB2(), - let actions = actionMap, - actions.keys.count > 0, - let actionString = actions.optionalStringForKey(KeyTitle), - !actionString.isEmpty { + let actions = actionMap, actions.keys.count > 0, + let actionString = actions.optionalStringForKey(KeyTitle), !actionString.isEmpty { let actionStringOnLine = actionString + (addNewLine ? "\n" : " ") actionText = actionStringOnLine @@ -646,18 +616,18 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt @available(*, deprecated) @objc public func reset(withActionMap actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegate: ActionObjectDelegate?) { + createLabel() + frontText = actionMap?.optionalStringForKey(KeyTitlePrefix) actionText = actionMap?.optionalStringForKey(KeyTitle) backText = actionMap?.optionalStringForKey(KeyTitlePostfix) - weak var weakDelegate: ActionObjectDelegate? = delegate - actionBlock = { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegate: weakDelegate as? CoreObjectActionLoadPresentDelegate) + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegate: delegate as? CoreObjectActionLoadPresentDelegate) } text = getTextFromStringComponents() - setup() + setLabelAttributes() } } From f64c793aebfc493e5fbb95173e9cf4d81d65103b Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Wed, 1 May 2019 15:42:49 -0400 Subject: [PATCH 04/45] Multi-action-text label WIP. --- MVMCoreUI/Atoms/Views/Label.swift | 103 +++++++++++++++++++++--- MVMCoreUI/Molecules/TwoButtonView.swift | 4 +- 2 files changed, 96 insertions(+), 11 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index b203e488..7acb4691 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -20,6 +20,9 @@ public typealias ActionBlock = () -> Void var actionText: String? var frontText: String? + var clauses = [ActionableClause]() + + // This is here for LabelWithInternalButton var makeWholeViewClickable = false // Set this property if you want updateView to update the font based on this standard and the size passed in. @@ -38,6 +41,26 @@ public typealias ActionBlock = () -> Void return !text.isEmpty || !attributedText.string.isEmpty } + struct ActionableClause { + var location: Int? + var length: Int? + var actionText: String? + var words: [String]? { + return actionText?.components(separatedBy: " ") + } + var actionBlock: ActionBlock? + + var range: NSRange { + return NSRange(location: location ?? 0, length: length ?? 0) + } + + func performAction() { + if let action = actionBlock { + action() + } + } + } + //------------------------------------------------------ // MARK: - Initialization //------------------------------------------------------ @@ -148,7 +171,7 @@ public typealias ActionBlock = () -> Void @objc public static func setUILabel(_ label: UILabel?, withJSON json: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) { - guard let label = label else { return } + guard let label = label as? Label else { return } label.text = json?.optionalStringForKey(KeyText) @@ -176,7 +199,6 @@ public typealias ActionBlock = () -> Void let attributedString = NSMutableAttributedString(string: labelText, attributes: [NSAttributedString.Key.font: label.font as UIFont, NSAttributedString.Key.foregroundColor: label.textColor as UIColor]) for case let attribute as [String: Any] in attributes { - guard let attributeType = attribute.optionalStringForKey(KeyType), let location = attribute["location"] as? Int, let length = attribute["length"] as? Int @@ -210,6 +232,29 @@ public typealias ActionBlock = () -> Void attributedString.removeAttribute(.font, range: range) attributedString.addAttribute(.font, value: font, range: range) } + case "actions": + let actions = attribute.arrayForKey("actions") + let text = json?.optionalStringForKey(KeyText) + + for case let action as [String: Any] in actions { + guard let locationx = action["location"] as? Int, + let lengthx = action["length"] as? Int + else { continue } + + let subStringRange = Range(NSRange(location: locationx, length: lengthx), in: text!) + let actionText = text![subStringRange!] + + label.clauses.append(ActionableClause(location: locationx, length: lengthx, + actionText: String(actionText), + actionBlock: { MVMCoreActionHandler.shared()?.handleAction(with: attribute, additionalData: additionalData, delegateObject: delegate) })) + if !label.isUserInteractionEnabled { + label.isUserInteractionEnabled = true + let tapGesture = UITapGestureRecognizer(target: label, action: #selector(textLinkTapped(_:))) + tapGesture.numberOfTapsRequired = 1 + label.addGestureRecognizer(tapGesture) + } + } + default: continue } @@ -308,22 +353,29 @@ public typealias ActionBlock = () -> Void } public func needsToBeConstrained() -> Bool { - return true; + return true } } // MARK: - UIControl Override extension Label { - override open func touchesEnded(_ touches: Set, with event: UIEvent?) { + @objc func textLinkTapped(_ gesture: UITapGestureRecognizer) { - if areTouches(inActionString: touches) { - if let action = actionBlock { + for clause in clauses { + if gesture.didTapAttributedTextInLabel(self, inRange: clause.range), let action = clause.actionBlock { action() } } } + override open func touchesEnded(_ touches: Set, with event: UIEvent?) { + + if areTouches(inActionString: touches), let action = actionBlock { + action() + } + } + private func areTouches(inActionString touches: Set?) -> Bool { if UIAccessibility.isVoiceOverRunning || makeWholeViewClickable { @@ -331,8 +383,8 @@ extension Label { } let location: CGPoint? = touches?.first?.location(in: self) - let index: Int = NSRange(location: frontText?.count ?? 0, length: actionText?.count ?? 0).location - let rangeArray = getRangeArrayOfWords(in: actionText, withInitalIndex: index) + let actionTextIndex: Int = NSRange(location: frontText?.count ?? 0, length: actionText?.count ?? 0).location + let rangeArray = getRangeArrayOfWords(in: actionText, withInitalIndex: actionTextIndex) let rectArray = getRectArray(from: rangeArray) as? [NSValue] ?? [] for rect in rectArray { @@ -342,7 +394,7 @@ extension Label { return true } else if wordRect.origin == .zero && wordRect.size.height == 0 && wordRect.size.width == 0 { - // Incase word rect is not found for any reason, make the whole label to be clicable to avoid non functioning link in production. + // In case word rect is not found for any reason, make the whole label to be clicable to avoid non functioning link in production. return true } } @@ -350,6 +402,7 @@ extension Label { return false } + // Works for LabelWithInternalButton private func getRangeArrayOfWords(in string: String?, withInitalIndex index: Int) -> [Any]? { var index = index @@ -368,6 +421,7 @@ extension Label { return rangeArray } + // Works for LabelWithInternalButton private func getRectArray(from rangeArray: [Any]?) -> [Any]? { var rectArray = [AnyHashable]() @@ -380,3 +434,34 @@ extension Label { return rectArray } } + + +extension UITapGestureRecognizer { + + func didTapAttributedTextInLabel(_ label: Label, inRange targetRange: NSRange) -> Bool { + + let layoutManager = NSLayoutManager() + let textContainer = NSTextContainer(size: .zero) + let textStorage = NSTextStorage(attributedString: label.attributedText!) + + layoutManager.addTextContainer(textContainer) + textStorage.addLayoutManager(layoutManager) + + textContainer.lineFragmentPadding = 0.0 + textContainer.lineBreakMode = label.lineBreakMode + textContainer.maximumNumberOfLines = label.numberOfLines + let labelSize = label.bounds.size + textContainer.size = labelSize + + // Find the tapped character location and compare it to the specified range +// let locationOfTouchInLabel = location(in: label) +// let textBoundingBox = layoutManager.usedRect(for: textContainer) +// let textContainerOffset = CGPoint(x: (labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x, +// y: (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y) +// let locationOfTouchInTextContainer = CGPoint(x: locationOfTouchInLabel.x - textContainerOffset.x, +// y: locationOfTouchInLabel.y - textContainerOffset.y) + let indexOfCharacter = layoutManager.characterIndex(for: location(in: label), in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil) + + return NSLocationInRange(indexOfCharacter, targetRange) + } +} diff --git a/MVMCoreUI/Molecules/TwoButtonView.swift b/MVMCoreUI/Molecules/TwoButtonView.swift index 66b7210a..c99de733 100644 --- a/MVMCoreUI/Molecules/TwoButtonView.swift +++ b/MVMCoreUI/Molecules/TwoButtonView.swift @@ -44,8 +44,8 @@ import UIKit if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) { backgroundColor = UIColor.mfGet(forHex: backgroundColorString) } - let primaryButtonMap = json?.optionalDictionaryForKey("primaryButton") - let secondaryButtonMap = json?.optionalDictionaryForKey("secondaryButton") + let primaryButtonMap = json?.optionalDictionaryForKey(KeyPrimaryButton) + let secondaryButtonMap = json?.optionalDictionaryForKey(KeySecondaryButton) set(primaryButtonJSON: primaryButtonMap, secondaryButtonJSON: secondaryButtonMap, delegateObject: delegateObject, additionalData: additionalData) } From 9c6ddc247019b2bd78f6551117c124b8015d0774 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Wed, 1 May 2019 16:30:24 -0400 Subject: [PATCH 05/45] Small changes. --- MVMCoreUI/Atoms/Views/Label.swift | 13 ++----------- MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift | 3 +-- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 7acb4691..01ffb3db 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -254,7 +254,6 @@ public typealias ActionBlock = () -> Void label.addGestureRecognizer(tapGesture) } } - default: continue } @@ -363,8 +362,8 @@ extension Label { @objc func textLinkTapped(_ gesture: UITapGestureRecognizer) { for clause in clauses { - if gesture.didTapAttributedTextInLabel(self, inRange: clause.range), let action = clause.actionBlock { - action() + if gesture.didTapAttributedTextInLabel(self, inRange: clause.range) { + clause.performAction() } } } @@ -435,7 +434,6 @@ extension Label { } } - extension UITapGestureRecognizer { func didTapAttributedTextInLabel(_ label: Label, inRange targetRange: NSRange) -> Bool { @@ -453,13 +451,6 @@ extension UITapGestureRecognizer { let labelSize = label.bounds.size textContainer.size = labelSize - // Find the tapped character location and compare it to the specified range -// let locationOfTouchInLabel = location(in: label) -// let textBoundingBox = layoutManager.usedRect(for: textContainer) -// let textContainerOffset = CGPoint(x: (labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x, -// y: (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y) -// let locationOfTouchInTextContainer = CGPoint(x: locationOfTouchInLabel.x - textContainerOffset.x, -// y: locationOfTouchInLabel.y - textContainerOffset.y) let indexOfCharacter = layoutManager.characterIndex(for: location(in: label), in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil) return NSLocationInRange(indexOfCharacter, targetRange) diff --git a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift index 375945a1..3daeb3b9 100644 --- a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift +++ b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift @@ -213,8 +213,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt actionBlock = { [weak self, weak delegateObject] in var performAction = true - if let wSelf = self, - let wButtonDelegate = (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate, + if let wSelf = self, let wButtonDelegate = (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate, wButtonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { performAction = wButtonDelegate.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? false } From d1153d4bf84c9f1a50fe6ba614017e32c4d7498f Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Thu, 2 May 2019 14:12:41 -0400 Subject: [PATCH 06/45] Added button delegate checks for labelWithInternalButton. WIP with multi-action label. --- MVMCoreUI/Atoms/Views/Label.swift | 122 +++++++++++++----- .../Atoms/Views/LabelWithInternalButton.swift | 36 ++++-- 2 files changed, 115 insertions(+), 43 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 01ffb3db..793b8aa5 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -11,17 +11,16 @@ import MVMCore public typealias ActionBlock = () -> Void -@objcMembers open class Label: UILabel, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol { +@objcMembers open class Label: UILabel, MVMCoreViewProtocol, MFButtonProtocol, MVMCoreUIMoleculeViewProtocol { //------------------------------------------------------ - // MARK: - Properties + // MARK: - General Properties //------------------------------------------------------ + // Specifically used in LabelWithInternalButton to interact with UIControl. var actionBlock: ActionBlock? var actionText: String? var frontText: String? - var clauses = [ActionableClause]() - // This is here for LabelWithInternalButton var makeWholeViewClickable = false @@ -41,14 +40,23 @@ public typealias ActionBlock = () -> Void return !text.isEmpty || !attributedText.string.isEmpty } - struct ActionableClause { + //------------------------------------------------------ + // MARK: - For Multi-Action Text + //------------------------------------------------------ + + private var clauses: [ActionableClause] = [] + + // Used for tappable links in the text. + private struct ActionableClause { + var labelView: Label? var location: Int? var length: Int? var actionText: String? + var actionBlock: ActionBlock? + var words: [String]? { return actionText?.components(separatedBy: " ") } - var actionBlock: ActionBlock? var range: NSRange { return NSRange(location: location ?? 0, length: length ?? 0) @@ -94,7 +102,7 @@ public typealias ActionBlock = () -> Void } //------------------------------------------------------ - // MARK: - Functions + // MARK: - Factory Functions //------------------------------------------------------ @objc public static func commonLabelH1(_ scale: Bool) -> Label { @@ -153,7 +161,46 @@ public typealias ActionBlock = () -> Void // MARK: - Functions //------------------------------------------------------ - @objc public static func setLabel(_ label: UILabel?, withHTML html: String?) { + /** + Provides an actionable range of text. + + - Attention: This method expects text to be set first. Otherwise, it will do nothing. + - Parameters: + - range: The range of text to be tapped. + - actionBlock: The code triggered when tapping the range of text. + */ + @objc public func addTappableLinkAttribute(range: NSRange, actionBlock: @escaping ActionBlock) { + + guard let text = self.text, + let subStringRange = Range(range, in: text) + else { return } + + clauses.append(ActionableClause(labelView: self, + location: range.location, + length: range.length, + actionText: String(text[subStringRange]), + actionBlock: actionBlock)) + + Label.setGestureInteraction(for: self) + } + + /** + Makes the view interactive and applies the gesture recognizer. + + - Parameters: + - label: The current label view that will have an actionable range of text. + */ + private static func setGestureInteraction(for label: Label) { + + if !label.isUserInteractionEnabled { + label.isUserInteractionEnabled = true + let tapGesture = UITapGestureRecognizer(target: label, action: #selector(textLinkTapped(_:))) + tapGesture.numberOfTapsRequired = 1 + label.addGestureRecognizer(tapGesture) + } + } + + @objc public static func setLabel(_ label: UILabel?, with html: String?) { guard let data = html?.data(using: .utf8) else { return } @@ -175,7 +222,7 @@ public typealias ActionBlock = () -> Void label.text = json?.optionalStringForKey(KeyText) - setLabel(label, withHTML: json?.optionalStringForKey("html")) + setLabel(label, with: json?.optionalStringForKey("html")) if let textColorHex = json?.optionalStringForKey(KeyTextColor), !textColorHex.isEmpty { label.textColor = UIColor.mfGet(forHex: textColorHex) @@ -234,25 +281,30 @@ public typealias ActionBlock = () -> Void } case "actions": let actions = attribute.arrayForKey("actions") - let text = json?.optionalStringForKey(KeyText) + guard let text = json?.optionalStringForKey(KeyText) else { continue } for case let action as [String: Any] in actions { - guard let locationx = action["location"] as? Int, - let lengthx = action["length"] as? Int + guard let actionLocation = action["location"] as? Int, + let actionLength = action["length"] as? Int, + let subStringRange = Range(NSRange(location: actionLocation, length: actionLength), in: text) else { continue } - let subStringRange = Range(NSRange(location: locationx, length: lengthx), in: text!) - let actionText = text![subStringRange!] - - label.clauses.append(ActionableClause(location: locationx, length: lengthx, - actionText: String(actionText), - actionBlock: { MVMCoreActionHandler.shared()?.handleAction(with: attribute, additionalData: additionalData, delegateObject: delegate) })) - if !label.isUserInteractionEnabled { - label.isUserInteractionEnabled = true - let tapGesture = UITapGestureRecognizer(target: label, action: #selector(textLinkTapped(_:))) - tapGesture.numberOfTapsRequired = 1 - label.addGestureRecognizer(tapGesture) - } + label.clauses.append(ActionableClause(labelView: label, + location: actionLocation, + length: actionLength, + actionText: String(text[subStringRange]), + actionBlock: { [weak delegate] in + var willPerform = true + + if let buttonDelegate = (delegate as? MVMCoreUIDelegateObject)?.buttonDelegate, + buttonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { + willPerform = buttonDelegate.button?(label, shouldPerformActionWithMap: json, additionalData: additionalData) ?? false + } + + if willPerform { + MVMCoreActionHandler.shared()?.handleAction(with: attribute, additionalData: additionalData, delegateObject: delegate) + } })) + Label.setGestureInteraction(for: label) } default: continue @@ -341,10 +393,10 @@ public typealias ActionBlock = () -> Void standardFontSize = 0 } } - - //------------------------------------------------------ - // MARK: - Atomization - //------------------------------------------------------ +} + +// MARK: - Atomization +extension Label { @objc public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { Label.setUILabel(self, withJSON: json, delegate: delegateObject, additionalData: additionalData) @@ -356,7 +408,8 @@ public typealias ActionBlock = () -> Void } } -// MARK: - UIControl Override + +// MARK: - UIControl functionality Override extension Label { @objc func textLinkTapped(_ gesture: UITapGestureRecognizer) { @@ -368,6 +421,7 @@ extension Label { } } + // For LabelWithInternalButton override open func touchesEnded(_ touches: Set, with event: UIEvent?) { if areTouches(inActionString: touches), let action = actionBlock { @@ -375,6 +429,7 @@ extension Label { } } + // For LabelWithInternalButton private func areTouches(inActionString touches: Set?) -> Bool { if UIAccessibility.isVoiceOverRunning || makeWholeViewClickable { @@ -401,7 +456,7 @@ extension Label { return false } - // Works for LabelWithInternalButton + // For LabelWithInternalButton private func getRangeArrayOfWords(in string: String?, withInitalIndex index: Int) -> [Any]? { var index = index @@ -420,7 +475,7 @@ extension Label { return rangeArray } - // Works for LabelWithInternalButton + // For LabelWithInternalButton private func getRectArray(from rangeArray: [Any]?) -> [Any]? { var rectArray = [AnyHashable]() @@ -434,13 +489,16 @@ extension Label { } } +// MARK: - UITapGestureRecognizer Override extension UITapGestureRecognizer { func didTapAttributedTextInLabel(_ label: Label, inRange targetRange: NSRange) -> Bool { + guard let attributedText = label.attributedText else { return false } + let layoutManager = NSLayoutManager() let textContainer = NSTextContainer(size: .zero) - let textStorage = NSTextStorage(attributedString: label.attributedText!) + let textStorage = NSTextStorage(attributedString: attributedText) layoutManager.addTextContainer(textContainer) textStorage.addLayoutManager(layoutManager) diff --git a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift index 3daeb3b9..e2db0fdb 100644 --- a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift +++ b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift @@ -146,11 +146,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt } public convenience init(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { - self.init(frontText: actionMap?.optionalStringForKey(KeyTitlePrefix), - actionText: actionMap?.optionalStringForKey(KeyTitle), - backText: actionMap?.optionalStringForKey(KeyTitlePostfix), - actionMap: actionMap, additionalData: additionalData, - delegateObject: delegateObject) + self.init(frontText: actionMap?.optionalStringForKey(KeyTitlePrefix), actionText: actionMap?.optionalStringForKey(KeyTitle), backText: actionMap?.optionalStringForKey(KeyTitlePostfix), actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) } public convenience init(frontText: String?, backText: String?, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { @@ -173,8 +169,17 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt setText(fullText, startTag: startTag, endTag: endTag) - actionBlock = { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + actionBlock = { [weak self, weak delegateObject] in + var performAction = true + + if let wSelf = self, let wButtonDelegate = (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate, + wButtonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { + performAction = wButtonDelegate.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? false + } + + if performAction { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + } } } @@ -406,8 +411,17 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt actionText = actionMap?.optionalStringForKey(KeyTitle) backText = actionMap?.optionalStringForKey(KeyTitlePostfix) - actionBlock = { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + actionBlock = { [weak self, weak delegateObject] in + var performAction = true + + if let wSelf = self, let wButtonDelegate = (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate, + wButtonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { + performAction = wButtonDelegate.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? false + } + + if performAction { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + } } text = getTextFromStringComponents() @@ -501,7 +515,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt @available(*, deprecated) private func setActionMap(_ actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, actionDelegate delegate: ActionObjectDelegate?, buttonDelegate: ButtonObjectDelegate?) { - actionBlock = { [weak self] in + actionBlock = { [weak self, weak buttonDelegate] in var performAction = true if let wSelf = self, let wButtonDelegate = buttonDelegate, wButtonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { @@ -621,7 +635,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt actionText = actionMap?.optionalStringForKey(KeyTitle) backText = actionMap?.optionalStringForKey(KeyTitlePostfix) - actionBlock = { + actionBlock = { [weak delegate] in MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegate: delegate as? CoreObjectActionLoadPresentDelegate) } From 18f8f7d9f37fdc73bd9aa7bfbb9f561691de5c4b Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Thu, 2 May 2019 15:37:12 -0400 Subject: [PATCH 07/45] Two means to set actionable text in code. --- MVMCoreUI/Atoms/Views/Label.swift | 36 +++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 793b8aa5..d6f0a5ed 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -46,6 +46,8 @@ public typealias ActionBlock = () -> Void private var clauses: [ActionableClause] = [] + var didSetGestureRecognizer = false + // Used for tappable links in the text. private struct ActionableClause { var labelView: Label? @@ -53,11 +55,7 @@ public typealias ActionBlock = () -> Void var length: Int? var actionText: String? var actionBlock: ActionBlock? - - var words: [String]? { - return actionText?.components(separatedBy: " ") - } - + var range: NSRange { return NSRange(location: location ?? 0, length: length ?? 0) } @@ -184,6 +182,31 @@ public typealias ActionBlock = () -> Void Label.setGestureInteraction(for: self) } + /** + Provides an actionable range of text. + + Allows actionable range to be established by a particular substring of the containing label text. + + - Attention: This method expects text to be set first. Otherwise, it will do nothing. + - Parameters: + - actionText: The actionable text contained witin the label's text. + - actionBlock: The code triggered when tapping the range of text. + */ + @objc public func addTappableLinkAttribute(actionText: String, actionBlock: @escaping ActionBlock) { + + guard let text = self.text else { return } + let string = text as NSString + let range = string.range(of: actionText) + + clauses.append(ActionableClause(labelView: self, + location: range.location, + length: range.length, + actionText: actionText, + actionBlock: actionBlock)) + + Label.setGestureInteraction(for: self) + } + /** Makes the view interactive and applies the gesture recognizer. @@ -192,8 +215,9 @@ public typealias ActionBlock = () -> Void */ private static func setGestureInteraction(for label: Label) { - if !label.isUserInteractionEnabled { + if !label.didSetGestureRecognizer { label.isUserInteractionEnabled = true + label.didSetGestureRecognizer = true let tapGesture = UITapGestureRecognizer(target: label, action: #selector(textLinkTapped(_:))) tapGesture.numberOfTapsRequired = 1 label.addGestureRecognizer(tapGesture) From 211d9f6c3b39541272286a6f239db6bf52a82ecc Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Fri, 3 May 2019 11:28:25 -0400 Subject: [PATCH 08/45] More functionality for label. --- MVMCoreUI/Atoms/Views/Label.swift | 96 +++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index d6f0a5ed..255fa342 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -156,7 +156,7 @@ public typealias ActionBlock = () -> Void } //------------------------------------------------------ - // MARK: - Functions + // MARK: - Tappable Methods //------------------------------------------------------ /** @@ -182,12 +182,47 @@ public typealias ActionBlock = () -> Void Label.setGestureInteraction(for: self) } + /** + Provides an actionable range of text. + + - Attention: This method expects text to be set first. Otherwise, it will do nothing. + - Parameters: + - range: The range of text to be tapped. + - actionMap: + - delegate: + - additionalData: + */ + @objc public func addTappableLinkAttribute(range: NSRange, actionMap: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) { + + guard let text = self.text, + let subStringRange = Range(range, in: text) + else { return } + + clauses.append(ActionableClause(labelView: self, + location: range.location, + length: range.length, + actionText: String(text[subStringRange]), + actionBlock: { [weak self, weak delegate] in + var willPerform = true + + if let wSelf = self, let buttonDelegate = (delegate as? MVMCoreUIDelegateObject)?.buttonDelegate, + buttonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { + willPerform = buttonDelegate.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? false + } + + if willPerform { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegate) + } })) + + Label.setGestureInteraction(for: self) + } + /** Provides an actionable range of text. Allows actionable range to be established by a particular substring of the containing label text. - - Attention: This method expects text to be set first. Otherwise, it will do nothing. + - Attention: This method expects text to be set first. Otherwise, it will do nothing. Do not use if actionText is not unique in the Label's text. - Parameters: - actionText: The actionable text contained witin the label's text. - actionBlock: The code triggered when tapping the range of text. @@ -207,6 +242,42 @@ public typealias ActionBlock = () -> Void Label.setGestureInteraction(for: self) } + /** + Provides an actionable range of text. + + - Attention: This method expects text to be set first. Otherwise, it will do nothing. Do not use if actionText is not unique in the Label's text. + - Parameters: + - actionText: The actionable text contained witin the label's text. + - actionMap: + - delegate: + - additionalData: + */ + @objc public func addTappableLinkAttribute(actionText: String, actionMap: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) { + + guard let text = self.text else { return } + let string = text as NSString + let range = string.range(of: actionText) + + clauses.append(ActionableClause(labelView: self, location: range.location, length: range.length, actionText: actionText, + actionBlock: { [weak self, weak delegate] in + var willPerform = true + + if let wSelf = self, let buttonDelegate = (delegate as? MVMCoreUIDelegateObject)?.buttonDelegate, + buttonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { + willPerform = buttonDelegate.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? false + } + + if willPerform { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegate) + } })) + + Label.setGestureInteraction(for: self) + } + + //------------------------------------------------------ + // MARK: - Functions + //------------------------------------------------------ + /** Makes the view interactive and applies the gesture recognizer. @@ -224,6 +295,13 @@ public typealias ActionBlock = () -> Void } } + /** + Makes the view interactive and applies the gesture recognizer. + + - Parameters: + - label: The label to be set. + - html: The url link to be applied as an attributed link. + */ @objc public static func setLabel(_ label: UILabel?, with html: String?) { guard let data = html?.data(using: .utf8) else { return } @@ -240,6 +318,15 @@ public typealias ActionBlock = () -> Void } } + /** + Makes the view interactive and applies the gesture recognizer. + + - Parameters: + - label: The current label view that will have an actionable range of text. + - json: Contains values which det the values of Label. + - delegate: + - additionalData: + */ @objc public static func setUILabel(_ label: UILabel?, withJSON json: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) { guard let label = label as? Label else { return } @@ -326,7 +413,7 @@ public typealias ActionBlock = () -> Void } if willPerform { - MVMCoreActionHandler.shared()?.handleAction(with: attribute, additionalData: additionalData, delegateObject: delegate) + MVMCoreActionHandler.shared()?.handleAction(with: json, additionalData: additionalData, delegateObject: delegate) } })) Label.setGestureInteraction(for: label) } @@ -436,7 +523,7 @@ extension Label { // MARK: - UIControl functionality Override extension Label { - @objc func textLinkTapped(_ gesture: UITapGestureRecognizer) { + @objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) { for clause in clauses { if gesture.didTapAttributedTextInLabel(self, inRange: clause.range) { @@ -516,6 +603,7 @@ extension Label { // MARK: - UITapGestureRecognizer Override extension UITapGestureRecognizer { + func didTapAttributedTextInLabel(_ label: Label, inRange targetRange: NSRange) -> Bool { guard let attributedText = label.attributedText else { return false } From 2f69b07bf56e24aafac38228a20575404f33da28 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Fri, 3 May 2019 14:20:23 -0400 Subject: [PATCH 09/45] Removed unneeded parameter. --- MVMCoreUI/Atoms/Views/Label.swift | 20 +++++++------------ .../Atoms/Views/LabelWithInternalButton.swift | 2 +- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 255fa342..54788654 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -50,7 +50,6 @@ public typealias ActionBlock = () -> Void // Used for tappable links in the text. private struct ActionableClause { - var labelView: Label? var location: Int? var length: Int? var actionText: String? @@ -173,8 +172,7 @@ public typealias ActionBlock = () -> Void let subStringRange = Range(range, in: text) else { return } - clauses.append(ActionableClause(labelView: self, - location: range.location, + clauses.append(ActionableClause(location: range.location, length: range.length, actionText: String(text[subStringRange]), actionBlock: actionBlock)) @@ -198,8 +196,7 @@ public typealias ActionBlock = () -> Void let subStringRange = Range(range, in: text) else { return } - clauses.append(ActionableClause(labelView: self, - location: range.location, + clauses.append(ActionableClause(location: range.location, length: range.length, actionText: String(text[subStringRange]), actionBlock: { [weak self, weak delegate] in @@ -233,8 +230,7 @@ public typealias ActionBlock = () -> Void let string = text as NSString let range = string.range(of: actionText) - clauses.append(ActionableClause(labelView: self, - location: range.location, + clauses.append(ActionableClause(location: range.location, length: range.length, actionText: actionText, actionBlock: actionBlock)) @@ -258,7 +254,7 @@ public typealias ActionBlock = () -> Void let string = text as NSString let range = string.range(of: actionText) - clauses.append(ActionableClause(labelView: self, location: range.location, length: range.length, actionText: actionText, + clauses.append(ActionableClause(location: range.location, length: range.length, actionText: actionText, actionBlock: { [weak self, weak delegate] in var willPerform = true @@ -400,8 +396,7 @@ public typealias ActionBlock = () -> Void let subStringRange = Range(NSRange(location: actionLocation, length: actionLength), in: text) else { continue } - label.clauses.append(ActionableClause(labelView: label, - location: actionLocation, + label.clauses.append(ActionableClause(location: actionLocation, length: actionLength, actionText: String(text[subStringRange]), actionBlock: { [weak delegate] in @@ -563,7 +558,7 @@ extension Label { return true } } - + return false } @@ -603,7 +598,6 @@ extension Label { // MARK: - UITapGestureRecognizer Override extension UITapGestureRecognizer { - func didTapAttributedTextInLabel(_ label: Label, inRange targetRange: NSRange) -> Bool { guard let attributedText = label.attributedText else { return false } @@ -620,7 +614,7 @@ extension UITapGestureRecognizer { textContainer.maximumNumberOfLines = label.numberOfLines let labelSize = label.bounds.size textContainer.size = labelSize - + let j = layoutManager.boundingRect(forGlyphRange: targetRange, in: textContainer) let indexOfCharacter = layoutManager.characterIndex(for: location(in: label), in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil) return NSLocationInRange(indexOfCharacter, targetRange) diff --git a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift index e2db0fdb..2bdab94a 100644 --- a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift +++ b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift @@ -210,7 +210,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt setAlternateActionTextAttributes([NSAttributedString.Key.underlineStyle: NSNumber(value: NSUnderlineStyle.single.rawValue)]) self.label?.attributedText = attributedText - self.label?.accessibilityTraits = .button + self.label?.accessibilityTraits = actionText?.isEmpty ?? false ? .staticText : .button } @objc public func setActionMap(_ actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { From 51820794db83eec5f1a28e37d172de0dbd56a898 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Fri, 3 May 2019 14:27:51 -0400 Subject: [PATCH 10/45] remove test val. --- MVMCoreUI/Atoms/Views/Label.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 54788654..3431a1fc 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -614,7 +614,7 @@ extension UITapGestureRecognizer { textContainer.maximumNumberOfLines = label.numberOfLines let labelSize = label.bounds.size textContainer.size = labelSize - let j = layoutManager.boundingRect(forGlyphRange: targetRange, in: textContainer) + let indexOfCharacter = layoutManager.characterIndex(for: location(in: label), in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil) return NSLocationInRange(indexOfCharacter, targetRange) From b7e99e9e5b508e9ba21e721e5dca26c487415dfb Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Sat, 4 May 2019 16:34:08 -0400 Subject: [PATCH 11/45] LabelWithInternalButton now properly relies on Label for its actions. Reworked makeWholeViewClickable. --- MVMCoreUI/Atoms/Views/Label.swift | 343 +++++++----------- .../Atoms/Views/LabelWithInternalButton.swift | 30 +- 2 files changed, 144 insertions(+), 229 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 3431a1fc..b229f62a 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -16,13 +16,8 @@ public typealias ActionBlock = () -> Void // MARK: - General Properties //------------------------------------------------------ - // Specifically used in LabelWithInternalButton to interact with UIControl. - var actionBlock: ActionBlock? - var actionText: String? - var frontText: String? - // This is here for LabelWithInternalButton - var makeWholeViewClickable = false + public var makeWholeViewClickable = false // TODO: TEST this ! // Set this property if you want updateView to update the font based on this standard and the size passed in. public var standardFontSize: CGFloat = 0.0 @@ -44,25 +39,33 @@ public typealias ActionBlock = () -> Void // MARK: - For Multi-Action Text //------------------------------------------------------ - private var clauses: [ActionableClause] = [] - var didSetGestureRecognizer = false + public var clauses: [ActionableClause] = [] { + didSet { + if !didSetGestureRecognizer { + isUserInteractionEnabled = true + didSetGestureRecognizer = true + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(textLinkTapped(_:))) + tapGesture.numberOfTapsRequired = 1 + addGestureRecognizer(tapGesture) + } + } + } + // Used for tappable links in the text. - private struct ActionableClause { + public struct ActionableClause { var location: Int? var length: Int? var actionText: String? var actionBlock: ActionBlock? - + var range: NSRange { return NSRange(location: location ?? 0, length: length ?? 0) } func performAction() { - if let action = actionBlock { - action() - } + actionBlock?() } } @@ -158,139 +161,12 @@ public typealias ActionBlock = () -> Void // MARK: - Tappable Methods //------------------------------------------------------ - /** - Provides an actionable range of text. - - - Attention: This method expects text to be set first. Otherwise, it will do nothing. - - Parameters: - - range: The range of text to be tapped. - - actionBlock: The code triggered when tapping the range of text. - */ - @objc public func addTappableLinkAttribute(range: NSRange, actionBlock: @escaping ActionBlock) { - - guard let text = self.text, - let subStringRange = Range(range, in: text) - else { return } - - clauses.append(ActionableClause(location: range.location, - length: range.length, - actionText: String(text[subStringRange]), - actionBlock: actionBlock)) - - Label.setGestureInteraction(for: self) - } - /** - Provides an actionable range of text. - - - Attention: This method expects text to be set first. Otherwise, it will do nothing. - - Parameters: - - range: The range of text to be tapped. - - actionMap: - - delegate: - - additionalData: - */ - @objc public func addTappableLinkAttribute(range: NSRange, actionMap: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) { - - guard let text = self.text, - let subStringRange = Range(range, in: text) - else { return } - - clauses.append(ActionableClause(location: range.location, - length: range.length, - actionText: String(text[subStringRange]), - actionBlock: { [weak self, weak delegate] in - var willPerform = true - - if let wSelf = self, let buttonDelegate = (delegate as? MVMCoreUIDelegateObject)?.buttonDelegate, - buttonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { - willPerform = buttonDelegate.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? false - } - - if willPerform { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegate) - } })) - - Label.setGestureInteraction(for: self) - } - - /** - Provides an actionable range of text. - - Allows actionable range to be established by a particular substring of the containing label text. - - - Attention: This method expects text to be set first. Otherwise, it will do nothing. Do not use if actionText is not unique in the Label's text. - - Parameters: - - actionText: The actionable text contained witin the label's text. - - actionBlock: The code triggered when tapping the range of text. - */ - @objc public func addTappableLinkAttribute(actionText: String, actionBlock: @escaping ActionBlock) { - - guard let text = self.text else { return } - let string = text as NSString - let range = string.range(of: actionText) - - clauses.append(ActionableClause(location: range.location, - length: range.length, - actionText: actionText, - actionBlock: actionBlock)) - - Label.setGestureInteraction(for: self) - } - - /** - Provides an actionable range of text. - - - Attention: This method expects text to be set first. Otherwise, it will do nothing. Do not use if actionText is not unique in the Label's text. - - Parameters: - - actionText: The actionable text contained witin the label's text. - - actionMap: - - delegate: - - additionalData: - */ - @objc public func addTappableLinkAttribute(actionText: String, actionMap: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) { - - guard let text = self.text else { return } - let string = text as NSString - let range = string.range(of: actionText) - - clauses.append(ActionableClause(location: range.location, length: range.length, actionText: actionText, - actionBlock: { [weak self, weak delegate] in - var willPerform = true - - if let wSelf = self, let buttonDelegate = (delegate as? MVMCoreUIDelegateObject)?.buttonDelegate, - buttonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { - willPerform = buttonDelegate.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? false - } - - if willPerform { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegate) - } })) - - Label.setGestureInteraction(for: self) - } //------------------------------------------------------ // MARK: - Functions //------------------------------------------------------ - /** - Makes the view interactive and applies the gesture recognizer. - - - Parameters: - - label: The current label view that will have an actionable range of text. - */ - private static func setGestureInteraction(for label: Label) { - - if !label.didSetGestureRecognizer { - label.isUserInteractionEnabled = true - label.didSetGestureRecognizer = true - let tapGesture = UITapGestureRecognizer(target: label, action: #selector(textLinkTapped(_:))) - tapGesture.numberOfTapsRequired = 1 - label.addGestureRecognizer(tapGesture) - } - } - /** Makes the view interactive and applies the gesture recognizer. @@ -410,7 +286,6 @@ public typealias ActionBlock = () -> Void if willPerform { MVMCoreActionHandler.shared()?.handleAction(with: json, additionalData: additionalData, delegateObject: delegate) } })) - Label.setGestureInteraction(for: label) } default: continue @@ -515,90 +390,126 @@ extension Label { } -// MARK: - UIControl functionality Override +// MARK: - Multi-Action Functionality extension Label { + /** + Provides an actionable range of text. + + - Attention: This method expects text to be set first. Otherwise, it will do nothing. + - Parameters: + - range: The range of text to be tapped. + - actionBlock: The code triggered when tapping the range of text. + */ + @objc public func addTappableLinkAttribute(range: NSRange, actionBlock: @escaping ActionBlock) { + + guard let text = self.text, + let subStringRange = Range(range, in: text) + else { return } + + clauses.append(ActionableClause(location: range.location, + length: range.length, + actionText: String(text[subStringRange]), + actionBlock: actionBlock)) + } + + /** + Provides an actionable range of text. + + - Attention: This method expects text to be set first. Otherwise, it will do nothing. + - Parameters: + - range: The range of text to be tapped. + - actionMap: + - delegate: + - additionalData: + */ + @objc public func addTappableLinkAttribute(range: NSRange, actionMap: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) { + + guard let text = self.text, + let subStringRange = Range(range, in: text) + else { return } + + clauses.append(ActionableClause(location: range.location, + length: range.length, + actionText: String(text[subStringRange]), + actionBlock: { [weak self, weak delegate] in + var willPerform = true + + if let wSelf = self, let buttonDelegate = (delegate as? MVMCoreUIDelegateObject)?.buttonDelegate, + buttonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { + willPerform = buttonDelegate.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? false + } + + if willPerform { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegate) + } })) + } + + /** + Provides an actionable range of text. + + Allows actionable range to be established by a particular substring of the containing label text. + + - Attention: This method expects text to be set first. Otherwise, it will do nothing. Do not use if actionText is not unique in the Label's text. + - Parameters: + - actionText: The actionable text contained witin the label's text. + - actionBlock: The code triggered when tapping the range of text. + */ + @objc public func addTappableLinkAttribute(actionText: String, actionBlock: @escaping ActionBlock) { + + guard let text = self.text else { return } + let string = text as NSString + let range = string.range(of: actionText) + + clauses.append(ActionableClause(location: range.location, + length: range.length, + actionText: actionText, + actionBlock: actionBlock)) + } + + /** + Provides an actionable range of text. + + - Attention: This method expects text to be set first. Otherwise, it will do nothing. Do not use if actionText is not unique in the Label's text. + - Parameters: + - actionText: The actionable text contained witin the label's text. + - actionMap: + - delegate: + - additionalData: + */ + @objc public func addTappableLinkAttribute(actionText: String, actionMap: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) { + + guard let text = self.text else { return } + let string = text as NSString + let range = string.range(of: actionText) + + clauses.append(ActionableClause(location: range.location, length: range.length, actionText: actionText, + actionBlock: { [weak self, weak delegate] in + var willPerform = true + + if let wSelf = self, let buttonDelegate = (delegate as? MVMCoreUIDelegateObject)?.buttonDelegate, + buttonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { + willPerform = buttonDelegate.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? false + } + + if willPerform { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegate) + } })) + } + @objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) { for clause in clauses { - if gesture.didTapAttributedTextInLabel(self, inRange: clause.range) { + if gesture.didTapAttributedTextInLabel(self, inRange: clause.range, isWholeViewTappable: makeWholeViewClickable) { clause.performAction() } } } - - // For LabelWithInternalButton - override open func touchesEnded(_ touches: Set, with event: UIEvent?) { - - if areTouches(inActionString: touches), let action = actionBlock { - action() - } - } - - // For LabelWithInternalButton - private func areTouches(inActionString touches: Set?) -> Bool { - - if UIAccessibility.isVoiceOverRunning || makeWholeViewClickable { - return true - } - - let location: CGPoint? = touches?.first?.location(in: self) - let actionTextIndex: Int = NSRange(location: frontText?.count ?? 0, length: actionText?.count ?? 0).location - let rangeArray = getRangeArrayOfWords(in: actionText, withInitalIndex: actionTextIndex) - let rectArray = getRectArray(from: rangeArray) as? [NSValue] ?? [] - - for rect in rectArray { - let wordRect: CGRect = rect.cgRectValue - - if let position = location, wordRect.contains(position) { - return true - - } else if wordRect.origin == .zero && wordRect.size.height == 0 && wordRect.size.width == 0 { - // In case word rect is not found for any reason, make the whole label to be clicable to avoid non functioning link in production. - return true - } - } - - return false - } - - // For LabelWithInternalButton - private func getRangeArrayOfWords(in string: String?, withInitalIndex index: Int) -> [Any]? { - - var index = index - let words = string?.components(separatedBy: " ") ?? [] - var rangeArray = [AnyHashable]() - - for word in words { - let finalWord = word + " " - let length: Int = finalWord.count - let wordRange = NSRange(location: index, length: length) - let rangeValue = NSValue(range: wordRange) - rangeArray.append(rangeValue) - index += length - } - - return rangeArray - } - - // For LabelWithInternalButton - private func getRectArray(from rangeArray: [Any]?) -> [Any]? { - - var rectArray = [AnyHashable]() - - for range in rangeArray as? [NSValue] ?? [] { - let rectValue = NSValue(cgRect: boundingRect(forCharacterRange: range.rangeValue)) - rectArray.append(rectValue) - } - - return rectArray - } } -// MARK: - UITapGestureRecognizer Override extension UITapGestureRecognizer { - func didTapAttributedTextInLabel(_ label: Label, inRange targetRange: NSRange) -> Bool { + func didTapAttributedTextInLabel(_ label: Label, inRange targetRange: NSRange, isWholeViewTappable: Bool) -> Bool { guard let attributedText = label.attributedText else { return false } @@ -617,6 +528,12 @@ extension UITapGestureRecognizer { let indexOfCharacter = layoutManager.characterIndex(for: location(in: label), in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil) - return NSLocationInRange(indexOfCharacter, targetRange) + var range = targetRange + + if isWholeViewTappable, let wholeRange = NSRange(attributedText.string) { + range = wholeRange + } + + return NSLocationInRange(indexOfCharacter, range) } } diff --git a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift index 2bdab94a..c5981e5e 100644 --- a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift +++ b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift @@ -61,7 +61,14 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt public var actionBlock: ActionBlock? { willSet(newActionBlock) { - label?.actionBlock = newActionBlock + if newActionBlock == nil { + label?.clauses = [] + } else { + label?.clauses = [Label.ActionableClause(location: actionRange.location, + length: actionRange.length, + actionText: actionText, + actionBlock: newActionBlock)] + } } } @@ -69,6 +76,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt return NSRange(location: frontText?.count ?? 0, length: actionText?.count ?? 0) } + // Makes entire range of text clickable public var makeWholeViewClickable = false { willSet(newBool) { label?.makeWholeViewClickable = newBool @@ -81,18 +89,8 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt } } - public var frontText: String? { - willSet(newFrontText) { - label?.frontText = newFrontText - } - } - - public var actionText: String? { - willSet(newActionText) { - label?.actionText = newActionText - } - } - + public var frontText: String? + public var actionText: String? public var backText: String? private var internalText: String = "" @@ -209,8 +207,8 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt // Adding the underline setAlternateActionTextAttributes([NSAttributedString.Key.underlineStyle: NSNumber(value: NSUnderlineStyle.single.rawValue)]) - self.label?.attributedText = attributedText - self.label?.accessibilityTraits = actionText?.isEmpty ?? false ? .staticText : .button + label?.attributedText = attributedText + label?.accessibilityTraits = actionText?.isEmpty ?? false ? .staticText : .button } @objc public func setActionMap(_ actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { @@ -538,10 +536,10 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt createLabel() self.frontText = frontText - setActionMap(actionMap, additionalData: additionalData, actionDelegate: delegate, buttonDelegate: buttonDelegate) self.actionText = actionText self.backText = backText text = getTextFromStringComponents() + setActionMap(actionMap, additionalData: additionalData, actionDelegate: delegate, buttonDelegate: buttonDelegate) setLabelAttributes() } From f1474ca74c4e2bf8dad12b8a5cce0262612ec2a3 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Mon, 6 May 2019 12:03:54 -0400 Subject: [PATCH 12/45] removed comment. --- MVMCoreUI/Atoms/Views/Label.swift | 7 ------- 1 file changed, 7 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index b229f62a..43990ecb 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -157,12 +157,6 @@ public typealias ActionBlock = () -> Void return Label(frame: .zero) } - //------------------------------------------------------ - // MARK: - Tappable Methods - //------------------------------------------------------ - - - //------------------------------------------------------ // MARK: - Functions //------------------------------------------------------ @@ -392,7 +386,6 @@ extension Label { // MARK: - Multi-Action Functionality extension Label { - /** Provides an actionable range of text. From dadfc837441784def67bcd8239cfe66602e5b5ab Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Tue, 7 May 2019 09:36:10 -0400 Subject: [PATCH 13/45] requested changes. --- MVMCoreUI/Atoms/Views/Label.swift | 66 ++++++++++++++++--------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 43990ecb..15e5cf1b 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -166,9 +166,9 @@ public typealias ActionBlock = () -> Void - Parameters: - label: The label to be set. - - html: The url link to be applied as an attributed link. + - html: any html string. */ - @objc public static func setLabel(_ label: UILabel?, with html: String?) { + @objc public static func setLabel(_ label: UILabel?, withHTML html: String?) { guard let data = html?.data(using: .utf8) else { return } @@ -195,11 +195,11 @@ public typealias ActionBlock = () -> Void */ @objc public static func setUILabel(_ label: UILabel?, withJSON json: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) { - guard let label = label as? Label else { return } + guard let label = label else { return } label.text = json?.optionalStringForKey(KeyText) - setLabel(label, with: json?.optionalStringForKey("html")) + setLabel(label, withHTML: json?.optionalStringForKey("html")) if let textColorHex = json?.optionalStringForKey(KeyTextColor), !textColorHex.isEmpty { label.textColor = UIColor.mfGet(forHex: textColorHex) @@ -257,8 +257,10 @@ public typealias ActionBlock = () -> Void attributedString.addAttribute(.font, value: font, range: range) } case "actions": - let actions = attribute.arrayForKey("actions") - guard let text = json?.optionalStringForKey(KeyText) else { continue } + guard let actionLabel = label as? Label, + let text = actionLabel.text, + let actions = attribute.optionalArrayForKey("actions") + else { continue } for case let action as [String: Any] in actions { guard let actionLocation = action["location"] as? Int, @@ -266,20 +268,20 @@ public typealias ActionBlock = () -> Void let subStringRange = Range(NSRange(location: actionLocation, length: actionLength), in: text) else { continue } - label.clauses.append(ActionableClause(location: actionLocation, - length: actionLength, - actionText: String(text[subStringRange]), - actionBlock: { [weak delegate] in - var willPerform = true - - if let buttonDelegate = (delegate as? MVMCoreUIDelegateObject)?.buttonDelegate, - buttonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { - willPerform = buttonDelegate.button?(label, shouldPerformActionWithMap: json, additionalData: additionalData) ?? false - } - - if willPerform { - MVMCoreActionHandler.shared()?.handleAction(with: json, additionalData: additionalData, delegateObject: delegate) - } })) + actionLabel.clauses.append(ActionableClause(location: actionLocation, + length: actionLength, + actionText: String(text[subStringRange]), + actionBlock: { [weak delegate] in + var willPerform = true + + if let buttonDelegate = (delegate as? MVMCoreUIDelegateObject)?.buttonDelegate, + buttonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { + willPerform = buttonDelegate.button?(actionLabel, shouldPerformActionWithMap: json, additionalData: additionalData) ?? false + } + + if willPerform { + MVMCoreActionHandler.shared()?.handleAction(with: json, additionalData: additionalData, delegateObject: delegate) + } })) } default: continue @@ -391,8 +393,8 @@ extension Label { - Attention: This method expects text to be set first. Otherwise, it will do nothing. - Parameters: - - range: The range of text to be tapped. - - actionBlock: The code triggered when tapping the range of text. + - range: The range of text to be tapped. + - actionBlock: The code triggered when tapping the range of text. */ @objc public func addTappableLinkAttribute(range: NSRange, actionBlock: @escaping ActionBlock) { @@ -411,10 +413,10 @@ extension Label { - Attention: This method expects text to be set first. Otherwise, it will do nothing. - Parameters: - - range: The range of text to be tapped. - - actionMap: - - delegate: - - additionalData: + - range: The range of text to be tapped. + - actionMap: + - delegate: + - additionalData: */ @objc public func addTappableLinkAttribute(range: NSRange, actionMap: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) { @@ -445,8 +447,8 @@ extension Label { - Attention: This method expects text to be set first. Otherwise, it will do nothing. Do not use if actionText is not unique in the Label's text. - Parameters: - - actionText: The actionable text contained witin the label's text. - - actionBlock: The code triggered when tapping the range of text. + - actionText: The actionable text contained witin the label's text. + - actionBlock: The code triggered when tapping the range of text. */ @objc public func addTappableLinkAttribute(actionText: String, actionBlock: @escaping ActionBlock) { @@ -465,10 +467,10 @@ extension Label { - Attention: This method expects text to be set first. Otherwise, it will do nothing. Do not use if actionText is not unique in the Label's text. - Parameters: - - actionText: The actionable text contained witin the label's text. - - actionMap: - - delegate: - - additionalData: + - actionText: The actionable text contained witin the label's text. + - actionMap: + - delegate: + - additionalData: */ @objc public func addTappableLinkAttribute(actionText: String, actionMap: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) { From 93f90bad474984d02eab8383387ec7ee27d5bf3a Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Tue, 7 May 2019 10:48:52 -0400 Subject: [PATCH 14/45] corrections made. --- MVMCoreUI/Atoms/Views/Label.swift | 108 +++--------------- .../Atoms/Views/LabelWithInternalButton.swift | 41 +------ 2 files changed, 20 insertions(+), 129 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 15e5cf1b..ad8a2d2e 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -57,7 +57,6 @@ public typealias ActionBlock = () -> Void public struct ActionableClause { var location: Int? var length: Int? - var actionText: String? var actionBlock: ActionBlock? var range: NSRange { @@ -258,30 +257,19 @@ public typealias ActionBlock = () -> Void } case "actions": guard let actionLabel = label as? Label, - let text = actionLabel.text, let actions = attribute.optionalArrayForKey("actions") else { continue } for case let action as [String: Any] in actions { guard let actionLocation = action["location"] as? Int, - let actionLength = action["length"] as? Int, - let subStringRange = Range(NSRange(location: actionLocation, length: actionLength), in: text) + let actionLength = action["length"] as? Int else { continue } actionLabel.clauses.append(ActionableClause(location: actionLocation, length: actionLength, - actionText: String(text[subStringRange]), - actionBlock: { [weak delegate] in - var willPerform = true - - if let buttonDelegate = (delegate as? MVMCoreUIDelegateObject)?.buttonDelegate, - buttonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { - willPerform = buttonDelegate.button?(actionLabel, shouldPerformActionWithMap: json, additionalData: additionalData) ?? false - } - - if willPerform { - MVMCoreActionHandler.shared()?.handleAction(with: json, additionalData: additionalData, delegateObject: delegate) - } })) + actionBlock: actionLabel.createActionBlockFrom(actionMap: json, + additionalData: additionalData, + delegate: delegate))) } default: continue @@ -291,6 +279,14 @@ public typealias ActionBlock = () -> Void } } + func createActionBlockFrom(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegate: DelegateObject?) -> ActionBlock { + return { [weak delegate] in + if (delegate as? MVMCoreUIDelegateObject)?.buttonDelegate?.button?(self, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegate) + } + } + } + //------------------------------------------------------ // MARK: - Methods //------------------------------------------------------ @@ -385,9 +381,9 @@ extension Label { } } - // MARK: - Multi-Action Functionality extension Label { + /** Provides an actionable range of text. @@ -398,13 +394,8 @@ extension Label { */ @objc public func addTappableLinkAttribute(range: NSRange, actionBlock: @escaping ActionBlock) { - guard let text = self.text, - let subStringRange = Range(range, in: text) - else { return } - clauses.append(ActionableClause(location: range.location, length: range.length, - actionText: String(text[subStringRange]), actionBlock: actionBlock)) } @@ -418,78 +409,13 @@ extension Label { - delegate: - additionalData: */ - @objc public func addTappableLinkAttribute(range: NSRange, actionMap: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) { - - guard let text = self.text, - let subStringRange = Range(range, in: text) - else { return } + @objc public func addTappableLinkAttribute(range: NSRange, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegate: DelegateObject?) { clauses.append(ActionableClause(location: range.location, length: range.length, - actionText: String(text[subStringRange]), - actionBlock: { [weak self, weak delegate] in - var willPerform = true - - if let wSelf = self, let buttonDelegate = (delegate as? MVMCoreUIDelegateObject)?.buttonDelegate, - buttonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { - willPerform = buttonDelegate.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? false - } - - if willPerform { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegate) - } })) - } - - /** - Provides an actionable range of text. - - Allows actionable range to be established by a particular substring of the containing label text. - - - Attention: This method expects text to be set first. Otherwise, it will do nothing. Do not use if actionText is not unique in the Label's text. - - Parameters: - - actionText: The actionable text contained witin the label's text. - - actionBlock: The code triggered when tapping the range of text. - */ - @objc public func addTappableLinkAttribute(actionText: String, actionBlock: @escaping ActionBlock) { - - guard let text = self.text else { return } - let string = text as NSString - let range = string.range(of: actionText) - - clauses.append(ActionableClause(location: range.location, - length: range.length, - actionText: actionText, - actionBlock: actionBlock)) - } - - /** - Provides an actionable range of text. - - - Attention: This method expects text to be set first. Otherwise, it will do nothing. Do not use if actionText is not unique in the Label's text. - - Parameters: - - actionText: The actionable text contained witin the label's text. - - actionMap: - - delegate: - - additionalData: - */ - @objc public func addTappableLinkAttribute(actionText: String, actionMap: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) { - - guard let text = self.text else { return } - let string = text as NSString - let range = string.range(of: actionText) - - clauses.append(ActionableClause(location: range.location, length: range.length, actionText: actionText, - actionBlock: { [weak self, weak delegate] in - var willPerform = true - - if let wSelf = self, let buttonDelegate = (delegate as? MVMCoreUIDelegateObject)?.buttonDelegate, - buttonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { - willPerform = buttonDelegate.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? false - } - - if willPerform { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegate) - } })) + actionBlock: createActionBlockFrom(actionMap: actionMap, + additionalData: additionalData, + delegate: delegate))) } @objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) { diff --git a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift index c5981e5e..dbb4cfc5 100644 --- a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift +++ b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift @@ -66,7 +66,6 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt } else { label?.clauses = [Label.ActionableClause(location: actionRange.location, length: actionRange.length, - actionText: actionText, actionBlock: newActionBlock)] } } @@ -166,19 +165,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt super.init(frame: .zero) setText(fullText, startTag: startTag, endTag: endTag) - - actionBlock = { [weak self, weak delegateObject] in - var performAction = true - - if let wSelf = self, let wButtonDelegate = (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate, - wButtonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { - performAction = wButtonDelegate.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? false - } - - if performAction { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) - } - } + actionBlock = label?.createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegate: delegateObject) } //------------------------------------------------------ @@ -213,18 +200,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt @objc public func setActionMap(_ actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { - actionBlock = { [weak self, weak delegateObject] in - var performAction = true - - if let wSelf = self, let wButtonDelegate = (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate, - wButtonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { - performAction = wButtonDelegate.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? false - } - - if performAction { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) - } - } + actionBlock = label?.createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegate: delegateObject) } //------------------------------------------------------ @@ -409,18 +385,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt actionText = actionMap?.optionalStringForKey(KeyTitle) backText = actionMap?.optionalStringForKey(KeyTitlePostfix) - actionBlock = { [weak self, weak delegateObject] in - var performAction = true - - if let wSelf = self, let wButtonDelegate = (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate, - wButtonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { - performAction = wButtonDelegate.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? false - } - - if performAction { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) - } - } + actionBlock = label?.createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegate: delegateObject) text = getTextFromStringComponents() setLabelAttributes() From 59248974ab889d9f957a1475b6280755bf5dc4db Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Fri, 10 May 2019 13:09:58 -0400 Subject: [PATCH 15/45] Changes made to Label & LabelWithInternalButton. --- MVMCoreUI/Atoms/Views/Label.swift | 140 ++++++++++-------- .../Atoms/Views/LabelWithInternalButton.swift | 51 +++---- 2 files changed, 95 insertions(+), 96 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index ad8a2d2e..f006afc7 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -11,20 +11,19 @@ import MVMCore public typealias ActionBlock = () -> Void + @objcMembers open class Label: UILabel, MVMCoreViewProtocol, MFButtonProtocol, MVMCoreUIMoleculeViewProtocol { //------------------------------------------------------ // MARK: - General Properties //------------------------------------------------------ - - // This is here for LabelWithInternalButton - public var makeWholeViewClickable = false // TODO: TEST this ! + + public var makeWholeViewClickable = false // Set this property if you want updateView to update the font based on this standard and the size passed in. public var standardFontSize: CGFloat = 0.0 // Set this to use a custom sizing object during updateView instead of the standard. public var sizeObject: MFSizeObject? - public var scaleSize: NSNumber? // Used for scaling the font in updateView. @@ -36,33 +35,18 @@ public typealias ActionBlock = () -> Void } //------------------------------------------------------ - // MARK: - For Multi-Action Text + // MARK: - Multi-Action Text //------------------------------------------------------ - var didSetGestureRecognizer = false - public var clauses: [ActionableClause] = [] { - didSet { - if !didSetGestureRecognizer { - isUserInteractionEnabled = true - didSetGestureRecognizer = true - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(textLinkTapped(_:))) - tapGesture.numberOfTapsRequired = 1 - addGestureRecognizer(tapGesture) - } - } + didSet { isUserInteractionEnabled = !clauses.isEmpty } } // Used for tappable links in the text. public struct ActionableClause { - var location: Int? - var length: Int? + var range: NSRange? var actionBlock: ActionBlock? - var range: NSRange { - return NSRange(location: location ?? 0, length: length ?? 0) - } - func performAction() { actionBlock?() } @@ -78,6 +62,10 @@ public typealias ActionBlock = () -> Void numberOfLines = 0 lineBreakMode = .byWordWrapping translatesAutoresizingMaskIntoConstraints = false + + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(textLinkTapped(_:))) + tapGesture.numberOfTapsRequired = 1 + addGestureRecognizer(tapGesture) } @objc public init() { @@ -161,8 +149,6 @@ public typealias ActionBlock = () -> Void //------------------------------------------------------ /** - Makes the view interactive and applies the gesture recognizer. - - Parameters: - label: The label to be set. - html: any html string. @@ -184,8 +170,6 @@ public typealias ActionBlock = () -> Void } /** - Makes the view interactive and applies the gesture recognizer. - - Parameters: - label: The current label view that will have an actionable range of text. - json: Contains values which det the values of Label. @@ -255,22 +239,13 @@ public typealias ActionBlock = () -> Void attributedString.removeAttribute(.font, range: range) attributedString.addAttribute(.font, value: font, range: range) } - case "actions": - guard let actionLabel = label as? Label, - let actions = attribute.optionalArrayForKey("actions") - else { continue } + case "action": + guard let actionLabel = label as? Label else { continue } - for case let action as [String: Any] in actions { - guard let actionLocation = action["location"] as? Int, - let actionLength = action["length"] as? Int - else { continue } - - actionLabel.clauses.append(ActionableClause(location: actionLocation, - length: actionLength, - actionBlock: actionLabel.createActionBlockFrom(actionMap: json, - additionalData: additionalData, - delegate: delegate))) - } + actionLabel.addActionableClause(range: range, + actionMap: json, + additionalData: additionalData, + delegateObject: delegate) default: continue } @@ -279,14 +254,55 @@ public typealias ActionBlock = () -> Void } } - func createActionBlockFrom(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegate: DelegateObject?) -> ActionBlock { - return { [weak delegate] in - if (delegate as? MVMCoreUIDelegateObject)?.buttonDelegate?.button?(self, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegate) + /// Reseting to default Label values. + @objc public func clearActionableClauses() { + + guard let attributedText = attributedText else { return } + let mutableAttributedString = NSMutableAttributedString(attributedString: attributedText) + + clauses.forEach { clause in + + guard let range = clause.range else { return } + mutableAttributedString.removeAttribute(NSAttributedString.Key.underlineStyle, range: range) + } + + self.attributedText = mutableAttributedString + clauses = [] + } + + public func createActionBlockFrom(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) -> ActionBlock { + return { + if (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate?.button?(self, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } } } + private func addActionableClause(range: NSRange, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { + + setDefaultAttributes(range: range) + clauses.append(ActionableClause(range: range, + actionBlock: createActionBlockFrom(actionMap: actionMap, + additionalData: additionalData, + delegateObject: delegateObject))) + } + + private func addActionableClause(range: NSRange, actionBlock: ActionBlock?) { + + setDefaultAttributes(range: range) + clauses.append(ActionableClause(range: range, actionBlock: actionBlock)) + } + + fileprivate func setDefaultAttributes(range: NSRange) { + + guard let attributedText = attributedText else { return } + + let mutableAttributedString = NSMutableAttributedString(attributedString: attributedText) + mutableAttributedString.addAttributes([NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue], range: range) + + self.attributedText = mutableAttributedString + } + //------------------------------------------------------ // MARK: - Methods //------------------------------------------------------ @@ -372,6 +388,7 @@ public typealias ActionBlock = () -> Void extension Label { @objc public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { + clauses = [] Label.setUILabel(self, withJSON: json, delegate: delegateObject, additionalData: additionalData) originalAttributedString = attributedText } @@ -394,9 +411,8 @@ extension Label { */ @objc public func addTappableLinkAttribute(range: NSRange, actionBlock: @escaping ActionBlock) { - clauses.append(ActionableClause(location: range.location, - length: range.length, - actionBlock: actionBlock)) + addActionableClause(range: range, + actionBlock: actionBlock) } /** @@ -409,28 +425,29 @@ extension Label { - delegate: - additionalData: */ - @objc public func addTappableLinkAttribute(range: NSRange, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegate: DelegateObject?) { + @objc public func addTappableLinkAttribute(range: NSRange, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { - clauses.append(ActionableClause(location: range.location, - length: range.length, - actionBlock: createActionBlockFrom(actionMap: actionMap, - additionalData: additionalData, - delegate: delegate))) + addActionableClause(range: range, + actionMap: actionMap, + additionalData: additionalData, + delegateObject: delegateObject) } @objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) { for clause in clauses { - if gesture.didTapAttributedTextInLabel(self, inRange: clause.range, isWholeViewTappable: makeWholeViewClickable) { + if let range = clause.range, gesture.didTapAttributedTextInLabel(self, inRange: range) { clause.performAction() + return } } } } +// MARK: - extension UITapGestureRecognizer { - func didTapAttributedTextInLabel(_ label: Label, inRange targetRange: NSRange, isWholeViewTappable: Bool) -> Bool { + func didTapAttributedTextInLabel(_ label: Label, inRange targetRange: NSRange) -> Bool { guard let attributedText = label.attributedText else { return false } @@ -444,17 +461,14 @@ extension UITapGestureRecognizer { textContainer.lineFragmentPadding = 0.0 textContainer.lineBreakMode = label.lineBreakMode textContainer.maximumNumberOfLines = label.numberOfLines - let labelSize = label.bounds.size - textContainer.size = labelSize + textContainer.size = label.bounds.size let indexOfCharacter = layoutManager.characterIndex(for: location(in: label), in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil) - var range = targetRange - - if isWholeViewTappable, let wholeRange = NSRange(attributedText.string) { - range = wholeRange + if label.makeWholeViewClickable { + return true } - return NSLocationInRange(indexOfCharacter, range) + return NSLocationInRange(indexOfCharacter, targetRange) } } diff --git a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift index dbb4cfc5..74393bd8 100644 --- a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift +++ b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift @@ -24,7 +24,6 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt public var attributedText: NSAttributedString? { willSet(newAttributedText) { if let newAttribText = newAttributedText { - let mutableAttributedText = NSMutableAttributedString(attributedString: newAttribText) let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = CGFloat(LabelWithInternalButtonLineSpace) @@ -64,18 +63,23 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt if newActionBlock == nil { label?.clauses = [] } else { - label?.clauses = [Label.ActionableClause(location: actionRange.location, - length: actionRange.length, - actionBlock: newActionBlock)] + label?.clauses = [Label.ActionableClause(range: actionRange, actionBlock: newActionBlock)] } } } + private var frontRange: NSRange { + return NSRange(location: 0, length: frontText?.count ?? 0) + } + private var actionRange: NSRange { return NSRange(location: frontText?.count ?? 0, length: actionText?.count ?? 0) } - // Makes entire range of text clickable + private var backRange: NSRange { + return NSRange(location: (frontText?.count ?? 0) + (actionText?.count ?? 0), length: backText?.count ?? 0) + } + public var makeWholeViewClickable = false { willSet(newBool) { label?.makeWholeViewClickable = newBool @@ -165,7 +169,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt super.init(frame: .zero) setText(fullText, startTag: startTag, endTag: endTag) - actionBlock = label?.createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegate: delegateObject) + actionBlock = label?.createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) } //------------------------------------------------------ @@ -195,12 +199,12 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt setAlternateActionTextAttributes([NSAttributedString.Key.underlineStyle: NSNumber(value: NSUnderlineStyle.single.rawValue)]) label?.attributedText = attributedText - label?.accessibilityTraits = actionText?.isEmpty ?? false ? .staticText : .button + label?.accessibilityTraits = actionText?.isEmpty ?? true ? .staticText : .button } @objc public func setActionMap(_ actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { - actionBlock = label?.createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegate: delegateObject) + actionBlock = label?.createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) } //------------------------------------------------------ @@ -252,7 +256,6 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt } attributedText = mutableAttributedString - // Added this line for underlining setAlternateActionTextAttributes([NSAttributedString.Key.underlineStyle: NSNumber(value: NSUnderlineStyle.single.rawValue)]) } @@ -346,27 +349,14 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt public func setAlternateNormalTextAttributes(_ attributes: [AnyHashable: Any]?) { - guard let _attributedText = attributedText, let _attributes = attributes as? [NSAttributedString.Key: Any] else { return } - let attributedString = NSMutableAttributedString(attributedString: _attributedText) + guard let attributedText = attributedText, let attributes = attributes as? [NSAttributedString.Key: Any] else { return } + let attributedString = NSMutableAttributedString(attributedString: attributedText) if !attributedString.string.isEmpty { - attributedString.addAttributes(_attributes, range: getFrontRange()) - attributedString.addAttributes(_attributes, range: getBackRange()) + attributedString.addAttributes(attributes, range: frontRange) + attributedString.addAttributes(attributes, range: backRange) } - attributedText = attributedString - } - - private func getFrontRange() -> NSRange { - - return NSRange(location: 0, length: frontText?.count ?? 0) - } - - private func getBackRange() -> NSRange { - - let textLocation: Int = (frontText?.count ?? 0) + (actionText?.count ?? 0) - let rangeLength: Int = backText?.count ?? 0 - - return NSRange(location: textLocation, length: rangeLength) + self.attributedText = attributedString } @objc public func setActionTextString(_ actionText: String?) { @@ -384,9 +374,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt frontText = actionMap?.optionalStringForKey(KeyTitlePrefix) actionText = actionMap?.optionalStringForKey(KeyTitle) backText = actionMap?.optionalStringForKey(KeyTitlePostfix) - - actionBlock = label?.createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegate: delegateObject) - + actionBlock = label?.createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) text = getTextFromStringComponents() setLabelAttributes() } @@ -433,13 +421,11 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt @available(*, deprecated) public convenience init(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, actionDelegate delegate: ActionObjectDelegate?, buttonDelegate: ButtonObjectDelegate?) { - self.init(frontText: actionMap?.optionalStringForKey(KeyTitlePrefix), actionText: actionMap?.optionalStringForKey(KeyTitle), backText: actionMap?.optionalStringForKey(KeyTitlePostfix), actionMap: actionMap, additionalData: additionalData, actionDelegate: delegate, buttonDelegate: buttonDelegate) } @available(*, deprecated) public convenience init(frontText: String?, backText: String?, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, actionDelegate delegate: ActionObjectDelegate?, buttonDelegate: ButtonObjectDelegate?) { - self.init(frontText: frontText, actionText: actionMap?.optionalStringForKey(KeyTitle), backText: backText, actionMap: actionMap, additionalData: additionalData, actionDelegate: delegate, buttonDelegate: buttonDelegate) } @@ -544,7 +530,6 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt } attributedText = mutableAttributedString - // Added this line for underlining setAlternateActionTextAttributes([NSAttributedString.Key.underlineStyle: NSNumber(value: NSUnderlineStyle.single.rawValue)]) } From ec1682a6329391ac5a9b23f7d5a91f134777be50 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Tue, 14 May 2019 16:26:35 -0400 Subject: [PATCH 16/45] Updated Label. Change LabelWithInternal for when nil is set for actionBlock. --- MVMCoreUI/Atoms/Views/Label.swift | 119 +++++++++--------- .../Atoms/Views/LabelWithInternalButton.swift | 25 ++-- 2 files changed, 65 insertions(+), 79 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index f006afc7..5ead8b61 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -92,18 +92,21 @@ public typealias ActionBlock = () -> Void // MARK: - Factory Functions //------------------------------------------------------ + /// H1 -> HeadlineLarge @objc public static func commonLabelH1(_ scale: Bool) -> Label { let label = Label.label() label.styleH1(scale) return label } + /// H2 -> Headline @objc public static func commonLabelH2(_ scale: Bool) -> Label { let label = Label.label() label.styleH2(scale) return label } + /// H3 -> SubHead @objc public static func commonLabelH3(_ scale: Bool) -> Label { let label = Label.label() label.styleH3(scale) @@ -116,18 +119,21 @@ public typealias ActionBlock = () -> Void return label } + /// B1 -> SubTitle @objc public static func commonLabelB1(_ scale: Bool) -> Label { let label = Label.label() label.styleB1(scale) return label } + /// B2 -> Body @objc public static func commonLabelB2(_ scale: Bool) -> Label { let label = Label.label() label.styleB2(scale) return label } + /// B3 -> Legal @objc public static func commonLabelB3(_ scale: Bool) -> Label { let label = Label.label() label.styleB3(scale) @@ -242,10 +248,10 @@ public typealias ActionBlock = () -> Void case "action": guard let actionLabel = label as? Label else { continue } - actionLabel.addActionableClause(range: range, - actionMap: json, - additionalData: additionalData, - delegateObject: delegate) + actionLabel.addTappableLinkAttribute(range: range, + actionMap: json, + additionalData: additionalData, + delegateObject: delegate) default: continue } @@ -254,54 +260,7 @@ public typealias ActionBlock = () -> Void } } - /// Reseting to default Label values. - @objc public func clearActionableClauses() { - - guard let attributedText = attributedText else { return } - let mutableAttributedString = NSMutableAttributedString(attributedString: attributedText) - - clauses.forEach { clause in - - guard let range = clause.range else { return } - mutableAttributedString.removeAttribute(NSAttributedString.Key.underlineStyle, range: range) - } - - self.attributedText = mutableAttributedString - clauses = [] - } - - public func createActionBlockFrom(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) -> ActionBlock { - return { - if (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate?.button?(self, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { - MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) - } - } - } - - private func addActionableClause(range: NSRange, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { - - setDefaultAttributes(range: range) - clauses.append(ActionableClause(range: range, - actionBlock: createActionBlockFrom(actionMap: actionMap, - additionalData: additionalData, - delegateObject: delegateObject))) - } - - private func addActionableClause(range: NSRange, actionBlock: ActionBlock?) { - - setDefaultAttributes(range: range) - clauses.append(ActionableClause(range: range, actionBlock: actionBlock)) - } - - fileprivate func setDefaultAttributes(range: NSRange) { - - guard let attributedText = attributedText else { return } - - let mutableAttributedString = NSMutableAttributedString(attributedString: attributedText) - mutableAttributedString.addAttributes([NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue], range: range) - - self.attributedText = mutableAttributedString - } + //------------------------------------------------------ // MARK: - Methods @@ -401,6 +360,39 @@ extension Label { // MARK: - Multi-Action Functionality extension Label { + /// Reseting to default Label values. + @objc public func clearActionableClauses() { + + guard let attributedText = attributedText else { return } + let mutableAttributedString = NSMutableAttributedString(attributedString: attributedText) + + clauses.forEach { clause in + guard let range = clause.range else { return } + mutableAttributedString.removeAttribute(NSAttributedString.Key.underlineStyle, range: range) + } + + self.attributedText = mutableAttributedString + clauses = [] + } + + public func createActionBlockFrom(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) -> ActionBlock { + return { + if (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate?.button?(self, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { + MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) + } + } + } + + fileprivate func setDefaultAttributes(range: NSRange) { + + guard let attributedText = attributedText else { return } + + let mutableAttributedString = NSMutableAttributedString(attributedString: attributedText) + mutableAttributedString.addAttributes([NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue], range: range) + + self.attributedText = mutableAttributedString + } + /** Provides an actionable range of text. @@ -411,8 +403,8 @@ extension Label { */ @objc public func addTappableLinkAttribute(range: NSRange, actionBlock: @escaping ActionBlock) { - addActionableClause(range: range, - actionBlock: actionBlock) + setDefaultAttributes(range: range) + clauses.append(ActionableClause(range: range, actionBlock: actionBlock)) } /** @@ -427,10 +419,11 @@ extension Label { */ @objc public func addTappableLinkAttribute(range: NSRange, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) { - addActionableClause(range: range, - actionMap: actionMap, - additionalData: additionalData, - delegateObject: delegateObject) + setDefaultAttributes(range: range) + clauses.append(ActionableClause(range: range, + actionBlock: createActionBlockFrom(actionMap: actionMap, + additionalData: additionalData, + delegateObject: delegateObject))) } @objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) { @@ -449,6 +442,10 @@ extension UITapGestureRecognizer { func didTapAttributedTextInLabel(_ label: Label, inRange targetRange: NSRange) -> Bool { + if label.makeWholeViewClickable { + return true + } + guard let attributedText = label.attributedText else { return false } let layoutManager = NSLayoutManager() @@ -464,11 +461,7 @@ extension UITapGestureRecognizer { textContainer.size = label.bounds.size let indexOfCharacter = layoutManager.characterIndex(for: location(in: label), in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil) - - if label.makeWholeViewClickable { - return true - } - + return NSLocationInRange(indexOfCharacter, targetRange) } } diff --git a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift index 74393bd8..56fe5eb1 100644 --- a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift +++ b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift @@ -61,7 +61,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt public var actionBlock: ActionBlock? { willSet(newActionBlock) { if newActionBlock == nil { - label?.clauses = [] + label?.clearActionableClauses() } else { label?.clauses = [Label.ActionableClause(range: actionRange, actionBlock: newActionBlock)] } @@ -81,15 +81,11 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt } public var makeWholeViewClickable = false { - willSet(newBool) { - label?.makeWholeViewClickable = newBool - } + willSet(newBool) { label?.makeWholeViewClickable = newBool } } override open var isEnabled: Bool { - didSet { - alpha = isEnabled ? 1 : DisableOppacity - } + didSet { alpha = isEnabled ? 1 : DisableOppacity } } public var frontText: String? @@ -133,7 +129,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt setLabelAttributes() } - // legacy + // Legacy public init(frontText: String?, actionText: String?, backText: String?, actionBlock block: ActionBlock?) { super.init(frame: .zero) @@ -141,8 +137,8 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt self.frontText = frontText self.actionText = actionText self.backText = backText - actionBlock = block text = getTextFromStringComponents() + actionBlock = block setLabelAttributes() } @@ -181,7 +177,6 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt if self.label == nil { let label = Label(frame: .zero) - label.isUserInteractionEnabled = true label.setContentCompressionResistancePriority(.required, for: .vertical) addSubview(label) @@ -215,10 +210,10 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt createLabel() self.frontText = frontText - setActionMap(actionMap, additionalData: additionalData, delegateObject: delegateObject) self.actionText = actionText self.backText = backText text = getTextFromStringComponents() + setActionMap(actionMap, additionalData: additionalData, delegateObject: delegateObject) setLabelAttributes() } @@ -307,7 +302,6 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt } } } - self.text = getTextFromStringComponents() setLabelAttributes() } @@ -374,8 +368,8 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt frontText = actionMap?.optionalStringForKey(KeyTitlePrefix) actionText = actionMap?.optionalStringForKey(KeyTitle) backText = actionMap?.optionalStringForKey(KeyTitlePostfix) - actionBlock = label?.createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) text = getTextFromStringComponents() + actionBlock = label?.createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject) setLabelAttributes() } @@ -578,22 +572,21 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt @objc public func reset(withActionMap actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegate: ActionObjectDelegate?) { createLabel() - frontText = actionMap?.optionalStringForKey(KeyTitlePrefix) actionText = actionMap?.optionalStringForKey(KeyTitle) backText = actionMap?.optionalStringForKey(KeyTitlePostfix) + text = getTextFromStringComponents() actionBlock = { [weak delegate] in MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegate: delegate as? CoreObjectActionLoadPresentDelegate) } - - text = getTextFromStringComponents() setLabelAttributes() } } // MARK: - Atomization extension LabelWithInternalButton: MVMCoreUIMoleculeViewProtocol { + // Default values for view. @objc open func setAsMolecule() { From 0f9aa7dc01ca8639058b8e16cab9b27ebb707331 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Mon, 20 May 2019 16:34:02 -0400 Subject: [PATCH 17/45] Adding new atom. --- MVMCoreUI/Atoms/Views/ItemDetailView.swift | 109 +++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 MVMCoreUI/Atoms/Views/ItemDetailView.swift diff --git a/MVMCoreUI/Atoms/Views/ItemDetailView.swift b/MVMCoreUI/Atoms/Views/ItemDetailView.swift new file mode 100644 index 00000000..7533d416 --- /dev/null +++ b/MVMCoreUI/Atoms/Views/ItemDetailView.swift @@ -0,0 +1,109 @@ +// +// ItemDetailView.swift +// MVMCoreUI +// +// Created by Christiano, Kevin on 5/20/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import Foundation + + +@objcMembers open class ItemDetailView: ViewConstrainingView { + //------------------------------------------------------ + // MARK: - Properties + //------------------------------------------------------ + + var title: Label? + var message: Label? + var detail: Label? + + //------------------------------------------------------ + // MARK: - Initialization + //------------------------------------------------------ + + public init() { + super.init(frame: .zero) + } + + public override init(frame: CGRect) { + super.init(frame: frame) + } + + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + public convenience init(actionMap: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { + self.init() + setWithJSON(actionMap: actionMap, delegateObject: delegateObject, additionalData: additionalData) + } + + override open func setupView() { + super.setupView() + + translatesAutoresizingMaskIntoConstraints = false + defaultState() + } + + //------------------------------------------------------ + // MARK: - Setup + //------------------------------------------------------ + + override open func updateView(_ size: CGFloat) { + super.updateView(size) + } + + func defaultState() { + + if title == nil { + title = Label.commonLabelB1(false) + message = Label.commonLabelB2(false) + detail = Label.commonLabelB1(false) + + if let title = title, let message = message, let detail = detail { + addSubview(title) + addSubview(message) + addSubview(detail) + + title.textAlignment = .left + message.textAlignment = .left + detail.textAlignment = .right + + NSLayoutConstraint.constraintPinSubview(title, pinTop: true, topConstant: PaddingTwo, pinBottom: false, bottomConstant: 0, pinLeft: true, leftConstant: 16, pinRight: false, rightConstant: 0) + NSLayoutConstraint.constraintPinSubview(message, pinTop: false, topConstant: 0, pinBottom: true, bottomConstant: PaddingTwo, pinLeft: true, leftConstant: 16, pinRight: true, rightConstant: 16) + NSLayoutConstraint.constraintPinSubview(detail, pinTop: true, topConstant: PaddingTwo, pinBottom: false, bottomConstant: 0, pinLeft: false, leftConstant: 0, pinRight: true, rightConstant: 16) + NSLayoutConstraint(pinFirstView: title, toSecondView: message, withConstant: 0, directionVertical: true)?.isActive = true + + title.trailingAnchor.constraint(lessThanOrEqualTo: detail.leadingAnchor, constant: PaddingOne).isActive = true + } + } + } + + func setWithJSON(actionMap: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { + + guard let dictionary = actionMap else { return } + + title?.text = dictionary.stringForkey(KeyTitle) + message?.text = dictionary.stringForkey(KeyMessage) + detail?.text = dictionary.stringForkey("detail") + + if let backgroundColorHex = dictionary[KeyBackgroundColor] as? String { + backgroundColor = UIColor.mfGet(forHex: backgroundColorHex) + } + } + + //------------------------------------------------------ + // MARK: - Atomization + //------------------------------------------------------ + + open override func setAsMolecule() { + super.setAsMolecule() + defaultState() + } + + open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { + super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + setWithJSON(actionMap: json, delegateObject: delegateObject, additionalData: additionalData) + } +} From c4ad3cc8811fa1095bb48bedbca04d4800592ed3 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Mon, 20 May 2019 16:34:17 -0400 Subject: [PATCH 18/45] adding new atom. --- MVMCoreUI.xcodeproj/project.pbxproj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 83b51092..7d026dc1 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -159,6 +159,7 @@ D2E1FADD2268B25E00AEFD8C /* MoleculeTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADC2268B25E00AEFD8C /* MoleculeTableViewCell.swift */; }; D2E1FADF2268B8E700AEFD8C /* ThreeLayerTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */; }; D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */; }; + DB06250B2293456500B72DD3 /* ItemDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB06250A2293456500B72DD3 /* ItemDetailView.swift */; }; DBC4391822442197001AB423 /* CaretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391622442196001AB423 /* CaretView.swift */; }; DBC4391922442197001AB423 /* DashLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391722442197001AB423 /* DashLine.swift */; }; DBC4391B224421A0001AB423 /* CaretButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391A224421A0001AB423 /* CaretButton.swift */; }; @@ -319,10 +320,11 @@ D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIViewControllerMappingObject.h; sourceTree = ""; }; D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIViewControllerMappingObject.m; sourceTree = ""; }; D2E1FADA2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUIDelegateObject.swift; sourceTree = ""; }; - DB891E822253FA8500022516 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; D2E1FADC2268B25E00AEFD8C /* MoleculeTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeTableViewCell.swift; sourceTree = ""; }; D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerTableViewController.swift; sourceTree = ""; }; D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeListTemplate.swift; sourceTree = ""; }; + DB06250A2293456500B72DD3 /* ItemDetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemDetailView.swift; sourceTree = ""; }; + DB891E822253FA8500022516 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; DBC4391622442196001AB423 /* CaretView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretView.swift; sourceTree = ""; }; DBC4391722442197001AB423 /* DashLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DashLine.swift; sourceTree = ""; }; DBC4391A224421A0001AB423 /* CaretButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretButton.swift; sourceTree = ""; }; @@ -569,6 +571,7 @@ children = ( DBC4391622442196001AB423 /* CaretView.swift */, DBC4391722442197001AB423 /* DashLine.swift */, + DB06250A2293456500B72DD3 /* ItemDetailView.swift */, D29DF17E21E69E2E003B2FB9 /* MFView.h */, D29DF17F21E69E2E003B2FB9 /* MFView.m */, D29DF31E21ED0CBA003B2FB9 /* LabelView.h */, @@ -920,6 +923,7 @@ D29DF29521E7ADB8003B2FB9 /* ProgrammaticScrollViewController.m in Sources */, D29DF16121E69996003B2FB9 /* MFViewController.m in Sources */, D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */, + DB06250B2293456500B72DD3 /* ItemDetailView.swift in Sources */, D22D1F47220496A30077CEC0 /* MVMCoreUISwitch.m in Sources */, D29DF28C21E7AC2B003B2FB9 /* ViewConstrainingView.m in Sources */, D29DF17B21E69E1F003B2FB9 /* PrimaryButton.m in Sources */, From 6d2220cbbf9b97beb912df9808ff46c9971cd6f3 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Fri, 24 May 2019 09:19:22 -0400 Subject: [PATCH 19/45] latest version of ItemDetailView. --- MVMCoreUI/Atoms/Views/ItemDetailView.swift | 121 +++++++++++++++++---- 1 file changed, 98 insertions(+), 23 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/ItemDetailView.swift b/MVMCoreUI/Atoms/Views/ItemDetailView.swift index 7533d416..b21e8504 100644 --- a/MVMCoreUI/Atoms/Views/ItemDetailView.swift +++ b/MVMCoreUI/Atoms/Views/ItemDetailView.swift @@ -18,6 +18,13 @@ import Foundation var message: Label? var detail: Label? + var titleWidth: NSLayoutConstraint? + var messageWidth: NSLayoutConstraint? + var detailWidth: NSLayoutConstraint? + + var messageTrail: NSLayoutConstraint? + var titleTrail: NSLayoutConstraint? + //------------------------------------------------------ // MARK: - Initialization //------------------------------------------------------ @@ -52,31 +59,99 @@ import Foundation override open func updateView(_ size: CGFloat) { super.updateView(size) + + if let titleText = title?.text, let messageText = message?.text, titleText.isEmpty && messageText.isEmpty { + detailWidth?.constant = size + detail?.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -PaddingTwo).isActive = true + return + } + + if let detailText = detail?.text, detailText.isEmpty { + messageTrail?.isActive = false + titleTrail?.isActive = false + message?.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true + title?.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true + detailWidth?.constant = 0 + titleWidth?.constant = size + messageWidth?.constant = size + return + } + + if let titleText = title?.text, titleText.isEmpty { + messageWidth?.constant = size * 0.6 - PaddingOne + detailWidth?.constant = size * 0.2 + titleWidth?.constant = 0 + return + } + + if let messageText = message?.text, messageText.isEmpty { + titleWidth?.constant = size * 0.6 - PaddingOne + detailWidth?.constant = size * 0.2 + messageWidth?.constant = 0 + return + } + + titleWidth?.constant = size * 0.6 - PaddingOne + messageWidth?.constant = size * 0.6 - PaddingOne + detailWidth?.constant = size * 0.2 } func defaultState() { - if title == nil { - title = Label.commonLabelB1(false) - message = Label.commonLabelB2(false) - detail = Label.commonLabelB1(false) + if title == nil && message == nil && detail == nil { - if let title = title, let message = message, let detail = detail { - addSubview(title) - addSubview(message) - addSubview(detail) - - title.textAlignment = .left - message.textAlignment = .left - detail.textAlignment = .right - - NSLayoutConstraint.constraintPinSubview(title, pinTop: true, topConstant: PaddingTwo, pinBottom: false, bottomConstant: 0, pinLeft: true, leftConstant: 16, pinRight: false, rightConstant: 0) - NSLayoutConstraint.constraintPinSubview(message, pinTop: false, topConstant: 0, pinBottom: true, bottomConstant: PaddingTwo, pinLeft: true, leftConstant: 16, pinRight: true, rightConstant: 16) - NSLayoutConstraint.constraintPinSubview(detail, pinTop: true, topConstant: PaddingTwo, pinBottom: false, bottomConstant: 0, pinLeft: false, leftConstant: 0, pinRight: true, rightConstant: 16) - NSLayoutConstraint(pinFirstView: title, toSecondView: message, withConstant: 0, directionVertical: true)?.isActive = true - - title.trailingAnchor.constraint(lessThanOrEqualTo: detail.leadingAnchor, constant: PaddingOne).isActive = true - } + let title = Label.commonLabelB1(false) + let message = Label.commonLabelB2(false) + let detail = Label.commonLabelB1(false) + + self.title = title + self.message = message + self.detail = detail + + addSubview(title) + addSubview(message) + addSubview(detail) + + title.textAlignment = .left + message.textAlignment = .left + detail.textAlignment = .right + + NSLayoutConstraint.constraintPinSubview(title, pinTop: true, topConstant: PaddingTwo, pinBottom: false, bottomConstant: 0, pinLeft: true, leftConstant: 0, pinRight: false, rightConstant: 0) + NSLayoutConstraint.constraintPinSubview(message, pinTop: false, topConstant: 0, pinBottom: true, bottomConstant: PaddingTwo, pinLeft: true, leftConstant: 0, pinRight: false, rightConstant: 0) + NSLayoutConstraint.constraintPinSubview(detail, pinTop: true, topConstant: PaddingTwo, pinBottom: false, bottomConstant: 0, pinLeft: false, leftConstant: 0, pinRight: true, rightConstant: 0) + NSLayoutConstraint(pinFirstView: title, toSecondView: message, withConstant: 0, directionVertical: true)?.isActive = true + + let titleTrail = title.trailingAnchor.constraint(greaterThanOrEqualTo: detail.leadingAnchor, constant: PaddingOne) + titleTrail.isActive = true + let messageTrail = message.trailingAnchor.constraint(greaterThanOrEqualTo: detail.leadingAnchor, constant: PaddingOne) + messageTrail.isActive = true + + titleWidth = title.widthAnchor.constraint(lessThanOrEqualToConstant: bounds.width * 0.8 - PaddingOne) + titleWidth?.priority = UILayoutPriority(rawValue: 750) + titleWidth?.isActive = true + + messageWidth = message.widthAnchor.constraint(lessThanOrEqualToConstant: bounds.width * 0.8 - PaddingOne) + messageWidth?.priority = UILayoutPriority(rawValue: 750) + messageWidth?.isActive = true + + detailWidth = detail.widthAnchor.constraint(equalToConstant: bounds.width * 0.2) + detailWidth?.priority = UILayoutPriority(rawValue: 755) + detailWidth?.isActive = true + + title.setContentHuggingPriority(UILayoutPriority(rawValue: 251), for: .horizontal) + title.setContentHuggingPriority(UILayoutPriority(rawValue: 249), for: .vertical) + title.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 750), for: .horizontal) + title.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 250), for: .vertical) + + message.setContentHuggingPriority(UILayoutPriority(rawValue: 251), for: .horizontal) + message.setContentHuggingPriority(UILayoutPriority(rawValue: 249), for: .vertical) + message.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 749), for: .horizontal) + message.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 250), for: .vertical) + + detail.setContentHuggingPriority(UILayoutPriority(rawValue: 251), for: .horizontal) + detail.setContentHuggingPriority(UILayoutPriority(rawValue: 249), for: .vertical) + detail.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 750), for: .horizontal) + detail.setContentCompressionResistancePriority(.required, for: .vertical) } } @@ -84,9 +159,9 @@ import Foundation guard let dictionary = actionMap else { return } - title?.text = dictionary.stringForkey(KeyTitle) - message?.text = dictionary.stringForkey(KeyMessage) - detail?.text = dictionary.stringForkey("detail") + title?.setWithJSON(dictionary.optionalDictionaryForKey("title"), delegateObject: delegateObject, additionalData: additionalData) + message?.setWithJSON(dictionary.optionalDictionaryForKey("message"), delegateObject: delegateObject, additionalData: additionalData) + detail?.setWithJSON(dictionary.optionalDictionaryForKey("detail"), delegateObject: delegateObject, additionalData: additionalData) if let backgroundColorHex = dictionary[KeyBackgroundColor] as? String { backgroundColor = UIColor.mfGet(forHex: backgroundColorHex) From 8c2b00657e06e9137dfdec22599221b72db29fde Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Fri, 24 May 2019 12:58:48 -0400 Subject: [PATCH 20/45] added ItemDetailView to molecule mapping. --- MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 0cc21b30..6b77e631 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -35,7 +35,8 @@ @"textField" : MFTextField.class, @"checkbox" : MVMCoreUICheckBox.class, @"switchLineItem" : SwitchLineItem.class, - @"switch" : Switch.class + @"switch" : Switch.class, + @"itemDetailView": ItemDetailView.class } mutableCopy]; }); return mapping; From f1ba116e8bada3e5cdb562b041ddd7081f5f479a Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Fri, 24 May 2019 14:58:54 -0400 Subject: [PATCH 21/45] switch to true. --- MVMCoreUI/Atoms/Views/ItemDetailView.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/ItemDetailView.swift b/MVMCoreUI/Atoms/Views/ItemDetailView.swift index b21e8504..ef532bcc 100644 --- a/MVMCoreUI/Atoms/Views/ItemDetailView.swift +++ b/MVMCoreUI/Atoms/Views/ItemDetailView.swift @@ -100,9 +100,9 @@ import Foundation if title == nil && message == nil && detail == nil { - let title = Label.commonLabelB1(false) - let message = Label.commonLabelB2(false) - let detail = Label.commonLabelB1(false) + let title = Label.commonLabelB1(true) + let message = Label.commonLabelB2(true) + let detail = Label.commonLabelB1(true) self.title = title self.message = message From 68f27da11739107b148a68180332ad5ce2a73f85 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Wed, 29 May 2019 10:23:39 -0400 Subject: [PATCH 22/45] Changed ItemDetailView name to StandardListItem to comply with molecule listing. --- MVMCoreUI.xcodeproj/project.pbxproj | 8 ++++---- .../{ItemDetailView.swift => StandardListItem.swift} | 2 +- MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) rename MVMCoreUI/Atoms/Views/{ItemDetailView.swift => StandardListItem.swift} (99%) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 56ec5860..23674bbc 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -161,7 +161,7 @@ D2E1FADD2268B25E00AEFD8C /* MoleculeTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADC2268B25E00AEFD8C /* MoleculeTableViewCell.swift */; }; D2E1FADF2268B8E700AEFD8C /* ThreeLayerTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */; }; D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */; }; - DB06250B2293456500B72DD3 /* ItemDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB06250A2293456500B72DD3 /* ItemDetailView.swift */; }; + DB06250B2293456500B72DD3 /* StandardListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB06250A2293456500B72DD3 /* StandardListItem.swift */; }; DBC4391822442197001AB423 /* CaretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391622442196001AB423 /* CaretView.swift */; }; DBC4391922442197001AB423 /* DashLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391722442197001AB423 /* DashLine.swift */; }; DBC4391B224421A0001AB423 /* CaretButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391A224421A0001AB423 /* CaretButton.swift */; }; @@ -327,7 +327,7 @@ D2E1FADC2268B25E00AEFD8C /* MoleculeTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeTableViewCell.swift; sourceTree = ""; }; D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerTableViewController.swift; sourceTree = ""; }; D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeListTemplate.swift; sourceTree = ""; }; - DB06250A2293456500B72DD3 /* ItemDetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemDetailView.swift; sourceTree = ""; }; + DB06250A2293456500B72DD3 /* StandardListItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StandardListItem.swift; sourceTree = ""; }; DB891E822253FA8500022516 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; DBC4391622442196001AB423 /* CaretView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretView.swift; sourceTree = ""; }; DBC4391722442197001AB423 /* DashLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DashLine.swift; sourceTree = ""; }; @@ -577,7 +577,7 @@ children = ( DBC4391622442196001AB423 /* CaretView.swift */, DBC4391722442197001AB423 /* DashLine.swift */, - DB06250A2293456500B72DD3 /* ItemDetailView.swift */, + DB06250A2293456500B72DD3 /* StandardListItem.swift */, D29DF17E21E69E2E003B2FB9 /* MFView.h */, D29DF17F21E69E2E003B2FB9 /* MFView.m */, D29DF31E21ED0CBA003B2FB9 /* LabelView.h */, @@ -930,7 +930,7 @@ D29DF29521E7ADB8003B2FB9 /* ProgrammaticScrollViewController.m in Sources */, D29DF16121E69996003B2FB9 /* MFViewController.m in Sources */, D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */, - DB06250B2293456500B72DD3 /* ItemDetailView.swift in Sources */, + DB06250B2293456500B72DD3 /* StandardListItem.swift in Sources */, D22D1F47220496A30077CEC0 /* MVMCoreUISwitch.m in Sources */, D29DF28C21E7AC2B003B2FB9 /* ViewConstrainingView.m in Sources */, D29DF17B21E69E1F003B2FB9 /* PrimaryButton.m in Sources */, diff --git a/MVMCoreUI/Atoms/Views/ItemDetailView.swift b/MVMCoreUI/Atoms/Views/StandardListItem.swift similarity index 99% rename from MVMCoreUI/Atoms/Views/ItemDetailView.swift rename to MVMCoreUI/Atoms/Views/StandardListItem.swift index ef532bcc..b7e23755 100644 --- a/MVMCoreUI/Atoms/Views/ItemDetailView.swift +++ b/MVMCoreUI/Atoms/Views/StandardListItem.swift @@ -9,7 +9,7 @@ import Foundation -@objcMembers open class ItemDetailView: ViewConstrainingView { +@objcMembers open class StandardListItem: ViewConstrainingView { //------------------------------------------------------ // MARK: - Properties //------------------------------------------------------ diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 6b77e631..b76674dd 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -36,7 +36,7 @@ @"checkbox" : MVMCoreUICheckBox.class, @"switchLineItem" : SwitchLineItem.class, @"switch" : Switch.class, - @"itemDetailView": ItemDetailView.class + @"standardListItem": StandardListItem.class } mutableCopy]; }); return mapping; From 7b2cfb7abb4df7f96c3c04e4743b922ec714a77b Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Wed, 29 May 2019 10:57:53 -0400 Subject: [PATCH 23/45] space. --- MVMCoreUI/Atoms/Views/StandardListItem.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/MVMCoreUI/Atoms/Views/StandardListItem.swift b/MVMCoreUI/Atoms/Views/StandardListItem.swift index 96e47f6f..e4ef7133 100644 --- a/MVMCoreUI/Atoms/Views/StandardListItem.swift +++ b/MVMCoreUI/Atoms/Views/StandardListItem.swift @@ -123,6 +123,7 @@ import Foundation let titleTrail = title.trailingAnchor.constraint(greaterThanOrEqualTo: detail.leadingAnchor, constant: PaddingOne) titleTrail.isActive = true + let messageTrail = message.trailingAnchor.constraint(greaterThanOrEqualTo: detail.leadingAnchor, constant: PaddingOne) messageTrail.isActive = true From 6dae7e9299742c50f72f088c825abab47f2d6098 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Wed, 29 May 2019 11:41:18 -0400 Subject: [PATCH 24/45] making some comments readable from option key. --- MVMCoreUI/Atoms/Views/Label.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index c8bd0981..65ac4c70 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -19,14 +19,14 @@ public typealias ActionBlock = () -> Void public var makeWholeViewClickable = false - // Set this property if you want updateView to update the font based on this standard and the size passed in. + /// Set this property if you want updateView to update the font based on this standard and the size passed in. public var standardFontSize: CGFloat = 0.0 - // Set this to use a custom sizing object during updateView instead of the standard. + /// Set this to use a custom sizing object during updateView instead of the standard. public var sizeObject: MFSizeObject? public var scaleSize: NSNumber? - // Used for scaling the font in updateView. + /// Used for scaling the font in updateView. private var originalAttributedString: NSAttributedString? public var hasText: Bool { @@ -42,7 +42,7 @@ public typealias ActionBlock = () -> Void didSet { isUserInteractionEnabled = !clauses.isEmpty } } - // Used for tappable links in the text. + /// Used for tappable links in the text. public struct ActionableClause { var range: NSRange? var actionBlock: ActionBlock? From 845036b4ec8639efaecc340e2dba538c076d95ec Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Wed, 29 May 2019 21:10:29 -0400 Subject: [PATCH 25/45] Changed atom name. making changes to LabelWithIntenralButton. --- MVMCoreUI.xcodeproj/project.pbxproj | 8 ++-- MVMCoreUI/Atoms/Views/Label.swift | 18 ++----- .../Atoms/Views/LabelWithInternalButton.swift | 2 +- ...istItem.swift => LeftRightLabelView.swift} | 48 +++++++++++++------ .../MVMCoreUIMoleculeMappingObject.m | 2 +- 5 files changed, 42 insertions(+), 36 deletions(-) rename MVMCoreUI/Atoms/Views/{StandardListItem.swift => LeftRightLabelView.swift} (79%) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 79ac0668..f63e075d 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -164,7 +164,7 @@ D2E1FADD2268B25E00AEFD8C /* MoleculeTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADC2268B25E00AEFD8C /* MoleculeTableViewCell.swift */; }; D2E1FADF2268B8E700AEFD8C /* ThreeLayerTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */; }; D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */; }; - DB06250B2293456500B72DD3 /* StandardListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB06250A2293456500B72DD3 /* StandardListItem.swift */; }; + DB06250B2293456500B72DD3 /* LeftRightLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */; }; DBC4391822442197001AB423 /* CaretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391622442196001AB423 /* CaretView.swift */; }; DBC4391922442197001AB423 /* DashLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391722442197001AB423 /* DashLine.swift */; }; DBC4391B224421A0001AB423 /* CaretButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC4391A224421A0001AB423 /* CaretButton.swift */; }; @@ -333,7 +333,7 @@ D2E1FADC2268B25E00AEFD8C /* MoleculeTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeTableViewCell.swift; sourceTree = ""; }; D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerTableViewController.swift; sourceTree = ""; }; D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeListTemplate.swift; sourceTree = ""; }; - DB06250A2293456500B72DD3 /* StandardListItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StandardListItem.swift; sourceTree = ""; }; + DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LeftRightLabelView.swift; sourceTree = ""; }; DB891E822253FA8500022516 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; DBC4391622442196001AB423 /* CaretView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaretView.swift; sourceTree = ""; }; DBC4391722442197001AB423 /* DashLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DashLine.swift; sourceTree = ""; }; @@ -584,7 +584,7 @@ children = ( DBC4391622442196001AB423 /* CaretView.swift */, DBC4391722442197001AB423 /* DashLine.swift */, - DB06250A2293456500B72DD3 /* StandardListItem.swift */, + DB06250A2293456500B72DD3 /* LeftRightLabelView.swift */, D29DF17E21E69E2E003B2FB9 /* MFView.h */, D29DF17F21E69E2E003B2FB9 /* MFView.m */, D29DF31E21ED0CBA003B2FB9 /* LabelView.h */, @@ -942,7 +942,7 @@ D29DF29521E7ADB8003B2FB9 /* ProgrammaticScrollViewController.m in Sources */, D29DF16121E69996003B2FB9 /* MFViewController.m in Sources */, D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */, - DB06250B2293456500B72DD3 /* StandardListItem.swift in Sources */, + DB06250B2293456500B72DD3 /* LeftRightLabelView.swift in Sources */, D22D1F47220496A30077CEC0 /* MVMCoreUISwitch.m in Sources */, D29DF28C21E7AC2B003B2FB9 /* ViewConstrainingView.m in Sources */, D29DF17B21E69E1F003B2FB9 /* PrimaryButton.m in Sources */, diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 65ac4c70..e0bb2a25 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -154,11 +154,6 @@ public typealias ActionBlock = () -> Void // MARK: - Functions //------------------------------------------------------ - /** - - Parameters: - - label: The label to be set. - - html: any html string. - */ @objc public static func setLabel(_ label: UILabel?, withHTML html: String?) { guard let data = html?.data(using: .utf8) else { return } @@ -175,17 +170,10 @@ public typealias ActionBlock = () -> Void } } - /** - - Parameters: - - label: The current label view that will have an actionable range of text. - - json: Contains values which det the values of Label. - - delegate: - - additionalData: - */ @objc public static func setUILabel(_ label: UILabel?, withJSON json: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) { guard let label = label else { return } - + label.attributedText = nil label.text = json?.optionalStringForKey(KeyText) setLabel(label, withHTML: json?.optionalStringForKey("html")) @@ -376,8 +364,8 @@ extension Label { } public func createActionBlockFrom(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) -> ActionBlock { - return { - if (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate?.button?(self, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { + return { [weak self] in + if (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate?.button?(self!, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } } diff --git a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift index e7d30fb0..76fb8db4 100644 --- a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift +++ b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift @@ -450,7 +450,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt setText(fullText, startTag: startTag, endTag: endTag) - actionBlock = { + actionBlock = { [weak delegate] in MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegate: delegate as? CoreObjectActionLoadPresentDelegate) } } diff --git a/MVMCoreUI/Atoms/Views/StandardListItem.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift similarity index 79% rename from MVMCoreUI/Atoms/Views/StandardListItem.swift rename to MVMCoreUI/Atoms/Views/LeftRightLabelView.swift index e4ef7133..118502b6 100644 --- a/MVMCoreUI/Atoms/Views/StandardListItem.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift @@ -9,7 +9,7 @@ import Foundation -@objcMembers open class StandardListItem: ViewConstrainingView { +@objcMembers open class LeftRightLabelView: ViewConstrainingView { //------------------------------------------------------ // MARK: - Properties //------------------------------------------------------ @@ -25,6 +25,9 @@ import Foundation var messageTrail: NSLayoutConstraint? var titleTrail: NSLayoutConstraint? + var titleDetailBaseline: NSLayoutConstraint? + var messageDetailBaseline: NSLayoutConstraint? + //------------------------------------------------------ // MARK: - Initialization //------------------------------------------------------ @@ -41,7 +44,7 @@ import Foundation super.init(coder: aDecoder) } - public convenience init(actionMap: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + public convenience init(actionMap: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { self.init() setWithJSON(actionMap: actionMap, delegateObject: delegateObject, additionalData: additionalData) } @@ -60,9 +63,14 @@ import Foundation override open func updateView(_ size: CGFloat) { super.updateView(size) + title?.updateView(size) + message?.updateView(size) + detail?.updateView(size) + if let titleText = title?.text, let messageText = message?.text, titleText.isEmpty && messageText.isEmpty { detailWidth?.constant = size detail?.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -PaddingTwo).isActive = true + titleDetailBaseline?.isActive = false return } @@ -81,6 +89,8 @@ import Foundation messageWidth?.constant = size * 0.6 - PaddingOne detailWidth?.constant = size * 0.2 titleWidth?.constant = 0 + titleDetailBaseline?.isActive = false + messageDetailBaseline?.isActive = true return } @@ -116,19 +126,27 @@ import Foundation message.textAlignment = .left detail.textAlignment = .right - NSLayoutConstraint.constraintPinSubview(title, pinTop: true, topConstant: PaddingTwo, pinBottom: false, bottomConstant: 0, pinLeft: true, leftConstant: 0, pinRight: false, rightConstant: 0) - NSLayoutConstraint.constraintPinSubview(message, pinTop: false, topConstant: 0, pinBottom: true, bottomConstant: PaddingTwo, pinLeft: true, leftConstant: 0, pinRight: false, rightConstant: 0) - NSLayoutConstraint.constraintPinSubview(detail, pinTop: true, topConstant: PaddingTwo, pinBottom: false, bottomConstant: 0, pinLeft: false, leftConstant: 0, pinRight: true, rightConstant: 0) - NSLayoutConstraint(pinFirstView: title, toSecondView: message, withConstant: 0, directionVertical: true)?.isActive = true - - let titleTrail = title.trailingAnchor.constraint(greaterThanOrEqualTo: detail.leadingAnchor, constant: PaddingOne) + title.topAnchor.constraint(equalTo: topAnchor).isActive = true + title.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + let titleTrail = title.trailingAnchor.constraint(greaterThanOrEqualTo: detail.leadingAnchor) titleTrail.isActive = true - let messageTrail = message.trailingAnchor.constraint(greaterThanOrEqualTo: detail.leadingAnchor, constant: PaddingOne) + message.topAnchor.constraint(equalTo: title.bottomAnchor).isActive = true + message.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + message.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true + let messageTrail = message.trailingAnchor.constraint(greaterThanOrEqualTo: detail.leadingAnchor) messageTrail.isActive = true + detail.topAnchor.constraint(equalTo: topAnchor).isActive = true + detail.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true + + titleDetailBaseline = detail.firstBaselineAnchor.constraint(equalTo: title.firstBaselineAnchor) + titleDetailBaseline?.isActive = true + + messageDetailBaseline = detail.firstBaselineAnchor.constraint(equalTo: message.firstBaselineAnchor) + titleWidth = title.widthAnchor.constraint(lessThanOrEqualToConstant: bounds.width * 0.8 - PaddingOne) - titleWidth?.priority = UILayoutPriority(rawValue: 750) + titleWidth?.priority = UILayoutPriority(rawValue: 751) titleWidth?.isActive = true messageWidth = message.widthAnchor.constraint(lessThanOrEqualToConstant: bounds.width * 0.8 - PaddingOne) @@ -136,7 +154,7 @@ import Foundation messageWidth?.isActive = true detailWidth = detail.widthAnchor.constraint(equalToConstant: bounds.width * 0.2) - detailWidth?.priority = UILayoutPriority(rawValue: 755) + detailWidth?.priority = UILayoutPriority(rawValue: 850) detailWidth?.isActive = true title.setContentHuggingPriority(UILayoutPriority(rawValue: 251), for: .horizontal) @@ -149,14 +167,14 @@ import Foundation message.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 749), for: .horizontal) message.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 250), for: .vertical) - detail.setContentHuggingPriority(UILayoutPriority(rawValue: 251), for: .horizontal) + detail.setContentHuggingPriority(UILayoutPriority(rawValue: 750), for: .horizontal) detail.setContentHuggingPriority(UILayoutPriority(rawValue: 249), for: .vertical) detail.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 750), for: .horizontal) - detail.setContentCompressionResistancePriority(.required, for: .vertical) + detail.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 999), for: .vertical) } } - func setWithJSON(actionMap: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + func setWithJSON(actionMap: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { guard let dictionary = actionMap else { return } @@ -178,7 +196,7 @@ import Foundation defaultState() } - open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) setWithJSON(actionMap: json, delegateObject: delegateObject, additionalData: additionalData) } diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index e03c495f..30f0aff6 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -38,7 +38,7 @@ @"listItem": MoleculeTableViewCell.class, @"switchLineItem": SwitchLineItem.class, @"switch": Switch.class, - @"standardListItem": StandardListItem.class + @"leftRightLabelView": LeftRightLabelView.class } mutableCopy]; }); return mapping; From efec81871c0ab02525ce53c34382ca7cc17b8d27 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Thu, 30 May 2019 14:13:44 -0400 Subject: [PATCH 26/45] corrections made. --- MVMCoreUI/Atoms/Views/Label.swift | 2 +- .../Atoms/Views/LabelWithInternalButton.swift | 21 +++++++------------ .../Atoms/Views/LeftRightLabelView.swift | 8 +++---- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index e0bb2a25..d604a2e7 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -365,7 +365,7 @@ extension Label { public func createActionBlockFrom(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) -> ActionBlock { return { [weak self] in - if (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate?.button?(self!, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { + if let wSelf = self, (delegateObject as? MVMCoreUIDelegateObject)?.buttonDelegate?.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } } diff --git a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift index 76fb8db4..2befda5a 100644 --- a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift +++ b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift @@ -14,7 +14,8 @@ public typealias ButtonObjectDelegate = NSObjectProtocol & ButtonDelegateProtoco public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProtocol & MVMCoreLoadDelegateProtocol & MVMCorePresentationDelegateProtocol & NSObjectProtocol -@objcMembers open class LabelWithInternalButton: UIControl, MVMCoreViewProtocol, MFButtonProtocol { +@available(*, deprecated, message: "This class is deprecated, please use the Label class.") +@objcMembers open class LabelWithInternalButton: UIControl, MVMCoreViewProtocol, MFButtonProtocol, MVMCoreUIMoleculeViewProtocol { //------------------------------------------------------ // MARK: - Properties //------------------------------------------------------ @@ -177,7 +178,6 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt if self.label == nil { let label = Label(frame: .zero) - label.isUserInteractionEnabled = true label.setContentCompressionResistancePriority(.required, for: .vertical) addSubview(label) NSLayoutConstraint.constraintPinSubview(label, pinTop: true, pinBottom: true, pinLeft: true, pinRight: true) @@ -458,14 +458,9 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt @available(*, deprecated) private func setActionMap(_ actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, actionDelegate delegate: ActionObjectDelegate?, buttonDelegate: ButtonObjectDelegate?) { - actionBlock = { [weak self, weak buttonDelegate] in - var performAction = true - - if let wSelf = self, let wButtonDelegate = buttonDelegate, wButtonDelegate.responds(to: #selector(ButtonObjectDelegate.button(_:shouldPerformActionWithMap:additionalData:))) { - performAction = wButtonDelegate.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? false - } - - if performAction { + actionBlock = { [weak self, weak delegate, weak buttonDelegate] in + + if let wSelf = self, buttonDelegate?.button?(wSelf, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegate: delegate as? CoreObjectActionLoadPresentDelegate) } } @@ -582,10 +577,10 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt } setLabelAttributes() } -} -// MARK: - Atomization -extension LabelWithInternalButton: MVMCoreUIMoleculeViewProtocol { + //------------------------------------------------------ + // MARK: - Atomization + //------------------------------------------------------ // Default values for view. @objc open func setAsMolecule() { diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift index 118502b6..8573ca87 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift @@ -178,9 +178,9 @@ import Foundation guard let dictionary = actionMap else { return } - title?.setWithJSON(dictionary.optionalDictionaryForKey("title"), delegateObject: delegateObject, additionalData: additionalData) - message?.setWithJSON(dictionary.optionalDictionaryForKey("message"), delegateObject: delegateObject, additionalData: additionalData) - detail?.setWithJSON(dictionary.optionalDictionaryForKey("detail"), delegateObject: delegateObject, additionalData: additionalData) + title?.setWithJSON(dictionary.optionalDictionaryForKey("title"), delegateObject: delegateObject as! MVMCoreUIDelegateObject, additionalData: additionalData) + message?.setWithJSON(dictionary.optionalDictionaryForKey("message"), delegateObject: delegateObject as! MVMCoreUIDelegateObject, additionalData: additionalData) + detail?.setWithJSON(dictionary.optionalDictionaryForKey("detail"), delegateObject: delegateObject as! MVMCoreUIDelegateObject, additionalData: additionalData) if let backgroundColorHex = dictionary[KeyBackgroundColor] as? String { backgroundColor = UIColor.mfGet(forHex: backgroundColorHex) @@ -197,7 +197,7 @@ import Foundation } open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + super.setWithJSON(json, delegateObject: delegateObject as! MVMCoreUIDelegateObject, additionalData: additionalData) setWithJSON(actionMap: json, delegateObject: delegateObject, additionalData: additionalData) } } From c884b97fff7c582defe7b88e2d7b67eca64a28fa Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Fri, 31 May 2019 11:38:30 -0400 Subject: [PATCH 27/45] Changed the way LeftRightLabelView works. --- .../Atoms/Views/LeftRightLabelView.swift | 170 ++++++------------ 1 file changed, 58 insertions(+), 112 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift index 8573ca87..220e4fb0 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift @@ -14,19 +14,8 @@ import Foundation // MARK: - Properties //------------------------------------------------------ - var title: Label? - var message: Label? - var detail: Label? - - var titleWidth: NSLayoutConstraint? - var messageWidth: NSLayoutConstraint? - var detailWidth: NSLayoutConstraint? - - var messageTrail: NSLayoutConstraint? - var titleTrail: NSLayoutConstraint? - - var titleDetailBaseline: NSLayoutConstraint? - var messageDetailBaseline: NSLayoutConstraint? + var leftText: Label? + var rightText: Label? //------------------------------------------------------ // MARK: - Initialization @@ -51,126 +40,80 @@ import Foundation override open func setupView() { super.setupView() - translatesAutoresizingMaskIntoConstraints = false defaultState() } + //------------------------------------------------------ + // MARK: - View Lifecycle + //------------------------------------------------------ + + override open func updateView(_ size: CGFloat) { + super.updateView(size) + + leftText?.updateView(size) + rightText?.updateView(size) + + layoutIfNeeded() + layoutSubviews() + } + //------------------------------------------------------ // MARK: - Setup //------------------------------------------------------ - override open func updateView(_ size: CGFloat) { - super.updateView(size) - - title?.updateView(size) - message?.updateView(size) - detail?.updateView(size) - - if let titleText = title?.text, let messageText = message?.text, titleText.isEmpty && messageText.isEmpty { - detailWidth?.constant = size - detail?.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -PaddingTwo).isActive = true - titleDetailBaseline?.isActive = false - return - } - - if let detailText = detail?.text, detailText.isEmpty { - messageTrail?.isActive = false - titleTrail?.isActive = false - message?.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true - title?.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true - detailWidth?.constant = 0 - titleWidth?.constant = size - messageWidth?.constant = size - return - } - - if let titleText = title?.text, titleText.isEmpty { - messageWidth?.constant = size * 0.6 - PaddingOne - detailWidth?.constant = size * 0.2 - titleWidth?.constant = 0 - titleDetailBaseline?.isActive = false - messageDetailBaseline?.isActive = true - return - } - - if let messageText = message?.text, messageText.isEmpty { - titleWidth?.constant = size * 0.6 - PaddingOne - detailWidth?.constant = size * 0.2 - messageWidth?.constant = 0 - return - } - - titleWidth?.constant = size * 0.6 - PaddingOne - messageWidth?.constant = size * 0.6 - PaddingOne - detailWidth?.constant = size * 0.2 - } - func defaultState() { - if title == nil && message == nil && detail == nil { + if leftText == nil && rightText == nil { - let title = Label.commonLabelB1(true) - let message = Label.commonLabelB2(true) - let detail = Label.commonLabelB1(true) + let leftText = Label.commonLabelB1(true) + let rightText = Label.commonLabelB1(true) - self.title = title - self.message = message - self.detail = detail + self.leftText = leftText + self.rightText = rightText - addSubview(title) - addSubview(message) - addSubview(detail) + addSubview(leftText) + addSubview(rightText) - title.textAlignment = .left - message.textAlignment = .left - detail.textAlignment = .right + leftText.textAlignment = .left + rightText.textAlignment = .right - title.topAnchor.constraint(equalTo: topAnchor).isActive = true - title.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true - let titleTrail = title.trailingAnchor.constraint(greaterThanOrEqualTo: detail.leadingAnchor) - titleTrail.isActive = true + // leftText.topAnchor.constraint(equalTo: topAnchor).isActive = true + // leftText.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + leftText.trailingAnchor.constraint(equalTo: rightText.leadingAnchor).isActive = true - message.topAnchor.constraint(equalTo: title.bottomAnchor).isActive = true - message.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true - message.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true - let messageTrail = message.trailingAnchor.constraint(greaterThanOrEqualTo: detail.leadingAnchor) - messageTrail.isActive = true + NSLayoutConstraint.constraintPinSubview(leftText, pinTop: true, pinBottom: false, pinLeft: true, pinRight: false) - detail.topAnchor.constraint(equalTo: topAnchor).isActive = true - detail.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true + let leftTextBottom = leftText.bottomAnchor.constraint(equalTo: bottomAnchor) + leftTextBottom.priority = UILayoutPriority(249) + leftTextBottom.isActive = true + bottomAnchor.constraint(greaterThanOrEqualTo: leftText.bottomAnchor).isActive = true - titleDetailBaseline = detail.firstBaselineAnchor.constraint(equalTo: title.firstBaselineAnchor) - titleDetailBaseline?.isActive = true + // rightText.topAnchor.constraint(equalTo: topAnchor).isActive = true + // rightText.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true + NSLayoutConstraint.constraintPinSubview(rightText, pinTop: true, pinBottom: false, pinLeft: false, pinRight: true) - messageDetailBaseline = detail.firstBaselineAnchor.constraint(equalTo: message.firstBaselineAnchor) - titleWidth = title.widthAnchor.constraint(lessThanOrEqualToConstant: bounds.width * 0.8 - PaddingOne) - titleWidth?.priority = UILayoutPriority(rawValue: 751) - titleWidth?.isActive = true + let rightTextBottom = rightText.bottomAnchor.constraint(equalTo: bottomAnchor) + rightTextBottom.priority = UILayoutPriority(rawValue: 249) + rightTextBottom.isActive = true + bottomAnchor.constraint(greaterThanOrEqualTo: rightText.bottomAnchor).isActive = true - messageWidth = message.widthAnchor.constraint(lessThanOrEqualToConstant: bounds.width * 0.8 - PaddingOne) - messageWidth?.priority = UILayoutPriority(rawValue: 750) - messageWidth?.isActive = true + let leftTextWidth = leftText.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.7) + leftTextWidth.priority = UILayoutPriority(rawValue: 100) + leftTextWidth.isActive = true - detailWidth = detail.widthAnchor.constraint(equalToConstant: bounds.width * 0.2) - detailWidth?.priority = UILayoutPriority(rawValue: 850) - detailWidth?.isActive = true + // let rightTextWidth = rightText.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.3) + // rightTextWidth.priority = UILayoutPriority(rawValue: 100) + // rightTextWidth.isActive = true - title.setContentHuggingPriority(UILayoutPriority(rawValue: 251), for: .horizontal) - title.setContentHuggingPriority(UILayoutPriority(rawValue: 249), for: .vertical) - title.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 750), for: .horizontal) - title.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 250), for: .vertical) + rightText.firstBaselineAnchor.constraint(equalTo: leftText.firstBaselineAnchor).isActive = true - message.setContentHuggingPriority(UILayoutPriority(rawValue: 251), for: .horizontal) - message.setContentHuggingPriority(UILayoutPriority(rawValue: 249), for: .vertical) - message.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 749), for: .horizontal) - message.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 250), for: .vertical) + leftText.setContentHuggingPriority(UILayoutPriority(rawValue: 801), for: .horizontal) + // leftText.setContentHuggingPriority(.required, for: .vertical) - detail.setContentHuggingPriority(UILayoutPriority(rawValue: 750), for: .horizontal) - detail.setContentHuggingPriority(UILayoutPriority(rawValue: 249), for: .vertical) - detail.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 750), for: .horizontal) - detail.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 999), for: .vertical) + rightText.setContentHuggingPriority(UILayoutPriority(rawValue: 802), for: .horizontal) + // rightText.setContentHuggingPriority(.required, for: .vertical) } } @@ -178,9 +121,8 @@ import Foundation guard let dictionary = actionMap else { return } - title?.setWithJSON(dictionary.optionalDictionaryForKey("title"), delegateObject: delegateObject as! MVMCoreUIDelegateObject, additionalData: additionalData) - message?.setWithJSON(dictionary.optionalDictionaryForKey("message"), delegateObject: delegateObject as! MVMCoreUIDelegateObject, additionalData: additionalData) - detail?.setWithJSON(dictionary.optionalDictionaryForKey("detail"), delegateObject: delegateObject as! MVMCoreUIDelegateObject, additionalData: additionalData) + leftText?.setWithJSON(dictionary.optionalDictionaryForKey("leftText"), delegateObject: delegateObject as? MVMCoreUIDelegateObject, additionalData: additionalData) + rightText?.setWithJSON(dictionary.optionalDictionaryForKey("rightText"), delegateObject: delegateObject as? MVMCoreUIDelegateObject, additionalData: additionalData) if let backgroundColorHex = dictionary[KeyBackgroundColor] as? String { backgroundColor = UIColor.mfGet(forHex: backgroundColorHex) @@ -197,7 +139,11 @@ import Foundation } open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject as! MVMCoreUIDelegateObject, additionalData: additionalData) + super.setWithJSON(json, delegateObject: delegateObject as? MVMCoreUIDelegateObject, additionalData: additionalData) setWithJSON(actionMap: json, delegateObject: delegateObject, additionalData: additionalData) } + + open override func needsToBeConstrained() -> Bool { + return true + } } From c4baffd6795562a064dc0646bc06e3ecd6937249 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Fri, 31 May 2019 12:33:56 -0400 Subject: [PATCH 28/45] Added logic for added control over label constraints. --- .../Atoms/Views/LeftRightLabelView.swift | 158 +++++++++++------- .../MVMCoreUIMoleculeMappingObject.m | 3 +- 2 files changed, 100 insertions(+), 61 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift index 220e4fb0..a2855d5a 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift @@ -9,13 +9,19 @@ import Foundation +@available(*, unavailable) @objcMembers open class LeftRightLabelView: ViewConstrainingView { //------------------------------------------------------ // MARK: - Properties //------------------------------------------------------ - var leftText: Label? - var rightText: Label? + var leftTextLabel: Label? + var rightTextLabel: Label? + + var leftTextWidth: NSLayoutConstraint? + var rightTextWidth: NSLayoutConstraint? + + var leftTextLabelTrailing: NSLayoutConstraint? //------------------------------------------------------ // MARK: - Initialization @@ -35,7 +41,7 @@ import Foundation public convenience init(actionMap: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { self.init() - setWithJSON(actionMap: actionMap, delegateObject: delegateObject, additionalData: additionalData) + setWithJSON(actionMap, delegateObject: delegateObject, additionalData: additionalData) } override open func setupView() { @@ -51,11 +57,10 @@ import Foundation override open func updateView(_ size: CGFloat) { super.updateView(size) - leftText?.updateView(size) - rightText?.updateView(size) + leftTextLabel?.updateView(size) + rightTextLabel?.updateView(size) layoutIfNeeded() - layoutSubviews() } //------------------------------------------------------ @@ -64,69 +69,88 @@ import Foundation func defaultState() { - if leftText == nil && rightText == nil { + if leftTextLabel == nil && rightTextLabel == nil { - let leftText = Label.commonLabelB1(true) - let rightText = Label.commonLabelB1(true) + let leftTextLabel = Label.commonLabelB1(true) + let rightTextLabel = Label.commonLabelB1(true) - self.leftText = leftText - self.rightText = rightText + self.leftTextLabel = leftTextLabel + self.rightTextLabel = rightTextLabel - addSubview(leftText) - addSubview(rightText) + addSubview(leftTextLabel) + addSubview(rightTextLabel) - leftText.textAlignment = .left - rightText.textAlignment = .right + leftTextLabel.textAlignment = .left + rightTextLabel.textAlignment = .right - // leftText.topAnchor.constraint(equalTo: topAnchor).isActive = true - // leftText.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true - leftText.trailingAnchor.constraint(equalTo: rightText.leadingAnchor).isActive = true - - NSLayoutConstraint.constraintPinSubview(leftText, pinTop: true, pinBottom: false, pinLeft: true, pinRight: false) - - let leftTextBottom = leftText.bottomAnchor.constraint(equalTo: bottomAnchor) - leftTextBottom.priority = UILayoutPriority(249) - leftTextBottom.isActive = true - bottomAnchor.constraint(greaterThanOrEqualTo: leftText.bottomAnchor).isActive = true - - // rightText.topAnchor.constraint(equalTo: topAnchor).isActive = true - // rightText.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true - NSLayoutConstraint.constraintPinSubview(rightText, pinTop: true, pinBottom: false, pinLeft: false, pinRight: true) - - - let rightTextBottom = rightText.bottomAnchor.constraint(equalTo: bottomAnchor) - rightTextBottom.priority = UILayoutPriority(rawValue: 249) - rightTextBottom.isActive = true - bottomAnchor.constraint(greaterThanOrEqualTo: rightText.bottomAnchor).isActive = true - - let leftTextWidth = leftText.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.7) - leftTextWidth.priority = UILayoutPriority(rawValue: 100) - leftTextWidth.isActive = true - - // let rightTextWidth = rightText.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.3) - // rightTextWidth.priority = UILayoutPriority(rawValue: 100) - // rightTextWidth.isActive = true - - rightText.firstBaselineAnchor.constraint(equalTo: leftText.firstBaselineAnchor).isActive = true - - leftText.setContentHuggingPriority(UILayoutPriority(rawValue: 801), for: .horizontal) - // leftText.setContentHuggingPriority(.required, for: .vertical) - - rightText.setContentHuggingPriority(UILayoutPriority(rawValue: 802), for: .horizontal) - // rightText.setContentHuggingPriority(.required, for: .vertical) + constrainBothLabels() } } - func setWithJSON(actionMap: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { + private func constrainBothLabels() { - guard let dictionary = actionMap else { return } + leftTextLabel?.topAnchor.constraint(equalTo: topAnchor).isActive = true + leftTextLabel?.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true - leftText?.setWithJSON(dictionary.optionalDictionaryForKey("leftText"), delegateObject: delegateObject as? MVMCoreUIDelegateObject, additionalData: additionalData) - rightText?.setWithJSON(dictionary.optionalDictionaryForKey("rightText"), delegateObject: delegateObject as? MVMCoreUIDelegateObject, additionalData: additionalData) - - if let backgroundColorHex = dictionary[KeyBackgroundColor] as? String { - backgroundColor = UIColor.mfGet(forHex: backgroundColorHex) + if let rightLabelLeadingAnchor = rightTextLabel?.leadingAnchor { + leftTextLabelTrailing = leftTextLabel?.trailingAnchor.constraint(equalTo: rightLabelLeadingAnchor, constant: -PaddingOne) + leftTextLabelTrailing?.isActive = true } + + NSLayoutConstraint.constraintPinSubview(leftTextLabel, pinTop: true, pinBottom: false, pinLeft: true, pinRight: false) + + let leftTextBottom = leftTextLabel?.bottomAnchor.constraint(equalTo: bottomAnchor) + leftTextBottom?.priority = UILayoutPriority(249) + leftTextBottom?.isActive = true + + if let leftLabelBottomAnchor = leftTextLabel?.bottomAnchor { + bottomAnchor.constraint(greaterThanOrEqualTo: leftLabelBottomAnchor).isActive = true + } + + rightTextLabel?.topAnchor.constraint(equalTo: topAnchor).isActive = true + rightTextLabel?.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true + NSLayoutConstraint.constraintPinSubview(rightTextLabel, pinTop: true, pinBottom: false, pinLeft: false, pinRight: true) + + let rightTextBottom = rightTextLabel?.bottomAnchor.constraint(equalTo: bottomAnchor) + rightTextBottom?.priority = UILayoutPriority(rawValue: 249) + rightTextBottom?.isActive = true + + if let rightLabelBottomAnchor = rightTextLabel?.bottomAnchor { + bottomAnchor.constraint(greaterThanOrEqualTo: rightLabelBottomAnchor).isActive = true + } + + leftTextWidth = leftTextLabel?.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.6) + leftTextWidth?.priority = UILayoutPriority(rawValue: 100) + leftTextWidth?.isActive = true + + rightTextWidth = rightTextLabel?.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.4) + rightTextWidth?.priority = UILayoutPriority(rawValue: 101) + rightTextWidth?.isActive = true + + if let leftLabelBaslineAnchor = leftTextLabel?.firstBaselineAnchor { + rightTextLabel?.firstBaselineAnchor.constraint(equalTo: leftLabelBaslineAnchor).isActive = true + } + + leftTextLabel?.setContentHuggingPriority(UILayoutPriority(rawValue: 801), for: .horizontal) + rightTextLabel?.setContentHuggingPriority(UILayoutPriority(rawValue: 802), for: .horizontal) + } + + private func constrainLeftLabel() { + + leftTextLabelTrailing?.constant = 0 + leftTextWidth?.isActive = false + leftTextWidth = leftTextLabel?.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1.0) + leftTextWidth?.priority = UILayoutPriority(rawValue: 999) + leftTextWidth?.isActive = true + } + + private func constrainRightLabel() { + + leftTextLabelTrailing?.constant = 0 + rightTextWidth?.isActive = false + rightTextWidth = rightTextLabel?.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1.0) + rightTextWidth?.priority = UILayoutPriority(rawValue: 999) + rightTextWidth?.isActive = true } //------------------------------------------------------ @@ -140,7 +164,23 @@ import Foundation open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { super.setWithJSON(json, delegateObject: delegateObject as? MVMCoreUIDelegateObject, additionalData: additionalData) - setWithJSON(actionMap: json, delegateObject: delegateObject, additionalData: additionalData) + + guard let dictionary = json else { return } + + leftTextLabel?.setWithJSON(dictionary.optionalDictionaryForKey("leftText"), delegateObject: delegateObject as? MVMCoreUIDelegateObject, additionalData: additionalData) + rightTextLabel?.setWithJSON(dictionary.optionalDictionaryForKey("rightText"), delegateObject: delegateObject as? MVMCoreUIDelegateObject, additionalData: additionalData) + + if let backgroundColorHex = dictionary[KeyBackgroundColor] as? String { + backgroundColor = UIColor.mfGet(forHex: backgroundColorHex) + } + + if let leftText = leftTextLabel?.text, leftText.isEmpty { + constrainRightLabel() + } else if let rightText = rightTextLabel?.text, rightText.isEmpty { + constrainLeftLabel() + } else { + constrainBothLabels() + } } open override func needsToBeConstrained() -> Bool { diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 30f0aff6..0d80000b 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -37,8 +37,7 @@ @"checkbox": MVMCoreUICheckBox.class, @"listItem": MoleculeTableViewCell.class, @"switchLineItem": SwitchLineItem.class, - @"switch": Switch.class, - @"leftRightLabelView": LeftRightLabelView.class + @"switch": Switch.class } mutableCopy]; }); return mapping; From 6a768fb18bd9736dd4c6fafd7520b5b2f558787e Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Fri, 31 May 2019 14:01:19 -0400 Subject: [PATCH 29/45] uneeded constraints. --- MVMCoreUI/Atoms/Views/LeftRightLabelView.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift index a2855d5a..2b2fe7e9 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift @@ -107,8 +107,6 @@ import Foundation bottomAnchor.constraint(greaterThanOrEqualTo: leftLabelBottomAnchor).isActive = true } - rightTextLabel?.topAnchor.constraint(equalTo: topAnchor).isActive = true - rightTextLabel?.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true NSLayoutConstraint.constraintPinSubview(rightTextLabel, pinTop: true, pinBottom: false, pinLeft: false, pinRight: true) let rightTextBottom = rightTextLabel?.bottomAnchor.constraint(equalTo: bottomAnchor) From 83cf1850ef9c80d0634392fba86650114f66e95a Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Mon, 3 Jun 2019 16:23:48 -0400 Subject: [PATCH 30/45] seemingly fixed the spacing issue. --- .../Atoms/Views/LeftRightLabelView.swift | 47 ++++++++++++++----- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift index 2b2fe7e9..6541647d 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift @@ -60,6 +60,10 @@ import Foundation leftTextLabel?.updateView(size) rightTextLabel?.updateView(size) + if let leftText = leftTextLabel?.text, let rightText = rightTextLabel?.text, !leftText.isEmpty && !rightText.isEmpty { + rightTextLabel?.preferredMaxLayoutWidth = size * 0.4 - PaddingDefaultHorizontalSpacing + } + layoutIfNeeded() } @@ -74,6 +78,9 @@ import Foundation let leftTextLabel = Label.commonLabelB1(true) let rightTextLabel = Label.commonLabelB1(true) + leftTextLabel.translatesAutoresizingMaskIntoConstraints = false + rightTextLabel.translatesAutoresizingMaskIntoConstraints = false + self.leftTextLabel = leftTextLabel self.rightTextLabel = rightTextLabel @@ -93,12 +100,10 @@ import Foundation leftTextLabel?.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true if let rightLabelLeadingAnchor = rightTextLabel?.leadingAnchor { - leftTextLabelTrailing = leftTextLabel?.trailingAnchor.constraint(equalTo: rightLabelLeadingAnchor, constant: -PaddingOne) + leftTextLabelTrailing = leftTextLabel?.trailingAnchor.constraint(equalTo: rightLabelLeadingAnchor, constant: -PaddingDefaultHorizontalSpacing) leftTextLabelTrailing?.isActive = true } - NSLayoutConstraint.constraintPinSubview(leftTextLabel, pinTop: true, pinBottom: false, pinLeft: true, pinRight: false) - let leftTextBottom = leftTextLabel?.bottomAnchor.constraint(equalTo: bottomAnchor) leftTextBottom?.priority = UILayoutPriority(249) leftTextBottom?.isActive = true @@ -107,7 +112,8 @@ import Foundation bottomAnchor.constraint(greaterThanOrEqualTo: leftLabelBottomAnchor).isActive = true } - NSLayoutConstraint.constraintPinSubview(rightTextLabel, pinTop: true, pinBottom: false, pinLeft: false, pinRight: true) + rightTextLabel?.topAnchor.constraint(equalTo: topAnchor).isActive = true + rightTextLabel?.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true let rightTextBottom = rightTextLabel?.bottomAnchor.constraint(equalTo: bottomAnchor) rightTextBottom?.priority = UILayoutPriority(rawValue: 249) @@ -117,38 +123,55 @@ import Foundation bottomAnchor.constraint(greaterThanOrEqualTo: rightLabelBottomAnchor).isActive = true } - leftTextWidth = leftTextLabel?.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.6) - leftTextWidth?.priority = UILayoutPriority(rawValue: 100) + leftTextWidth = leftTextLabel?.widthAnchor.constraint(greaterThanOrEqualTo: widthAnchor, multiplier: 0.6) + leftTextWidth?.priority = UILayoutPriority(rawValue: 995) leftTextWidth?.isActive = true - rightTextWidth = rightTextLabel?.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.4) - rightTextWidth?.priority = UILayoutPriority(rawValue: 101) + let leftTextHeight = leftTextLabel?.heightAnchor.constraint(greaterThanOrEqualToConstant: 0) + leftTextHeight?.priority = UILayoutPriority(rawValue: 901) + leftTextHeight?.isActive = true + + rightTextWidth = rightTextLabel?.widthAnchor.constraint(lessThanOrEqualTo: widthAnchor, multiplier: 0.4) + rightTextWidth?.priority = UILayoutPriority(rawValue: 906) rightTextWidth?.isActive = true + let rightTextHeight = rightTextLabel?.heightAnchor.constraint(greaterThanOrEqualToConstant: 0) + rightTextHeight?.priority = UILayoutPriority(rawValue: 901) + rightTextHeight?.isActive = true + if let leftLabelBaslineAnchor = leftTextLabel?.firstBaselineAnchor { rightTextLabel?.firstBaselineAnchor.constraint(equalTo: leftLabelBaslineAnchor).isActive = true } - leftTextLabel?.setContentHuggingPriority(UILayoutPriority(rawValue: 801), for: .horizontal) - rightTextLabel?.setContentHuggingPriority(UILayoutPriority(rawValue: 802), for: .horizontal) + leftTextLabel?.setContentHuggingPriority(UILayoutPriority(rawValue: 901), for: .horizontal) + rightTextLabel?.setContentHuggingPriority(UILayoutPriority(rawValue: 902), for: .horizontal) + + leftTextLabel?.setContentHuggingPriority(.required, for: .vertical) + rightTextLabel?.setContentHuggingPriority(.required, for: .vertical) + + leftTextLabel?.setContentCompressionResistancePriority(.required, for: .vertical) + rightTextLabel?.setContentCompressionResistancePriority(.required, for: .vertical) + rightTextLabel?.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 902), for: .horizontal) } private func constrainLeftLabel() { leftTextLabelTrailing?.constant = 0 - leftTextWidth?.isActive = false leftTextWidth = leftTextLabel?.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1.0) leftTextWidth?.priority = UILayoutPriority(rawValue: 999) leftTextWidth?.isActive = true + + leftTextLabel?.setContentHuggingPriority(.required, for: .vertical) } private func constrainRightLabel() { leftTextLabelTrailing?.constant = 0 - rightTextWidth?.isActive = false rightTextWidth = rightTextLabel?.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1.0) rightTextWidth?.priority = UILayoutPriority(rawValue: 999) rightTextWidth?.isActive = true + + rightTextLabel?.setContentHuggingPriority(.required, for: .vertical) } //------------------------------------------------------ From 1734cb3101cab732075f0a59a60c2864f51f5fbc Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Mon, 3 Jun 2019 16:34:18 -0400 Subject: [PATCH 31/45] padding adjustment. --- MVMCoreUI/Atoms/Views/LeftRightLabelView.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift index 6541647d..d1c3af01 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift @@ -61,7 +61,7 @@ import Foundation rightTextLabel?.updateView(size) if let leftText = leftTextLabel?.text, let rightText = rightTextLabel?.text, !leftText.isEmpty && !rightText.isEmpty { - rightTextLabel?.preferredMaxLayoutWidth = size * 0.4 - PaddingDefaultHorizontalSpacing + rightTextLabel?.preferredMaxLayoutWidth = size * 0.4 } layoutIfNeeded() @@ -100,7 +100,7 @@ import Foundation leftTextLabel?.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true if let rightLabelLeadingAnchor = rightTextLabel?.leadingAnchor { - leftTextLabelTrailing = leftTextLabel?.trailingAnchor.constraint(equalTo: rightLabelLeadingAnchor, constant: -PaddingDefaultHorizontalSpacing) + leftTextLabelTrailing = leftTextLabel?.trailingAnchor.constraint(equalTo: rightLabelLeadingAnchor, constant: -16) leftTextLabelTrailing?.isActive = true } @@ -174,6 +174,7 @@ import Foundation rightTextLabel?.setContentHuggingPriority(.required, for: .vertical) } + //------------------------------------------------------ // MARK: - Atomization //------------------------------------------------------ From f91e23e47b340b599569957118554fb030724a72 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Tue, 4 Jun 2019 09:10:42 -0400 Subject: [PATCH 32/45] LeftRightButton now available. --- MVMCoreUI/Atoms/Views/LeftRightLabelView.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift index d1c3af01..b1d2b057 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift @@ -9,7 +9,6 @@ import Foundation -@available(*, unavailable) @objcMembers open class LeftRightLabelView: ViewConstrainingView { //------------------------------------------------------ // MARK: - Properties From d3e8ba562f6968e86ce4889daff34254f09935f5 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Tue, 4 Jun 2019 09:54:56 -0400 Subject: [PATCH 33/45] mild updates. --- MVMCoreUI/Atoms/Views/LeftRightLabelView.swift | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift index b1d2b057..fb924453 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift @@ -1,5 +1,5 @@ // -// ItemDetailView.swift +// LeftRightLabelView.swift // MVMCoreUI // // Created by Christiano, Kevin on 5/20/19. @@ -173,7 +173,6 @@ import Foundation rightTextLabel?.setContentHuggingPriority(.required, for: .vertical) } - //------------------------------------------------------ // MARK: - Atomization //------------------------------------------------------ @@ -199,8 +198,6 @@ import Foundation constrainRightLabel() } else if let rightText = rightTextLabel?.text, rightText.isEmpty { constrainLeftLabel() - } else { - constrainBothLabels() } } From 83446c3e9bdacee21e3b9486b2bbfae3c7d3e00f Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Tue, 4 Jun 2019 16:30:14 -0400 Subject: [PATCH 34/45] added protocol. --- MVMCoreUI/Atoms/Views/Label.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index a542c95b..12a6defb 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -12,7 +12,7 @@ import MVMCore public typealias ActionBlock = () -> Void -@objcMembers open class Label: UILabel, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol { +@objcMembers open class Label: UILabel, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol { //------------------------------------------------------ // MARK: - General Properties //------------------------------------------------------ From e8ab74635162323faca9cbd9f9df27976b1f0147 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Mon, 10 Jun 2019 14:02:07 -0400 Subject: [PATCH 35/45] Latest state of LeftRightLabel. Requires fine tuning. --- .../Atoms/Views/LeftRightLabelView.swift | 171 ++++++++---------- 1 file changed, 77 insertions(+), 94 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift index fb924453..c415ab03 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift @@ -11,16 +11,28 @@ import Foundation @objcMembers open class LeftRightLabelView: ViewConstrainingView { //------------------------------------------------------ - // MARK: - Properties + // MARK: - Outlets //------------------------------------------------------ - var leftTextLabel: Label? - var rightTextLabel: Label? + let leftTextLabel = Label.commonLabelB1(true) + let rightTextLabel = Label.commonLabelB1(true) + + //------------------------------------------------------ + // MARK: - Constraints + //------------------------------------------------------ var leftTextWidth: NSLayoutConstraint? var rightTextWidth: NSLayoutConstraint? - var leftTextLabelTrailing: NSLayoutConstraint? + var rightTextLabelLeading: NSLayoutConstraint? + + //------------------------------------------------------ + // MARK: - Properties + //------------------------------------------------------ + + var leftRightMarginsValue: CGFloat { + return layoutMargins.left + layoutMargins.right + } //------------------------------------------------------ // MARK: - Initialization @@ -38,15 +50,23 @@ import Foundation super.init(coder: aDecoder) } - public convenience init(actionMap: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { + public convenience init(json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { self.init() - setWithJSON(actionMap, delegateObject: delegateObject, additionalData: additionalData) + setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) } override open func setupView() { super.setupView() - translatesAutoresizingMaskIntoConstraints = false - defaultState() + + guard subviews.isEmpty else { return } + + addSubview(leftTextLabel) + addSubview(rightTextLabel) + + leftTextLabel.textAlignment = .left + rightTextLabel.textAlignment = .right + + constrainBothLabels() } //------------------------------------------------------ @@ -56,147 +76,110 @@ import Foundation override open func updateView(_ size: CGFloat) { super.updateView(size) - leftTextLabel?.updateView(size) - rightTextLabel?.updateView(size) + leftTextLabel.updateView(size) + rightTextLabel.updateView(size) - if let leftText = leftTextLabel?.text, let rightText = rightTextLabel?.text, !leftText.isEmpty && !rightText.isEmpty { - rightTextLabel?.preferredMaxLayoutWidth = size * 0.4 + if leftTextLabel.hasText && rightTextLabel.hasText { + rightTextLabel.preferredMaxLayoutWidth = (size - leftRightMarginsValue) * 0.4 } - - layoutIfNeeded() } //------------------------------------------------------ // MARK: - Setup //------------------------------------------------------ - func defaultState() { - - if leftTextLabel == nil && rightTextLabel == nil { - - let leftTextLabel = Label.commonLabelB1(true) - let rightTextLabel = Label.commonLabelB1(true) - - leftTextLabel.translatesAutoresizingMaskIntoConstraints = false - rightTextLabel.translatesAutoresizingMaskIntoConstraints = false - - self.leftTextLabel = leftTextLabel - self.rightTextLabel = rightTextLabel - - addSubview(leftTextLabel) - addSubview(rightTextLabel) - - leftTextLabel.textAlignment = .left - rightTextLabel.textAlignment = .right - - constrainBothLabels() - } - } - private func constrainBothLabels() { - leftTextLabel?.topAnchor.constraint(equalTo: topAnchor).isActive = true - leftTextLabel?.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + leftTextLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true + leftTextLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true - if let rightLabelLeadingAnchor = rightTextLabel?.leadingAnchor { - leftTextLabelTrailing = leftTextLabel?.trailingAnchor.constraint(equalTo: rightLabelLeadingAnchor, constant: -16) - leftTextLabelTrailing?.isActive = true - } + rightTextLabelLeading = rightTextLabel.leadingAnchor.constraint(equalTo: leftTextLabel.trailingAnchor, constant: 16) + rightTextLabelLeading?.isActive = true - let leftTextBottom = leftTextLabel?.bottomAnchor.constraint(equalTo: bottomAnchor) - leftTextBottom?.priority = UILayoutPriority(249) - leftTextBottom?.isActive = true + let leftTextBottom = leftTextLabel.bottomAnchor.constraint(equalTo: bottomAnchor) + leftTextBottom.priority = UILayoutPriority(249) + leftTextBottom.isActive = true - if let leftLabelBottomAnchor = leftTextLabel?.bottomAnchor { - bottomAnchor.constraint(greaterThanOrEqualTo: leftLabelBottomAnchor).isActive = true - } + bottomAnchor.constraint(greaterThanOrEqualTo: leftTextLabel.bottomAnchor).isActive = true - rightTextLabel?.topAnchor.constraint(equalTo: topAnchor).isActive = true - rightTextLabel?.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true + rightTextLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true + let rightLayout = layoutMarginsGuide.trailingAnchor.constraint(equalTo: rightTextLabel.trailingAnchor) + rightLayout.priority = UILayoutPriority(rawValue: 995) + rightLayout.isActive = true - let rightTextBottom = rightTextLabel?.bottomAnchor.constraint(equalTo: bottomAnchor) - rightTextBottom?.priority = UILayoutPriority(rawValue: 249) - rightTextBottom?.isActive = true + let rightTextBottom = rightTextLabel.bottomAnchor.constraint(equalTo: bottomAnchor) + rightTextBottom.priority = UILayoutPriority(rawValue: 249) + rightTextBottom.isActive = true - if let rightLabelBottomAnchor = rightTextLabel?.bottomAnchor { - bottomAnchor.constraint(greaterThanOrEqualTo: rightLabelBottomAnchor).isActive = true - } + bottomAnchor.constraint(greaterThanOrEqualTo: rightTextLabel.bottomAnchor).isActive = true - leftTextWidth = leftTextLabel?.widthAnchor.constraint(greaterThanOrEqualTo: widthAnchor, multiplier: 0.6) + leftTextWidth = leftTextLabel.widthAnchor.constraint(greaterThanOrEqualTo: widthAnchor, multiplier: 0.6, constant: -leftRightMarginsValue) leftTextWidth?.priority = UILayoutPriority(rawValue: 995) leftTextWidth?.isActive = true - let leftTextHeight = leftTextLabel?.heightAnchor.constraint(greaterThanOrEqualToConstant: 0) - leftTextHeight?.priority = UILayoutPriority(rawValue: 901) - leftTextHeight?.isActive = true - - rightTextWidth = rightTextLabel?.widthAnchor.constraint(lessThanOrEqualTo: widthAnchor, multiplier: 0.4) + rightTextWidth = rightTextLabel.widthAnchor.constraint(lessThanOrEqualTo: widthAnchor, multiplier: 0.4, constant: -leftRightMarginsValue) rightTextWidth?.priority = UILayoutPriority(rawValue: 906) rightTextWidth?.isActive = true - let rightTextHeight = rightTextLabel?.heightAnchor.constraint(greaterThanOrEqualToConstant: 0) - rightTextHeight?.priority = UILayoutPriority(rawValue: 901) - rightTextHeight?.isActive = true + leftTextLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) + rightTextLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 901), for: .horizontal) - if let leftLabelBaslineAnchor = leftTextLabel?.firstBaselineAnchor { - rightTextLabel?.firstBaselineAnchor.constraint(equalTo: leftLabelBaslineAnchor).isActive = true - } + leftTextLabel.setContentHuggingPriority(.required, for: .vertical) + rightTextLabel.setContentHuggingPriority(.required, for: .vertical) - leftTextLabel?.setContentHuggingPriority(UILayoutPriority(rawValue: 901), for: .horizontal) - rightTextLabel?.setContentHuggingPriority(UILayoutPriority(rawValue: 902), for: .horizontal) - - leftTextLabel?.setContentHuggingPriority(.required, for: .vertical) - rightTextLabel?.setContentHuggingPriority(.required, for: .vertical) - - leftTextLabel?.setContentCompressionResistancePriority(.required, for: .vertical) - rightTextLabel?.setContentCompressionResistancePriority(.required, for: .vertical) - rightTextLabel?.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 902), for: .horizontal) + leftTextLabel.setContentCompressionResistancePriority(.required, for: .vertical) + rightTextLabel.setContentCompressionResistancePriority(.required, for: .vertical) + rightTextLabel.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 902), for: .horizontal) } private func constrainLeftLabel() { - leftTextLabelTrailing?.constant = 0 - leftTextWidth = leftTextLabel?.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1.0) + rightTextLabelLeading?.isActive = false + layoutMarginsGuide.trailingAnchor.constraint(equalTo: leftTextLabel.trailingAnchor).isActive = true + leftTextWidth?.isActive = false + leftTextWidth = leftTextLabel.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1.0, constant: -leftRightMarginsValue) leftTextWidth?.priority = UILayoutPriority(rawValue: 999) leftTextWidth?.isActive = true - - leftTextLabel?.setContentHuggingPriority(.required, for: .vertical) } private func constrainRightLabel() { - leftTextLabelTrailing?.constant = 0 - rightTextWidth = rightTextLabel?.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1.0) + rightTextLabelLeading?.isActive = false + rightTextLabel.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor).isActive = true + layoutMarginsGuide.leadingAnchor.constraint(equalTo: rightTextLabel.leadingAnchor).isActive = true + rightTextWidth?.isActive = false + rightTextWidth = rightTextLabel.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1.0, constant: -leftRightMarginsValue) rightTextWidth?.priority = UILayoutPriority(rawValue: 999) rightTextWidth?.isActive = true + } + + override open func resetConstraints() { + super.resetConstraints() - rightTextLabel?.setContentHuggingPriority(.required, for: .vertical) + leftTextWidth?.isActive = false + rightTextWidth?.isActive = false + constrainBothLabels() } //------------------------------------------------------ // MARK: - Atomization //------------------------------------------------------ - open override func setAsMolecule() { - super.setAsMolecule() - defaultState() - } - open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { super.setWithJSON(json, delegateObject: delegateObject as? MVMCoreUIDelegateObject, additionalData: additionalData) guard let dictionary = json else { return } - leftTextLabel?.setWithJSON(dictionary.optionalDictionaryForKey("leftText"), delegateObject: delegateObject as? MVMCoreUIDelegateObject, additionalData: additionalData) - rightTextLabel?.setWithJSON(dictionary.optionalDictionaryForKey("rightText"), delegateObject: delegateObject as? MVMCoreUIDelegateObject, additionalData: additionalData) + leftTextLabel.setWithJSON(dictionary.optionalDictionaryForKey("leftText"), delegateObject: delegateObject as? MVMCoreUIDelegateObject, additionalData: additionalData) + rightTextLabel.setWithJSON(dictionary.optionalDictionaryForKey("rightText"), delegateObject: delegateObject as? MVMCoreUIDelegateObject, additionalData: additionalData) if let backgroundColorHex = dictionary[KeyBackgroundColor] as? String { backgroundColor = UIColor.mfGet(forHex: backgroundColorHex) } - if let leftText = leftTextLabel?.text, leftText.isEmpty { + if !leftTextLabel.hasText { constrainRightLabel() - } else if let rightText = rightTextLabel?.text, rightText.isEmpty { + } else if !rightTextLabel.hasText { constrainLeftLabel() } } From 9fb4c099b25fb18f860529fcfc078056a5426469 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Tue, 11 Jun 2019 09:52:25 -0400 Subject: [PATCH 36/45] Updated Deprecated LabelWithInternalButton message. Improved layout of LeftRightLabelView. --- .../Atoms/Views/LabelWithInternalButton.swift | 2 +- .../Atoms/Views/LeftRightLabelView.swift | 28 ++++++------------- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift index 2befda5a..5cc6d50b 100644 --- a/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift +++ b/MVMCoreUI/Atoms/Views/LabelWithInternalButton.swift @@ -14,7 +14,7 @@ public typealias ButtonObjectDelegate = NSObjectProtocol & ButtonDelegateProtoco public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProtocol & MVMCoreLoadDelegateProtocol & MVMCorePresentationDelegateProtocol & NSObjectProtocol -@available(*, deprecated, message: "This class is deprecated, please use the Label class.") +@available(*, deprecated, message: "Use Label instead.") @objcMembers open class LabelWithInternalButton: UIControl, MVMCoreViewProtocol, MFButtonProtocol, MVMCoreUIMoleculeViewProtocol { //------------------------------------------------------ // MARK: - Properties diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift index c415ab03..d5cf579b 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift @@ -26,14 +26,6 @@ import Foundation var rightTextLabelLeading: NSLayoutConstraint? - //------------------------------------------------------ - // MARK: - Properties - //------------------------------------------------------ - - var leftRightMarginsValue: CGFloat { - return layoutMargins.left + layoutMargins.right - } - //------------------------------------------------------ // MARK: - Initialization //------------------------------------------------------ @@ -80,7 +72,7 @@ import Foundation rightTextLabel.updateView(size) if leftTextLabel.hasText && rightTextLabel.hasText { - rightTextLabel.preferredMaxLayoutWidth = (size - leftRightMarginsValue) * 0.4 + rightTextLabel.preferredMaxLayoutWidth = floor((size - 16) * 0.4) } } @@ -113,16 +105,16 @@ import Foundation bottomAnchor.constraint(greaterThanOrEqualTo: rightTextLabel.bottomAnchor).isActive = true - leftTextWidth = leftTextLabel.widthAnchor.constraint(greaterThanOrEqualTo: widthAnchor, multiplier: 0.6, constant: -leftRightMarginsValue) + leftTextWidth = leftTextLabel.widthAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.widthAnchor, multiplier: 0.6) leftTextWidth?.priority = UILayoutPriority(rawValue: 995) leftTextWidth?.isActive = true - rightTextWidth = rightTextLabel.widthAnchor.constraint(lessThanOrEqualTo: widthAnchor, multiplier: 0.4, constant: -leftRightMarginsValue) + rightTextWidth = rightTextLabel.widthAnchor.constraint(lessThanOrEqualTo: layoutMarginsGuide.widthAnchor, multiplier: 0.4) rightTextWidth?.priority = UILayoutPriority(rawValue: 906) rightTextWidth?.isActive = true - leftTextLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: .horizontal) - rightTextLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 901), for: .horizontal) + leftTextLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 901), for: .horizontal) + rightTextLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 902), for: .horizontal) leftTextLabel.setContentHuggingPriority(.required, for: .vertical) rightTextLabel.setContentHuggingPriority(.required, for: .vertical) @@ -137,7 +129,7 @@ import Foundation rightTextLabelLeading?.isActive = false layoutMarginsGuide.trailingAnchor.constraint(equalTo: leftTextLabel.trailingAnchor).isActive = true leftTextWidth?.isActive = false - leftTextWidth = leftTextLabel.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1.0, constant: -leftRightMarginsValue) + leftTextWidth = leftTextLabel.widthAnchor.constraint(equalTo: layoutMarginsGuide.widthAnchor, multiplier: 1.0) leftTextWidth?.priority = UILayoutPriority(rawValue: 999) leftTextWidth?.isActive = true } @@ -146,9 +138,9 @@ import Foundation rightTextLabelLeading?.isActive = false rightTextLabel.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor).isActive = true - layoutMarginsGuide.leadingAnchor.constraint(equalTo: rightTextLabel.leadingAnchor).isActive = true + rightTextLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true rightTextWidth?.isActive = false - rightTextWidth = rightTextLabel.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1.0, constant: -leftRightMarginsValue) + rightTextWidth = rightTextLabel.widthAnchor.constraint(equalTo: layoutMarginsGuide.widthAnchor, multiplier: 1.0) rightTextWidth?.priority = UILayoutPriority(rawValue: 999) rightTextWidth?.isActive = true } @@ -183,8 +175,4 @@ import Foundation constrainLeftLabel() } } - - open override func needsToBeConstrained() -> Bool { - return true - } } From f3f645894d387bcd9423fdc068795a6776d68b70 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Tue, 11 Jun 2019 14:47:52 -0400 Subject: [PATCH 37/45] Adding class to mappingObject. --- MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 0004027a..01f0e298 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -38,7 +38,8 @@ @"checkbox": MVMCoreUICheckBox.class, @"listItem": MoleculeTableViewCell.class, @"switchLineItem": SwitchLineItem.class, - @"switch": Switch.class + @"switch": Switch.class, + @"leftRightLabelView": LeftRightLabelView.class } mutableCopy]; }); return mapping; From 77655b5f618a93972d824816d349ebf5d0d0659d Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Tue, 18 Jun 2019 10:53:22 -0400 Subject: [PATCH 38/45] fixed constraints. Added comment. --- .../Atoms/Views/LeftRightLabelView.swift | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift index d5cf579b..3deeae76 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift @@ -24,7 +24,9 @@ import Foundation var leftTextWidth: NSLayoutConstraint? var rightTextWidth: NSLayoutConstraint? + var rightTextLabeltrailing: NSLayoutConstraint? var rightTextLabelLeading: NSLayoutConstraint? + var leftTextTrailingLabel: NSLayoutConstraint? //------------------------------------------------------ // MARK: - Initialization @@ -71,6 +73,7 @@ import Foundation leftTextLabel.updateView(size) rightTextLabel.updateView(size) + // Resolves text layout issues found between both dynamically sized labels, number is not exact to screen width but performs as required. if leftTextLabel.hasText && rightTextLabel.hasText { rightTextLabel.preferredMaxLayoutWidth = floor((size - 16) * 0.4) } @@ -85,15 +88,15 @@ import Foundation leftTextLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true leftTextLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true - rightTextLabelLeading = rightTextLabel.leadingAnchor.constraint(equalTo: leftTextLabel.trailingAnchor, constant: 16) - rightTextLabelLeading?.isActive = true - let leftTextBottom = leftTextLabel.bottomAnchor.constraint(equalTo: bottomAnchor) leftTextBottom.priority = UILayoutPriority(249) leftTextBottom.isActive = true bottomAnchor.constraint(greaterThanOrEqualTo: leftTextLabel.bottomAnchor).isActive = true + rightTextLabelLeading = rightTextLabel.leadingAnchor.constraint(equalTo: leftTextLabel.trailingAnchor, constant: 16) + rightTextLabelLeading?.isActive = true + rightTextLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true let rightLayout = layoutMarginsGuide.trailingAnchor.constraint(equalTo: rightTextLabel.trailingAnchor) rightLayout.priority = UILayoutPriority(rawValue: 995) @@ -127,7 +130,8 @@ import Foundation private func constrainLeftLabel() { rightTextLabelLeading?.isActive = false - layoutMarginsGuide.trailingAnchor.constraint(equalTo: leftTextLabel.trailingAnchor).isActive = true + leftTextTrailingLabel = layoutMarginsGuide.trailingAnchor.constraint(equalTo: leftTextLabel.trailingAnchor) + leftTextTrailingLabel?.isActive = true leftTextWidth?.isActive = false leftTextWidth = leftTextLabel.widthAnchor.constraint(equalTo: layoutMarginsGuide.widthAnchor, multiplier: 1.0) leftTextWidth?.priority = UILayoutPriority(rawValue: 999) @@ -137,8 +141,10 @@ import Foundation private func constrainRightLabel() { rightTextLabelLeading?.isActive = false - rightTextLabel.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor).isActive = true - rightTextLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true + rightTextLabeltrailing = rightTextLabel.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor) + rightTextLabeltrailing?.isActive = true + rightTextLabelLeading = rightTextLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor) + rightTextLabelLeading?.isActive = true rightTextWidth?.isActive = false rightTextWidth = rightTextLabel.widthAnchor.constraint(equalTo: layoutMarginsGuide.widthAnchor, multiplier: 1.0) rightTextWidth?.priority = UILayoutPriority(rawValue: 999) @@ -148,6 +154,8 @@ import Foundation override open func resetConstraints() { super.resetConstraints() + leftTextTrailingLabel?.isActive = false + rightTextLabelLeading?.isActive = false leftTextWidth?.isActive = false rightTextWidth?.isActive = false constrainBothLabels() From a6f82e60771d7baf75a29cc832adb42d81ce346b Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Tue, 18 Jun 2019 10:59:19 -0400 Subject: [PATCH 39/45] Compositional changes. --- MVMCoreUI/Atoms/Views/LeftRightLabelView.swift | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift index 3deeae76..29cbf337 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift @@ -26,7 +26,7 @@ import Foundation var rightTextLabeltrailing: NSLayoutConstraint? var rightTextLabelLeading: NSLayoutConstraint? - var leftTextTrailingLabel: NSLayoutConstraint? + var leftTextLabelTrailing: NSLayoutConstraint? //------------------------------------------------------ // MARK: - Initialization @@ -73,7 +73,7 @@ import Foundation leftTextLabel.updateView(size) rightTextLabel.updateView(size) - // Resolves text layout issues found between both dynamically sized labels, number is not exact to screen width but performs as required. + // Resolves text layout issues found between both dynamically sized labels, number is not exact but performs as required. if leftTextLabel.hasText && rightTextLabel.hasText { rightTextLabel.preferredMaxLayoutWidth = floor((size - 16) * 0.4) } @@ -130,8 +130,10 @@ import Foundation private func constrainLeftLabel() { rightTextLabelLeading?.isActive = false - leftTextTrailingLabel = layoutMarginsGuide.trailingAnchor.constraint(equalTo: leftTextLabel.trailingAnchor) - leftTextTrailingLabel?.isActive = true + + leftTextLabelTrailing = layoutMarginsGuide.trailingAnchor.constraint(equalTo: leftTextLabel.trailingAnchor) + leftTextLabelTrailing?.isActive = true + leftTextWidth?.isActive = false leftTextWidth = leftTextLabel.widthAnchor.constraint(equalTo: layoutMarginsGuide.widthAnchor, multiplier: 1.0) leftTextWidth?.priority = UILayoutPriority(rawValue: 999) @@ -141,10 +143,13 @@ import Foundation private func constrainRightLabel() { rightTextLabelLeading?.isActive = false + rightTextLabeltrailing = rightTextLabel.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor) rightTextLabeltrailing?.isActive = true + rightTextLabelLeading = rightTextLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor) rightTextLabelLeading?.isActive = true + rightTextWidth?.isActive = false rightTextWidth = rightTextLabel.widthAnchor.constraint(equalTo: layoutMarginsGuide.widthAnchor, multiplier: 1.0) rightTextWidth?.priority = UILayoutPriority(rawValue: 999) @@ -154,7 +159,7 @@ import Foundation override open func resetConstraints() { super.resetConstraints() - leftTextTrailingLabel?.isActive = false + leftTextLabelTrailing?.isActive = false rightTextLabelLeading?.isActive = false leftTextWidth?.isActive = false rightTextWidth?.isActive = false From 4b8b73c5e5d1bc8d32e89fc773dd7650847add1a Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Tue, 18 Jun 2019 11:06:21 -0400 Subject: [PATCH 40/45] Removed uneeded constraint. --- MVMCoreUI/Atoms/Views/LeftRightLabelView.swift | 6 ------ 1 file changed, 6 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift index 29cbf337..27fb5638 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift @@ -24,7 +24,6 @@ import Foundation var leftTextWidth: NSLayoutConstraint? var rightTextWidth: NSLayoutConstraint? - var rightTextLabeltrailing: NSLayoutConstraint? var rightTextLabelLeading: NSLayoutConstraint? var leftTextLabelTrailing: NSLayoutConstraint? @@ -130,7 +129,6 @@ import Foundation private func constrainLeftLabel() { rightTextLabelLeading?.isActive = false - leftTextLabelTrailing = layoutMarginsGuide.trailingAnchor.constraint(equalTo: leftTextLabel.trailingAnchor) leftTextLabelTrailing?.isActive = true @@ -143,10 +141,6 @@ import Foundation private func constrainRightLabel() { rightTextLabelLeading?.isActive = false - - rightTextLabeltrailing = rightTextLabel.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor) - rightTextLabeltrailing?.isActive = true - rightTextLabelLeading = rightTextLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor) rightTextLabelLeading?.isActive = true From 986dc46296634513867ba5ce58ea6201fb860721 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Tue, 18 Jun 2019 12:46:07 -0400 Subject: [PATCH 41/45] removed unnecessay. --- MVMCoreUI/Atoms/Views/LeftRightLabelView.swift | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift index 27fb5638..f9353ec1 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift @@ -129,13 +129,9 @@ import Foundation private func constrainLeftLabel() { rightTextLabelLeading?.isActive = false + leftTextLabelTrailing?.isActive = false leftTextLabelTrailing = layoutMarginsGuide.trailingAnchor.constraint(equalTo: leftTextLabel.trailingAnchor) leftTextLabelTrailing?.isActive = true - - leftTextWidth?.isActive = false - leftTextWidth = leftTextLabel.widthAnchor.constraint(equalTo: layoutMarginsGuide.widthAnchor, multiplier: 1.0) - leftTextWidth?.priority = UILayoutPriority(rawValue: 999) - leftTextWidth?.isActive = true } private func constrainRightLabel() { @@ -143,11 +139,6 @@ import Foundation rightTextLabelLeading?.isActive = false rightTextLabelLeading = rightTextLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor) rightTextLabelLeading?.isActive = true - - rightTextWidth?.isActive = false - rightTextWidth = rightTextLabel.widthAnchor.constraint(equalTo: layoutMarginsGuide.widthAnchor, multiplier: 1.0) - rightTextWidth?.priority = UILayoutPriority(rawValue: 999) - rightTextWidth?.isActive = true } override open func resetConstraints() { @@ -155,8 +146,6 @@ import Foundation leftTextLabelTrailing?.isActive = false rightTextLabelLeading?.isActive = false - leftTextWidth?.isActive = false - rightTextWidth?.isActive = false constrainBothLabels() } From 52bfb1a8d128c981408ae410ce9041f4618b61b2 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Wed, 19 Jun 2019 13:07:12 -0400 Subject: [PATCH 42/45] Updated constraints. Dried up code. Address rightLabel issues when using preferredMaxLayout. --- .../Atoms/Views/LeftRightLabelView.swift | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift index f9353ec1..2b71c04e 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift @@ -21,9 +21,6 @@ import Foundation // MARK: - Constraints //------------------------------------------------------ - var leftTextWidth: NSLayoutConstraint? - var rightTextWidth: NSLayoutConstraint? - var rightTextLabelLeading: NSLayoutConstraint? var leftTextLabelTrailing: NSLayoutConstraint? @@ -75,6 +72,8 @@ import Foundation // Resolves text layout issues found between both dynamically sized labels, number is not exact but performs as required. if leftTextLabel.hasText && rightTextLabel.hasText { rightTextLabel.preferredMaxLayoutWidth = floor((size - 16) * 0.4) + } else if rightTextLabel.hasText { + rightTextLabel.preferredMaxLayoutWidth = 0.0 } } @@ -107,13 +106,13 @@ import Foundation bottomAnchor.constraint(greaterThanOrEqualTo: rightTextLabel.bottomAnchor).isActive = true - leftTextWidth = leftTextLabel.widthAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.widthAnchor, multiplier: 0.6) - leftTextWidth?.priority = UILayoutPriority(rawValue: 995) - leftTextWidth?.isActive = true + let leftTextWidth = leftTextLabel.widthAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.widthAnchor, multiplier: 0.6) + leftTextWidth.priority = UILayoutPriority(rawValue: 995) + leftTextWidth.isActive = true - rightTextWidth = rightTextLabel.widthAnchor.constraint(lessThanOrEqualTo: layoutMarginsGuide.widthAnchor, multiplier: 0.4) - rightTextWidth?.priority = UILayoutPriority(rawValue: 906) - rightTextWidth?.isActive = true + let rightTextWidth = rightTextLabel.widthAnchor.constraint(lessThanOrEqualTo: layoutMarginsGuide.widthAnchor, multiplier: 0.4) + rightTextWidth.priority = UILayoutPriority(rawValue: 906) + rightTextWidth.isActive = true leftTextLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 901), for: .horizontal) rightTextLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 902), for: .horizontal) @@ -128,25 +127,29 @@ import Foundation private func constrainLeftLabel() { - rightTextLabelLeading?.isActive = false - leftTextLabelTrailing?.isActive = false + deactivateMiddleConstraint() leftTextLabelTrailing = layoutMarginsGuide.trailingAnchor.constraint(equalTo: leftTextLabel.trailingAnchor) leftTextLabelTrailing?.isActive = true } private func constrainRightLabel() { - rightTextLabelLeading?.isActive = false + deactivateMiddleConstraint() rightTextLabelLeading = rightTextLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor) rightTextLabelLeading?.isActive = true } - override open func resetConstraints() { - super.resetConstraints() + override open func reset() { + super.reset() + + deactivateMiddleConstraint() + constrainBothLabels() + } + + private func deactivateMiddleConstraint() { leftTextLabelTrailing?.isActive = false rightTextLabelLeading?.isActive = false - constrainBothLabels() } //------------------------------------------------------ From f23cee6ba90fb243fa5ab391d6abbd64a21b873e Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Wed, 19 Jun 2019 16:17:22 -0400 Subject: [PATCH 43/45] further attention paid to the preferredMaxLayout. --- MVMCoreUI/Atoms/Views/LeftRightLabelView.swift | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift index 2b71c04e..81d9f2ff 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift @@ -71,9 +71,11 @@ import Foundation // Resolves text layout issues found between both dynamically sized labels, number is not exact but performs as required. if leftTextLabel.hasText && rightTextLabel.hasText { - rightTextLabel.preferredMaxLayoutWidth = floor((size - 16) * 0.4) - } else if rightTextLabel.hasText { - rightTextLabel.preferredMaxLayoutWidth = 0.0 + let padding = MFStyler.defaultHorizontalPadding(forSize: size) * 2 + let maximumTextWidth = (size - (padding + 16 + layoutMargins.left + layoutMargins.right)) * 0.4 + rightTextLabel.preferredMaxLayoutWidth = round(maximumTextWidth) + } else { + rightTextLabel.preferredMaxLayoutWidth = 0 } } @@ -144,6 +146,9 @@ import Foundation deactivateMiddleConstraint() constrainBothLabels() + leftTextLabel.text = "" + rightTextLabel.text = "" + backgroundColor = nil } private func deactivateMiddleConstraint() { From 353f8cd301bd074719ddb8a606c62aefb29d735f Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Wed, 19 Jun 2019 16:49:07 -0400 Subject: [PATCH 44/45] VCV updated to account for directionalLayoutMargins. LRLabel improved. Needs slight adjustment for iPhone SE. --- MVMCoreUI/Atoms/Views/LeftRightLabelView.swift | 2 +- MVMCoreUI/Atoms/Views/ViewConstrainingView.m | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift index 81d9f2ff..bb11374e 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift @@ -72,7 +72,7 @@ import Foundation // Resolves text layout issues found between both dynamically sized labels, number is not exact but performs as required. if leftTextLabel.hasText && rightTextLabel.hasText { let padding = MFStyler.defaultHorizontalPadding(forSize: size) * 2 - let maximumTextWidth = (size - (padding + 16 + layoutMargins.left + layoutMargins.right)) * 0.4 + let maximumTextWidth = (size - (padding + 16)) * 0.4 rightTextLabel.preferredMaxLayoutWidth = round(maximumTextWidth) } else { rightTextLabel.preferredMaxLayoutWidth = 0 diff --git a/MVMCoreUI/Atoms/Views/ViewConstrainingView.m b/MVMCoreUI/Atoms/Views/ViewConstrainingView.m index a7412b93..02d965b7 100644 --- a/MVMCoreUI/Atoms/Views/ViewConstrainingView.m +++ b/MVMCoreUI/Atoms/Views/ViewConstrainingView.m @@ -274,6 +274,11 @@ [super setupView]; self.translatesAutoresizingMaskIntoConstraints = NO; self.backgroundColor = [UIColor clearColor]; + if (@available(iOS 11.0, *)) { + self.directionalLayoutMargins = NSDirectionalEdgeInsetsZero; + } else { + self.layoutMargins = UIEdgeInsetsZero; + } } - (void)updateView:(CGFloat)size { From 9cfa00c5331b7d9838792b8cf756a6bfc5f4b058 Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Thu, 20 Jun 2019 09:20:45 -0400 Subject: [PATCH 45/45] Further improved label dynamic sizing. --- MVMCoreUI/Atoms/Views/LeftRightLabelView.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift index bb11374e..1ca1402a 100644 --- a/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift +++ b/MVMCoreUI/Atoms/Views/LeftRightLabelView.swift @@ -73,8 +73,9 @@ import Foundation if leftTextLabel.hasText && rightTextLabel.hasText { let padding = MFStyler.defaultHorizontalPadding(forSize: size) * 2 let maximumTextWidth = (size - (padding + 16)) * 0.4 - rightTextLabel.preferredMaxLayoutWidth = round(maximumTextWidth) - } else { + // Subtracting 10 resolves issues of SE and iPad + rightTextLabel.preferredMaxLayoutWidth = round(maximumTextWidth) - 10 + } else { rightTextLabel.preferredMaxLayoutWidth = 0 } }