Moving interaction functionality to label.
This commit is contained in:
parent
07f8921a4f
commit
8da88c4c87
@ -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<UITouch>, with event: UIEvent?) {
|
||||
|
||||
if areTouches(inActionString: touches) {
|
||||
if let action = actionBlock {
|
||||
action()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func areTouches(inActionString touches: Set<UITouch>?) -> 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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<UITouch>, 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<UITouch>?) -> 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 {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user