mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-21 00:52:43 +02:00
Setup NSTextAttachmentViewProvider
This commit is contained in:
@@ -18,8 +18,8 @@ import UIKit
|
||||
|
||||
extension UITextView {
|
||||
private enum Constants {
|
||||
/// Distance threshold at which link can still be considered as "near" from location.
|
||||
static let linkHorizontalDistanceThreshold: CGFloat = 16.0
|
||||
/// Distance threshold at which linkified text attachment can still be considered as "near" location.
|
||||
static let attachmentLinkHorizontalDistanceThreshold: CGFloat = 16.0
|
||||
}
|
||||
|
||||
/// Determine if there is a link near a location point in UITextView bounds.
|
||||
@@ -27,40 +27,63 @@ extension UITextView {
|
||||
/// - Parameters:
|
||||
/// - point: The point inside the UITextView bounds
|
||||
/// - Returns: true to indicate that a link has been detected near the location point.
|
||||
@objc func isThereALinkNearPoint(_ point: CGPoint) -> Bool {
|
||||
@objc func isThereALinkNearLocation(_ point: CGPoint) -> Bool {
|
||||
return urlForLinkAtLocation(point) != nil
|
||||
}
|
||||
|
||||
/// Detect link near a location point in UITextView bounds.
|
||||
///
|
||||
/// - Parameter point: The point inside the UITextView bounds
|
||||
/// - Returns: link detected at given location
|
||||
@objc func urlForLinkAtLocation(_ point: CGPoint) -> URL? {
|
||||
guard bounds.contains(point),
|
||||
let textPosition = closestPosition(to: point)
|
||||
else {
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// The value of `NSLinkAttributeName` attribute could be an URL or a String object.
|
||||
func attributeToLink(_ attribute: Any) -> URL? {
|
||||
if let link = attribute as? URL {
|
||||
return link
|
||||
} else if let stringURL = attribute as? String {
|
||||
return URL(string: stringURL)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Depending on cursor position on a character containing both an attachment
|
||||
// and a link (e.g. a mention pill), a positive result can be retrieved either
|
||||
// from textStylingAtPosition or tokenizer's rangeEnclosingPosition.
|
||||
if let attributes = textStyling(at: textPosition, in: .forward),
|
||||
attributes[.link] != nil {
|
||||
let linkAttribute = attributes[.link] {
|
||||
// Using textStyling shouldn't provide false positives.
|
||||
return true
|
||||
return attributeToLink(linkAttribute)
|
||||
} else if let textRange = tokenizer.rangeEnclosingPosition(textPosition,
|
||||
with: .character,
|
||||
inDirection: .layout(.left)) {
|
||||
let startIndex = offset(from: beginningOfDocument, to: textRange.start)
|
||||
if attributedText.attribute(.link, at: startIndex, effectiveRange: nil) != nil {
|
||||
if let linkAttribute = attributedText.attribute(.link, at: startIndex, effectiveRange: nil) {
|
||||
// Fix false positives from tokenizer's rangeEnclosingPosition.
|
||||
// These occur if given point is located on the same line as a
|
||||
// trailing character containing a mention pill. Detected link is
|
||||
// trailing linkified text attachment. Detected link is
|
||||
// rejected if actual distance from attachment trailing to point
|
||||
// is greater than linkHorizontalDistanceThreshold.
|
||||
let glyphIndex = layoutManager.glyphIndexForCharacter(at: startIndex)
|
||||
let attachmentWidth = layoutManager.attachmentSize(forGlyphAt: glyphIndex).width
|
||||
let glyphStartX = layoutManager.location(forGlyphAt: glyphIndex).x
|
||||
let distance = point.x - (glyphStartX + attachmentWidth)
|
||||
|
||||
// TODO: improve using a range perhaps (beware of negative values when no attachment)
|
||||
return distance < Constants.linkHorizontalDistanceThreshold
|
||||
// Width is -1 when there is no attachment.
|
||||
if attachmentWidth > 0 {
|
||||
let glyphStartX = layoutManager.location(forGlyphAt: glyphIndex).x
|
||||
let start = glyphStartX - Constants.attachmentLinkHorizontalDistanceThreshold
|
||||
let end = glyphStartX + attachmentWidth + Constants.attachmentLinkHorizontalDistanceThreshold
|
||||
let range = (start...end)
|
||||
|
||||
return range.contains(point.x) ? attributeToLink(linkAttribute) : nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
return NO;
|
||||
}
|
||||
|
||||
return [self isThereALinkNearPoint:point];
|
||||
return [self isThereALinkNearLocation:point];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user