-
-
Save saoudrizwan/986714d5a093f481fb3f4f6589418ea6 to your computer and use it in GitHub Desktop.
class LinkResponsiveTextView: UITextView { | |
override init(frame: CGRect, textContainer: NSTextContainer?) { | |
super.init(frame: frame, textContainer: textContainer) | |
self.delaysContentTouches = false | |
// required for tap to pass through on to superview & for links to work | |
self.isScrollEnabled = false | |
self.isEditable = false | |
self.isUserInteractionEnabled = true | |
self.isSelectable = true | |
} | |
required init?(coder aDecoder: NSCoder) { | |
fatalError("init(coder:) has not been implemented") | |
} | |
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { | |
// location of the tap | |
var location = point | |
location.x -= self.textContainerInset.left | |
location.y -= self.textContainerInset.top | |
// find the character that's been tapped | |
let characterIndex = self.layoutManager.characterIndex(for: location, in: self.textContainer, fractionOfDistanceBetweenInsertionPoints: nil) | |
if characterIndex < self.textStorage.length { | |
// if the character is a link, handle the tap as UITextView normally would | |
if (self.textStorage.attribute(NSLinkAttributeName, at: characterIndex, effectiveRange: nil) != nil) { | |
return self | |
} | |
} | |
// otherwise return nil so the tap goes on to the next receiver | |
return nil | |
} | |
} |
Hey, sorry for the delay, I didn't get a notification :(
It does look better, and it's easier to scan. Two more, if you don't mind :) Probably subjective, though
Since textStorage.attribute(NSLinkAttributeName, at: characterIndex, effectiveRange: nil) != nil
is such a long statement, if it'd be extracted in a constant, the if
would be even easier to scan:
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let location = CGPoint(x: location.x - textContainerInset.left, y: location.y - textContainerInset.top)
let characterIndex = layoutManager.characterIndex(for: location, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
let characterIsPartOfURL = textStorage.attribute(NSLinkAttributeName, at: characterIndex, effectiveRange: nil) != nil
if characterIndex < textStorage.length, characterIsPartOfURL {
return self
}
return nil
}
I'd usually say that using a guard
would make the intent of the whole method slightly better expressed, since it's usually better to "follow the happy path", but in this case the purpose of the method can be thought of "always return nil, except on URLs".
Thanks again for the suggestion, I updated my post to include this solution as well!
Hey @saoudrizwan I'm getting "Fatal error: init(coder:) has not been implemented:" any idea on how or where to implement that so that I get this class working? Thank you!!
I was able to fix the issue by replacing your required init with this:
required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
Thanks @rolandleth, I just added those few bits to better inform people about what was going on. Here's the prettier version: