Character index at touch point for UILabel

后端 未结 7 1771
Happy的楠姐
Happy的楠姐 2020-12-05 03:09

For a UILabel, I\'d like to find out which character index is at specific point received from a touch event. I\'d like to solve this problem for iOS 7 using Tex

相关标签:
7条回答
  • 2020-12-05 03:40

    I'm using this in the context of a UIViewRepresentable in SwiftUI, and trying to add links to it. None of the code I found in these answers was quite right (especially for multi-line), and this is as precise (and as clean) as I could get it:

    // set up the text engine
    let layoutManager = NSLayoutManager()
    let textContainer = NSTextContainer(size: .zero)
    let textStorage = NSTextStorage(attributedString: attrString)
    
    // copy over properties from the label
    // assuming left aligned text, might need further adjustments for other alignments
    textContainer.lineFragmentPadding = 0
    textContainer.lineBreakMode = label.lineBreakMode
    textContainer.maximumNumberOfLines = label.numberOfLines
    let labelSize = label.bounds.size
    textContainer.size = labelSize
    
    // hook up the text engine
    layoutManager.addTextContainer(textContainer)
    textStorage.addLayoutManager(layoutManager)
    
    // adjust for the layout manager's geometry (not sure exactly how this works but it's required)
    let locationOfTouchInLabel = tap.location(in: label)
    let textBoundingBox = layoutManager.usedRect(for: textContainer)
    let textContainerOffset = CGPoint(
        x: labelSize.width/2 - textBoundingBox.midX,
        y: labelSize.height/2 - textBoundingBox.midY
    )
    let locationOfTouchInTextContainer = CGPoint(
        x: locationOfTouchInLabel.x - textContainerOffset.x,
        y: locationOfTouchInLabel.y - textContainerOffset.y
    )
    
    // actually perform the check to get the index, accounting for multiple lines
    let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
    
    // get the attributes at the index
    let attributes = attrString.attributes(at: indexOfCharacter, effectiveRange: nil)
    
    // use `.attachment` instead of `.link` so you can bring your own styling
    if let url = attributes[.attachment] as? URL {
         UIApplication.shared.open(url, options: [:], completionHandler: nil)
    }
    
    0 讨论(0)
提交回复
热议问题