Changes made to Label & LabelWithInternalButton.

This commit is contained in:
Christiano, Kevin 2019-05-10 13:09:58 -04:00
parent f04e4de01c
commit 59248974ab
2 changed files with 95 additions and 96 deletions

View File

@ -11,20 +11,19 @@ import MVMCore
public typealias ActionBlock = () -> Void public typealias ActionBlock = () -> Void
@objcMembers open class Label: UILabel, MVMCoreViewProtocol, MFButtonProtocol, MVMCoreUIMoleculeViewProtocol { @objcMembers open class Label: UILabel, MVMCoreViewProtocol, MFButtonProtocol, MVMCoreUIMoleculeViewProtocol {
//------------------------------------------------------ //------------------------------------------------------
// MARK: - General Properties // MARK: - General Properties
//------------------------------------------------------ //------------------------------------------------------
// This is here for LabelWithInternalButton public 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. // 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 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 sizeObject: MFSizeObject?
public var scaleSize: NSNumber? public var scaleSize: NSNumber?
// Used for scaling the font in updateView. // 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] = [] { public var clauses: [ActionableClause] = [] {
didSet { didSet { isUserInteractionEnabled = !clauses.isEmpty }
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. // Used for tappable links in the text.
public struct ActionableClause { public struct ActionableClause {
var location: Int? var range: NSRange?
var length: Int?
var actionBlock: ActionBlock? var actionBlock: ActionBlock?
var range: NSRange {
return NSRange(location: location ?? 0, length: length ?? 0)
}
func performAction() { func performAction() {
actionBlock?() actionBlock?()
} }
@ -78,6 +62,10 @@ public typealias ActionBlock = () -> Void
numberOfLines = 0 numberOfLines = 0
lineBreakMode = .byWordWrapping lineBreakMode = .byWordWrapping
translatesAutoresizingMaskIntoConstraints = false translatesAutoresizingMaskIntoConstraints = false
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(textLinkTapped(_:)))
tapGesture.numberOfTapsRequired = 1
addGestureRecognizer(tapGesture)
} }
@objc public init() { @objc public init() {
@ -161,8 +149,6 @@ public typealias ActionBlock = () -> Void
//------------------------------------------------------ //------------------------------------------------------
/** /**
Makes the view interactive and applies the gesture recognizer.
- Parameters: - Parameters:
- label: The label to be set. - label: The label to be set.
- html: any html string. - html: any html string.
@ -184,8 +170,6 @@ public typealias ActionBlock = () -> Void
} }
/** /**
Makes the view interactive and applies the gesture recognizer.
- Parameters: - Parameters:
- label: The current label view that will have an actionable range of text. - label: The current label view that will have an actionable range of text.
- json: Contains values which det the values of Label. - json: Contains values which det the values of Label.
@ -255,22 +239,13 @@ public typealias ActionBlock = () -> Void
attributedString.removeAttribute(.font, range: range) attributedString.removeAttribute(.font, range: range)
attributedString.addAttribute(.font, value: font, range: range) attributedString.addAttribute(.font, value: font, range: range)
} }
case "actions": case "action":
guard let actionLabel = label as? Label, guard let actionLabel = label as? Label else { continue }
let actions = attribute.optionalArrayForKey("actions")
else { continue }
for case let action as [String: Any] in actions { actionLabel.addActionableClause(range: range,
guard let actionLocation = action["location"] as? Int, actionMap: json,
let actionLength = action["length"] as? Int additionalData: additionalData,
else { continue } delegateObject: delegate)
actionLabel.clauses.append(ActionableClause(location: actionLocation,
length: actionLength,
actionBlock: actionLabel.createActionBlockFrom(actionMap: json,
additionalData: additionalData,
delegate: delegate)))
}
default: default:
continue continue
} }
@ -279,14 +254,55 @@ public typealias ActionBlock = () -> Void
} }
} }
func createActionBlockFrom(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegate: DelegateObject?) -> ActionBlock { /// Reseting to default Label values.
return { [weak delegate] in @objc public func clearActionableClauses() {
if (delegate as? MVMCoreUIDelegateObject)?.buttonDelegate?.button?(self, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true {
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegate) 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 // MARK: - Methods
//------------------------------------------------------ //------------------------------------------------------
@ -372,6 +388,7 @@ public typealias ActionBlock = () -> Void
extension Label { extension Label {
@objc public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) { @objc public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: DelegateObject?, additionalData: [AnyHashable: Any]?) {
clauses = []
Label.setUILabel(self, withJSON: json, delegate: delegateObject, additionalData: additionalData) Label.setUILabel(self, withJSON: json, delegate: delegateObject, additionalData: additionalData)
originalAttributedString = attributedText originalAttributedString = attributedText
} }
@ -394,9 +411,8 @@ extension Label {
*/ */
@objc public func addTappableLinkAttribute(range: NSRange, actionBlock: @escaping ActionBlock) { @objc public func addTappableLinkAttribute(range: NSRange, actionBlock: @escaping ActionBlock) {
clauses.append(ActionableClause(location: range.location, addActionableClause(range: range,
length: range.length, actionBlock: actionBlock)
actionBlock: actionBlock))
} }
/** /**
@ -409,28 +425,29 @@ extension Label {
- delegate: - delegate:
- additionalData: - 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, addActionableClause(range: range,
length: range.length, actionMap: actionMap,
actionBlock: createActionBlockFrom(actionMap: actionMap, additionalData: additionalData,
additionalData: additionalData, delegateObject: delegateObject)
delegate: delegate)))
} }
@objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) { @objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) {
for clause in clauses { 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() clause.performAction()
return
} }
} }
} }
} }
// MARK: -
extension UITapGestureRecognizer { 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 } guard let attributedText = label.attributedText else { return false }
@ -444,17 +461,14 @@ extension UITapGestureRecognizer {
textContainer.lineFragmentPadding = 0.0 textContainer.lineFragmentPadding = 0.0
textContainer.lineBreakMode = label.lineBreakMode textContainer.lineBreakMode = label.lineBreakMode
textContainer.maximumNumberOfLines = label.numberOfLines textContainer.maximumNumberOfLines = label.numberOfLines
let labelSize = label.bounds.size textContainer.size = label.bounds.size
textContainer.size = labelSize
let indexOfCharacter = layoutManager.characterIndex(for: location(in: label), in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil) let indexOfCharacter = layoutManager.characterIndex(for: location(in: label), in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
var range = targetRange if label.makeWholeViewClickable {
return true
if isWholeViewTappable, let wholeRange = NSRange(attributedText.string) {
range = wholeRange
} }
return NSLocationInRange(indexOfCharacter, range) return NSLocationInRange(indexOfCharacter, targetRange)
} }
} }

View File

@ -24,7 +24,6 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
public var attributedText: NSAttributedString? { public var attributedText: NSAttributedString? {
willSet(newAttributedText) { willSet(newAttributedText) {
if let newAttribText = newAttributedText { if let newAttribText = newAttributedText {
let mutableAttributedText = NSMutableAttributedString(attributedString: newAttribText) let mutableAttributedText = NSMutableAttributedString(attributedString: newAttribText)
let paragraphStyle = NSMutableParagraphStyle() let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = CGFloat(LabelWithInternalButtonLineSpace) paragraphStyle.lineSpacing = CGFloat(LabelWithInternalButtonLineSpace)
@ -64,18 +63,23 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
if newActionBlock == nil { if newActionBlock == nil {
label?.clauses = [] label?.clauses = []
} else { } else {
label?.clauses = [Label.ActionableClause(location: actionRange.location, label?.clauses = [Label.ActionableClause(range: actionRange, actionBlock: newActionBlock)]
length: actionRange.length,
actionBlock: newActionBlock)]
} }
} }
} }
private var frontRange: NSRange {
return NSRange(location: 0, length: frontText?.count ?? 0)
}
private var actionRange: NSRange { private var actionRange: NSRange {
return NSRange(location: frontText?.count ?? 0, length: actionText?.count ?? 0) 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 { public var makeWholeViewClickable = false {
willSet(newBool) { willSet(newBool) {
label?.makeWholeViewClickable = newBool label?.makeWholeViewClickable = newBool
@ -165,7 +169,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
super.init(frame: .zero) super.init(frame: .zero)
setText(fullText, startTag: startTag, endTag: endTag) 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)]) setAlternateActionTextAttributes([NSAttributedString.Key.underlineStyle: NSNumber(value: NSUnderlineStyle.single.rawValue)])
label?.attributedText = attributedText 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?) { @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 attributedText = mutableAttributedString
// Added this line for underlining
setAlternateActionTextAttributes([NSAttributedString.Key.underlineStyle: NSNumber(value: NSUnderlineStyle.single.rawValue)]) setAlternateActionTextAttributes([NSAttributedString.Key.underlineStyle: NSNumber(value: NSUnderlineStyle.single.rawValue)])
} }
@ -346,27 +349,14 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
public func setAlternateNormalTextAttributes(_ attributes: [AnyHashable: Any]?) { public func setAlternateNormalTextAttributes(_ attributes: [AnyHashable: Any]?) {
guard let _attributedText = attributedText, let _attributes = attributes as? [NSAttributedString.Key: Any] else { return } guard let attributedText = attributedText, let attributes = attributes as? [NSAttributedString.Key: Any] else { return }
let attributedString = NSMutableAttributedString(attributedString: _attributedText) let attributedString = NSMutableAttributedString(attributedString: attributedText)
if !attributedString.string.isEmpty { if !attributedString.string.isEmpty {
attributedString.addAttributes(_attributes, range: getFrontRange()) attributedString.addAttributes(attributes, range: frontRange)
attributedString.addAttributes(_attributes, range: getBackRange()) attributedString.addAttributes(attributes, range: backRange)
} }
attributedText = attributedString self.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)
} }
@objc public func setActionTextString(_ actionText: String?) { @objc public func setActionTextString(_ actionText: String?) {
@ -384,9 +374,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
frontText = actionMap?.optionalStringForKey(KeyTitlePrefix) frontText = actionMap?.optionalStringForKey(KeyTitlePrefix)
actionText = actionMap?.optionalStringForKey(KeyTitle) actionText = actionMap?.optionalStringForKey(KeyTitle)
backText = actionMap?.optionalStringForKey(KeyTitlePostfix) backText = actionMap?.optionalStringForKey(KeyTitlePostfix)
actionBlock = label?.createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject)
actionBlock = label?.createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegate: delegateObject)
text = getTextFromStringComponents() text = getTextFromStringComponents()
setLabelAttributes() setLabelAttributes()
} }
@ -433,13 +421,11 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
@available(*, deprecated) @available(*, deprecated)
public convenience init(actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, actionDelegate delegate: ActionObjectDelegate?, buttonDelegate: ButtonObjectDelegate?) { 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) self.init(frontText: actionMap?.optionalStringForKey(KeyTitlePrefix), actionText: actionMap?.optionalStringForKey(KeyTitle), backText: actionMap?.optionalStringForKey(KeyTitlePostfix), actionMap: actionMap, additionalData: additionalData, actionDelegate: delegate, buttonDelegate: buttonDelegate)
} }
@available(*, deprecated) @available(*, deprecated)
public convenience init(frontText: String?, backText: String?, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, actionDelegate delegate: ActionObjectDelegate?, buttonDelegate: ButtonObjectDelegate?) { 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) 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 attributedText = mutableAttributedString
// Added this line for underlining
setAlternateActionTextAttributes([NSAttributedString.Key.underlineStyle: NSNumber(value: NSUnderlineStyle.single.rawValue)]) setAlternateActionTextAttributes([NSAttributedString.Key.underlineStyle: NSNumber(value: NSUnderlineStyle.single.rawValue)])
} }