Restructuring the tappable text feature. Must account of the offset incurred by adding an extra space and image attachment to the overall length of the label. Included a sorting feature to ensure clauses array is always in ascending order by location.

This commit is contained in:
Kevin G Christiano 2019-07-26 13:39:28 -04:00
parent 849a4d761a
commit 81ccaa5bbd

View File

@ -18,7 +18,9 @@ public typealias ActionBlock = () -> Void
//------------------------------------------------------
public var makeWholeViewClickable = false
fileprivate var hasAttachmentImage = false
/// Indication that attributed text attachment will affect range location of attributed text.
fileprivate var hasAttachmentImageInsideText = 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
@ -40,7 +42,17 @@ public typealias ActionBlock = () -> Void
//------------------------------------------------------
public var clauses: [ActionableClause] = [] {
didSet { isUserInteractionEnabled = !clauses.isEmpty }
didSet {
isUserInteractionEnabled = !clauses.isEmpty
if clauses.count > 1 {
clauses.sort{ first, second in
guard let firstLocation = first.range?.location,
let secondLocation = second.range?.location
else { return false }
return firstLocation < secondLocation
}
}
}
}
/// Used for tappable links in the text.
@ -212,6 +224,8 @@ public typealias ActionBlock = () -> Void
let range = NSRange(location: location, length: length)
print(attributeType)
switch attributeType {
case "underline":
attributedString.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: range)
@ -233,7 +247,10 @@ public typealias ActionBlock = () -> Void
mutableString.append(NSAttributedString(attachment: imageAttachment))
attributedString.insert(mutableString, at: location)
(label as? Label)?.hasAttachmentImage = true
if location < attributedString.length {
(label as? Label)?.hasAttachmentImageInsideText = true
}
case "font":
if let fontStyle = attribute.optionalStringForKey("style") {
@ -332,19 +349,20 @@ public typealias ActionBlock = () -> Void
attributedString.addAttribute(.font, value: fontObj.withSize(stylerSize) as Any, range: range)
}
}
// Loop the original attributed string, resize the image attachments.
originalAttributedString.enumerateAttribute(.attachment, in: NSRange(location: 0, length: originalAttributedString.length), options: []) { value, range, stop in
if value is NSTextAttachment, let attachment = value as? NSTextAttachment,
let stylerSize = MFStyler.sizeObjectGeneric(forCurrentDevice: attachment.bounds.width)?.getValueBased(onSize: size) {
let dimension = round(stylerSize)
attachment.bounds = CGRect(x: 0, y: 0, width: dimension, height: dimension)
}
}
attributedText = attributedString
} else if !MVMCoreGetterUtility.fequal(a: Float(standardFontSize), b: 0.0), let sizeObject = self.sizeObject ?? MFStyler.sizeObjectGeneric(forCurrentDevice: standardFontSize) {
self.font = self.font.withSize(sizeObject.getValueBased(onSize: size))
} else if !MVMCoreGetterUtility.fequal(a: Float(standardFontSize), b: 0.0), let sizeObject = sizeObject ?? MFStyler.sizeObjectGeneric(forCurrentDevice: standardFontSize) {
font = font.withSize(sizeObject.getValueBased(onSize: size))
}
}
@ -379,8 +397,6 @@ public typealias ActionBlock = () -> Void
mutableString.append(NSAttributedString(string: " "))
mutableString.append(NSAttributedString(attachment: Label.getTextAttachmentImage(dimension: font.pointSize)))
self.attributedText = mutableString
hasAttachmentImage = true
}
/**
@ -403,7 +419,9 @@ public typealias ActionBlock = () -> Void
mutableString.insert(NSAttributedString(attachment: Label.getTextAttachmentImage(dimension: font.pointSize)), at: index + 1)
self.attributedText = mutableString
hasAttachmentImage = true
if index < attributedText.length {
hasAttachmentImageInsideText = true
}
}
static func getTextAttachmentImage(dimension: CGFloat) -> NSTextAttachment {
@ -425,7 +443,7 @@ extension Label {
text = nil
attributedText = nil
originalAttributedString = nil
hasAttachmentImage = false
hasAttachmentImageInsideText = false
styleB2(true)
}
@ -528,8 +546,36 @@ extension Label {
@objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) {
var offset = 0
var attachmentLocations: [Int] = []
var attachmentIndex = 0
// Used to identify all images attached in the label.
if hasAttachmentImageInsideText {
attributedText?.enumerateAttribute(.attachment, in: NSRange(location: 0, length: attributedText!.length), options: []) { value, range, stop in
guard value is NSTextAttachment else { return }
attachmentLocations.append(range.location)
if range.location == 0 {
offset = 1
}
}
}
for clause in clauses {
if let range = clause.range, gesture.didTapAttributedTextInLabel(self, inRange: range) {
guard let range = clause.range else { return }
// Must increment an offset becuase every image added to text interior increases text length by 1 or 2 depending on location.
if hasAttachmentImageInsideText {
if range.location > attachmentLocations[attachmentIndex] {
offset += 2
attachmentIndex += 1
}
}
// This determines if we tapped on the desired range of text.
if gesture.didTapAttributedTextInLabel(self, inRange: range, offset: offset) {
clause.performAction()
return
}
@ -540,31 +586,17 @@ extension Label {
// MARK: -
extension UITapGestureRecognizer {
func didTapAttributedTextInLabel(_ label: Label, inRange targetRange: NSRange) -> Bool {
func didTapAttributedTextInLabel(_ label: Label, inRange targetRange: NSRange, offset: Int) -> Bool {
if label.makeWholeViewClickable {
return true
}
guard let attributedText = label.attributedText, let font = label.font else { return false }
var fontToAnalyze: UIFont? = font
// Necessary where label text is not consistent in font.
for attr in attributedText.attributes(at: targetRange.location, effectiveRange: nil) {
if attr.key == NSAttributedString.Key.font {
fontToAnalyze = attr.value as? UIFont
}
}
let range = label.hasAttachmentImage ? NSRange(location: 0, length: attributedText.length) : targetRange
let mutableAttribString = NSMutableAttributedString(attributedString: attributedText)
mutableAttribString.addAttributes([NSAttributedString.Key.font: fontToAnalyze as Any], range: range)
guard let attributedText = label.attributedText else { return false }
let textStorage = NSTextStorage(attributedString: attributedText)
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: .zero)
let textStorage = NSTextStorage(attributedString: mutableAttribString)
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
@ -576,6 +608,11 @@ extension UITapGestureRecognizer {
let indexOfGlyph = layoutManager.glyphIndex(for: location(in: label), in: textContainer)
if label.hasAttachmentImageInsideText {
let location = targetRange.location + offset
return NSLocationInRange(indexOfGlyph, NSRange(location: location, length: targetRange.length))
}
return NSLocationInRange(indexOfGlyph, targetRange)
}
}