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:
parent
849a4d761a
commit
81ccaa5bbd
@ -18,7 +18,9 @@ public typealias ActionBlock = () -> Void
|
|||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
|
|
||||||
public var makeWholeViewClickable = false
|
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.
|
/// 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
|
||||||
@ -40,7 +42,17 @@ public typealias ActionBlock = () -> Void
|
|||||||
//------------------------------------------------------
|
//------------------------------------------------------
|
||||||
|
|
||||||
public var clauses: [ActionableClause] = [] {
|
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.
|
/// Used for tappable links in the text.
|
||||||
@ -212,6 +224,8 @@ public typealias ActionBlock = () -> Void
|
|||||||
|
|
||||||
let range = NSRange(location: location, length: length)
|
let range = NSRange(location: location, length: length)
|
||||||
|
|
||||||
|
print(attributeType)
|
||||||
|
|
||||||
switch attributeType {
|
switch attributeType {
|
||||||
case "underline":
|
case "underline":
|
||||||
attributedString.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: range)
|
attributedString.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: range)
|
||||||
@ -233,7 +247,10 @@ public typealias ActionBlock = () -> Void
|
|||||||
mutableString.append(NSAttributedString(attachment: imageAttachment))
|
mutableString.append(NSAttributedString(attachment: imageAttachment))
|
||||||
|
|
||||||
attributedString.insert(mutableString, at: location)
|
attributedString.insert(mutableString, at: location)
|
||||||
(label as? Label)?.hasAttachmentImage = true
|
|
||||||
|
if location < attributedString.length {
|
||||||
|
(label as? Label)?.hasAttachmentImageInsideText = true
|
||||||
|
}
|
||||||
|
|
||||||
case "font":
|
case "font":
|
||||||
if let fontStyle = attribute.optionalStringForKey("style") {
|
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)
|
attributedString.addAttribute(.font, value: fontObj.withSize(stylerSize) as Any, range: range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop the original attributed string, resize the image attachments.
|
// Loop the original attributed string, resize the image attachments.
|
||||||
originalAttributedString.enumerateAttribute(.attachment, in: NSRange(location: 0, length: originalAttributedString.length), options: []) { value, range, stop in
|
originalAttributedString.enumerateAttribute(.attachment, in: NSRange(location: 0, length: originalAttributedString.length), options: []) { value, range, stop in
|
||||||
if value is NSTextAttachment, let attachment = value as? NSTextAttachment,
|
if value is NSTextAttachment, let attachment = value as? NSTextAttachment,
|
||||||
let stylerSize = MFStyler.sizeObjectGeneric(forCurrentDevice: attachment.bounds.width)?.getValueBased(onSize: size) {
|
let stylerSize = MFStyler.sizeObjectGeneric(forCurrentDevice: attachment.bounds.width)?.getValueBased(onSize: size) {
|
||||||
|
|
||||||
let dimension = round(stylerSize)
|
let dimension = round(stylerSize)
|
||||||
attachment.bounds = CGRect(x: 0, y: 0, width: dimension, height: dimension)
|
attachment.bounds = CGRect(x: 0, y: 0, width: dimension, height: dimension)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
attributedText = attributedString
|
attributedText = attributedString
|
||||||
} else if !MVMCoreGetterUtility.fequal(a: Float(standardFontSize), b: 0.0), let sizeObject = self.sizeObject ?? MFStyler.sizeObjectGeneric(forCurrentDevice: standardFontSize) {
|
} else if !MVMCoreGetterUtility.fequal(a: Float(standardFontSize), b: 0.0), let sizeObject = sizeObject ?? MFStyler.sizeObjectGeneric(forCurrentDevice: standardFontSize) {
|
||||||
self.font = self.font.withSize(sizeObject.getValueBased(onSize: size))
|
font = font.withSize(sizeObject.getValueBased(onSize: size))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,8 +397,6 @@ public typealias ActionBlock = () -> Void
|
|||||||
mutableString.append(NSAttributedString(string: " "))
|
mutableString.append(NSAttributedString(string: " "))
|
||||||
mutableString.append(NSAttributedString(attachment: Label.getTextAttachmentImage(dimension: font.pointSize)))
|
mutableString.append(NSAttributedString(attachment: Label.getTextAttachmentImage(dimension: font.pointSize)))
|
||||||
self.attributedText = mutableString
|
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)
|
mutableString.insert(NSAttributedString(attachment: Label.getTextAttachmentImage(dimension: font.pointSize)), at: index + 1)
|
||||||
self.attributedText = mutableString
|
self.attributedText = mutableString
|
||||||
|
|
||||||
hasAttachmentImage = true
|
if index < attributedText.length {
|
||||||
|
hasAttachmentImageInsideText = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func getTextAttachmentImage(dimension: CGFloat) -> NSTextAttachment {
|
static func getTextAttachmentImage(dimension: CGFloat) -> NSTextAttachment {
|
||||||
@ -425,7 +443,7 @@ extension Label {
|
|||||||
text = nil
|
text = nil
|
||||||
attributedText = nil
|
attributedText = nil
|
||||||
originalAttributedString = nil
|
originalAttributedString = nil
|
||||||
hasAttachmentImage = false
|
hasAttachmentImageInsideText = false
|
||||||
styleB2(true)
|
styleB2(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -528,8 +546,36 @@ extension Label {
|
|||||||
|
|
||||||
@objc private func textLinkTapped(_ gesture: UITapGestureRecognizer) {
|
@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 {
|
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()
|
clause.performAction()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -540,31 +586,17 @@ extension Label {
|
|||||||
// MARK: -
|
// MARK: -
|
||||||
extension UITapGestureRecognizer {
|
extension UITapGestureRecognizer {
|
||||||
|
|
||||||
func didTapAttributedTextInLabel(_ label: Label, inRange targetRange: NSRange) -> Bool {
|
func didTapAttributedTextInLabel(_ label: Label, inRange targetRange: NSRange, offset: Int) -> Bool {
|
||||||
|
|
||||||
if label.makeWholeViewClickable {
|
if label.makeWholeViewClickable {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let attributedText = label.attributedText, let font = label.font else { return false }
|
guard let attributedText = label.attributedText else { return false }
|
||||||
|
|
||||||
var fontToAnalyze: UIFont? = font
|
let textStorage = NSTextStorage(attributedString: attributedText)
|
||||||
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
let layoutManager = NSLayoutManager()
|
let layoutManager = NSLayoutManager()
|
||||||
let textContainer = NSTextContainer(size: .zero)
|
let textContainer = NSTextContainer(size: .zero)
|
||||||
let textStorage = NSTextStorage(attributedString: mutableAttribString)
|
|
||||||
|
|
||||||
layoutManager.addTextContainer(textContainer)
|
layoutManager.addTextContainer(textContainer)
|
||||||
textStorage.addLayoutManager(layoutManager)
|
textStorage.addLayoutManager(layoutManager)
|
||||||
@ -576,6 +608,11 @@ extension UITapGestureRecognizer {
|
|||||||
|
|
||||||
let indexOfGlyph = layoutManager.glyphIndex(for: location(in: label), in: textContainer)
|
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)
|
return NSLocationInRange(indexOfGlyph, targetRange)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user