From 59248974ab889d9f957a1475b6280755bf5dc4db Mon Sep 17 00:00:00 2001 From: "Christiano, Kevin" Date: Fri, 10 May 2019 13:09:58 -0400 Subject: [PATCH] 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)]) }