Moving interaction functionality to label.

This commit is contained in:
Christiano, Kevin 2019-04-25 11:41:06 -04:00
parent 07f8921a4f
commit 8da88c4c87
2 changed files with 106 additions and 93 deletions

View File

@ -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
}
}

View File

@ -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 {