You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS Swift:如何为TextView中的NSTextAttachment添加点击识别?

识别NSTextAttachment的点击操作方案

因为NSTextAttachment并非UIView子类,无法直接添加手势识别器,我们需要通过监听UITextView的点击事件,判断点击位置是否落在附件区域内来实现需求。

具体实现步骤

  1. 为UITextView添加点击手势
  2. 在手势回调中计算点击位置对应的文本索引
  3. 检查该索引处的属性是否包含NSTextAttachment
  4. 确认是附件后执行对应逻辑

代码实现

1. 修改表格单元格配置代码

在cellForRowAt中为文本视图添加手势,并绑定行号用于后续定位数据:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "ChatTableViewCell", for: indexPath) as! ChatTableViewCell
    let attachment = messageData[indexPath.row].attachment
      
    let attributedText2 = NSMutableAttributedString(string: messageData[indexPath.row].message + "\n\n")
    let attachmentString = NSAttributedString(attachment: attachment)
    attributedText2.append(attachmentString)
    
    // 可选:给附件对应的文本范围添加自定义标记,提升判断准确性
    let attachmentRange = NSRange(location: attributedText2.length - attachmentString.length, length: attachmentString.length)
    attributedText2.addAttribute(NSAttributedString.Key(rawValue: "isAttachment"), value: true, range: attachmentRange)
    
    cell.chatTextView.attributedText = attributedText2
    cell.chatTextView.isEditable = false
    cell.chatTextView.isSelectable = false
    
    // 避免重复添加手势
    cell.chatTextView.gestureRecognizers?.removeAll(where: { $0 is UITapGestureRecognizer })
    let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleAttachmentTap(_:)))
    cell.chatTextView.addGestureRecognizer(tapGesture)
    cell.chatTextView.tag = indexPath.row

    return cell  
}

2. 实现点击回调逻辑

@objc func handleAttachmentTap(_ gesture: UITapGestureRecognizer) {
    guard let textView = gesture.view as? UITextView else { return }
    let tapLocation = gesture.location(in: textView)
    
    // 获取点击位置对应的文本位置
    guard let textPosition = textView.closestPosition(to: tapLocation),
          let range = textView.textRange(from: textPosition, to: textPosition) else {
        return
    }
    
    let characterIndex = textView.offset(from: textView.beginningOfDocument, to: textPosition)
    guard let attributedText = textView.attributedText else { return }
    
    // 获取该位置的文本属性
    let attributes = attributedText.attributes(at: characterIndex, effectiveRange: nil)
    
    // 验证是否为目标附件
    if attributes[NSAttributedString.Key(rawValue: "isAttachment")] as? Bool == true,
       let attachment = attributes[.attachment] as? NSTextAttachment {
        let row = textView.tag
        let targetData = messageData[row]
        // 这里执行附件点击后的逻辑,比如预览图片、跳转等
        print("点击了第\(row)行的附件:\(attachment)")
    }
}

注意事项

  • 确保UITextView的isEditable设为false,避免系统默认交互干扰手势触发
  • 若需要保留文本选择功能,可将isSelectable设为true,并调整手势的cancelsTouchesInView属性为false

内容的提问来源于stack exchange,提问作者husharoonie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 00:15:35