Added button delegate checks for labelWithInternalButton. WIP with multi-action label.
This commit is contained in:
parent
9c6ddc2470
commit
d1153d4bf8
@ -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<UITouch>, with event: UIEvent?) {
|
||||
|
||||
if areTouches(inActionString: touches), let action = actionBlock {
|
||||
@ -375,6 +429,7 @@ extension Label {
|
||||
}
|
||||
}
|
||||
|
||||
// For LabelWithInternalButton
|
||||
private func areTouches(inActionString touches: Set<UITouch>?) -> 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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user