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

Swift 5中UILabel使用NSTextAttachment无法显示完整图片求助

解决Swift中NSTextAttachment在UILabel显示图片过小的问题

问题描述

尝试在Swift 5中使用NSTextAttachment将网络图片展示在UITableViewCell的UILabel(chatNote)中,期望图片以**屏幕宽度75%、高度50%**的尺寸完整显示,但实际仅呈现极小的裁剪片段:

  • 期望效果:图片按比例适配目标尺寸,完整填充label区域
  • 实际效果:仅显示原图的极小一部分,几乎不可见
  • 裁剪细节:仅展示原图的局部小区域

原实现代码

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cellIdentifier = "ChatsAndMessagesChatBoxTableCell"
    var cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? ChatsAndMessagesChatBoxTableCell
    if (cell == nil) {
        cell = ChatsAndMessagesChatBoxTableCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: cellIdentifier) as ChatsAndMessagesChatBoxTableCell
    }

    let pic_url = "https://fastly.picsum.photos/id/0/5000/3333.jpg?hmac=_j6ghY5fCfSD6tvtcV74zXivkJSPIfR9B8w34XeQmvU"

    URLSession.shared.dataTask(with: NSURL(string: pic_url)! as URL, completionHandler: { (data, response, error) -> Void in
        if error != nil {
            print(error ?? "No Error")
            return
        }
        DispatchQueue.main.async(execute: { () -> Void in
            let image = UIImage(data: data!)

            // 1. Create an NSTextAttachment
            let imageAttachment = NSTextAttachment()

            let screenWidth = UIScreen.main.bounds.width
            let screenHeight = UIScreen.main.bounds.height
            let desiredWidth = screenWidth * 0.75
            let desiredHeight = screenHeight * 0.5
            let imageAspectRatio = image!.size.width / image!.size.height
            var finalImageWidth: CGFloat = desiredWidth
            var finalImageHeight: CGFloat = desiredHeight
            // Calculate final dimensions to fit within the desired ratios
            if (desiredWidth / desiredHeight) > imageAspectRatio {
                // Image is wider than the desired ratio, adjust height to fit width
                finalImageHeight = desiredWidth / imageAspectRatio
            } else {
                // Image is taller than or matches the desired ratio, adjust width to fit height
                finalImageWidth = desiredHeight * imageAspectRatio
            }

            imageAttachment.image = image
            imageAttachment.bounds = CGRect(x: 0, y: ((cell?.chatNote.font.capHeight)! - finalImageHeight) / 2, width: finalImageWidth, height: finalImageHeight)

            let imageAttributedString = NSAttributedString(attachment: imageAttachment)
            cell?.chatNote.attributedText = imageAttributedString
        })
    }).resume()

    return cell!
}

问题原因

  1. UILabel配置缺失:默认numberOfLines = 1且未设置preferredMaxLayoutWidth,导致label无法自动调整行高适配大尺寸附件,图片被强制裁剪到默认行高范围内。
  2. bounds的Y轴偏移错误:使用font.capHeight计算垂直偏移时,该值远小于图片目标高度,导致图片向上偏移后大部分区域被label裁剪。
  3. cell复用未处理:未取消cell之前的网络请求,可能导致复用cell时显示错误图片。

修复方案

1. 配置UILabel基础属性

确保label支持多行显示并设置最大布局宽度:

cell.chatNote.numberOfLines = 0
let targetLabelWidth = UIScreen.main.bounds.width * 0.75
cell.chatNote.preferredMaxLayoutWidth = targetLabelWidth

2. 修正NSTextAttachment的bounds设置

调整Y轴偏移计算逻辑,让图片垂直居中且完整显示在label内:

imageAttachment.bounds = CGRect(
    x: 0,
    y: (cell.chatNote.font.descender - finalHeight) / 2,
    width: finalWidth,
    height: finalHeight
)

3. 优化网络请求与cell复用

取消cell之前的网络请求,避免复用错误;同时使用weak cell防止循环引用。

4. 开启TableView自动行高

确保tableView能根据内容自动调整行高:

// 在viewDidLoad中设置
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 200 // 设置合理的预估高度

修复后的完整代码

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cellIdentifier = "ChatsAndMessagesChatBoxTableCell"
    // 强制转换,确保cell已正确注册
    let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! ChatsAndMessagesChatBoxTableCell
    
    // 配置UILabel核心属性
    let screenWidth = UIScreen.main.bounds.width
    let targetLabelWidth = screenWidth * 0.75
    cell.chatNote.numberOfLines = 0
    cell.chatNote.preferredMaxLayoutWidth = targetLabelWidth
    
    guard let picUrl = URL(string: "https://fastly.picsum.photos/id/0/5000/3333.jpg?hmac=_j6ghY5fCfSD6tvtcV74zXivkJSPIfR9B8w34XeQmvU") else {
        return cell
    }
    
    // 取消cell之前的同URL网络请求,避免复用错误
    URLSession.shared.getAllTasks { tasks in
        tasks.filter { $0.currentRequest?.url == picUrl }.forEach { $0.cancel() }
    }
    
    URLSession.shared.dataTask(with: picUrl) { [weak cell] data, response, error in
        guard let data = data, let image = UIImage(data: data), error == nil else {
            print(error ?? "图片加载失败")
            return
        }
        
        DispatchQueue.main.async {
            guard let cell = cell else { return }
            
            let screenHeight = UIScreen.main.bounds.height
            let desiredWidth = targetLabelWidth
            let desiredHeight = screenHeight * 0.5
            let imageAspectRatio = image.size.width / image.size.height
            
            // 计算适配后的图片尺寸,保持原比例
            var finalWidth: CGFloat = desiredWidth
            var finalHeight: CGFloat = desiredHeight
            if desiredWidth / desiredHeight > imageAspectRatio {
                finalHeight = desiredWidth / imageAspectRatio
            } else {
                finalWidth = desiredHeight * imageAspectRatio
            }
            
            let imageAttachment = NSTextAttachment()
            imageAttachment.image = image
            // 调整垂直偏移,让图片在label内垂直居中
            imageAttachment.bounds = CGRect(
                x: 0,
                y: (cell.chatNote.font.descender - finalHeight) / 2,
                width: finalWidth,
                height: finalHeight
            )
            
            let attributedString = NSAttributedString(attachment: imageAttachment)
            cell.chatNote.attributedText = attributedString
            
            // 刷新tableView布局,确保行高更新
            tableView.beginUpdates()
            tableView.endUpdates()
        }
    }.resume()
    
    return cell
}

额外注意事项

  • 自定义cell中,确保chatNote的约束正确:左右边距绑定到contentView(或设置固定宽度等于targetLabelWidth),上下边距绑定到contentView,让label能根据内容自动拉伸高度。
  • 若图片尺寸过大,建议先压缩图片再设置到NSTextAttachment,避免内存占用过高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 11:43:10