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

求助:如何用VisualEffectView仅模糊UITextView文本而非整个视图

解决UITextView仅模糊文本而非整个视图的问题

嗨,我来帮你搞定这个只模糊UITextView文本、不碰整个视图的问题~你现在遇到的情况是把VisualEffectView直接铺满了整个UITextView的bounds,所以连视图边缘也被模糊了。咱们可以通过几种方式精准控制模糊范围,只作用在文本上:

方法一:调整VisualEffectView的Frame到文本实际区域

这是最贴合你当前使用VisualEffectView库的方案,核心思路是只让模糊视图覆盖文本实际占据的区域,而不是整个UITextView:

  1. 先获取UITextView中文本的实际渲染范围,UITextView的layoutManager能帮我们拿到这个区域;
  2. 把这个区域的坐标转换到UITextView的坐标系下;
  3. 给VisualEffectView设置这个精准的frame,同时处理好动态文本更新的情况。

代码示例:

var visualEffectViewDescription = VisualEffectView()
visualEffectViewDescription.blurRadius = 3

// 获取文本实际占据的矩形区域
let textContainerRect = descriptionOfWorkout.layoutManager.usedRect(for: descriptionOfWorkout.textContainer)
// 转换为UITextView自身的坐标系
let targetFrame = descriptionOfWorkout.convert(textContainerRect, from: descriptionOfWorkout.textContainer)

// 设置模糊视图的frame
visualEffectViewDescription.frame = targetFrame
// 添加自动适配,防止文本变化时模糊区域不跟着变
visualEffectViewDescription.autoresizingMask = [.flexibleWidth, .flexibleHeight]

descriptionOfWorkout.addSubview(visualEffectViewDescription)

// 如果文本是动态编辑的,记得在textViewDidChange里更新frame
func textViewDidChange(_ textView: UITextView) {
    let updatedTextRect = textView.layoutManager.usedRect(for: textView.textContainer)
    let updatedFrame = textView.convert(updatedTextRect, from: textView.textContainer)
    visualEffectViewDescription.frame = updatedFrame
}

方法二:用CoreGraphics生成模糊文本图像

如果VisualEffectView的调整满足不了需求,也可以直接生成模糊的文本图像,替代原文本显示:

func generateBlurredTextImage(text: String, font: UIFont, textColor: UIColor, blurRadius: CGFloat) -> UIImage? {
    // 先绘制原始文本到图像上下文
    let textSize = text.size(withAttributes: [.font: font])
    UIGraphicsBeginImageContextWithOptions(textSize, false, UIScreen.main.scale)
    text.draw(at: .zero, withAttributes: [.font: font, .foregroundColor: textColor])
    guard let originalTextImage = UIGraphicsGetImageFromCurrentImageContext() else {
        UIGraphicsEndImageContext()
        return nil
    }
    UIGraphicsEndImageContext()
    
    // 应用高斯模糊滤镜
    let ciContext = CIContext(options: nil)
    guard let ciImage = CIImage(image: originalTextImage) else { return nil }
    let blurFilter = CIFilter(name: "CIGaussianBlur")!
    blurFilter.setValue(ciImage, forKey: kCIInputImageKey)
    blurFilter.setValue(blurRadius, forKey: kCIInputRadiusKey)
    
    guard let blurredCiImage = blurFilter.outputImage else { return nil }
    return UIImage(ciImage: blurredCiImage, scale: UIScreen.main.scale, orientation: .up)
}

// 使用示例:把模糊图像作为文本附件显示在UITextView中
if let blurredImage = generateBlurredTextImage(text: descriptionOfWorkout.text, 
                                               font: descriptionOfWorkout.font!, 
                                               textColor: descriptionOfWorkout.textColor, 
                                               blurRadius: 3) {
    let textAttachment = NSTextAttachment()
    textAttachment.image = blurredImage
    textAttachment.bounds = CGRect(origin: .zero, size: blurredImage.size)
    let attributedText = NSAttributedString(attachment: textAttachment)
    descriptionOfWorkout.attributedText = attributedText
}

这种方法适合静态文本,如果是可编辑的UITextView,需要实时监听文本变化并重新生成模糊图像。

方法三:给VisualEffectView添加文本形状的遮罩

通过遮罩层,让VisualEffectView只在文本区域显示模糊效果:

var visualEffectViewDescription = VisualEffectView()
visualEffectViewDescription.frame = descriptionOfWorkout.bounds
visualEffectViewDescription.blurRadius = 3

// 创建文本形状的遮罩层
let maskLayer = CAShapeLayer()
let textPath = UIBezierPath()

// 遍历文本的每一行,添加到路径中
descriptionOfWorkout.layoutManager.enumerateLineFragments(forGlyphRange: NSRange(location: 0, length: descriptionOfWorkout.text.count)) { rect, usedRect, textContainer, glyphRange, stop in
    let convertedRect = descriptionOfWorkout.convert(usedRect, from: textContainer)
    textPath.append(UIBezierPath(rect: convertedRect))
}

maskLayer.path = textPath.cgPath
maskLayer.fillRule = .evenOdd
visualEffectViewDescription.layer.mask = maskLayer

descriptionOfWorkout.addSubview(visualEffectViewDescription)

// 同样,文本变化时需要更新遮罩
func textViewDidChange(_ textView: UITextView) {
    let newTextPath = UIBezierPath()
    textView.layoutManager.enumerateLineFragments(forGlyphRange: NSRange(location: 0, length: textView.text.count)) { rect, usedRect, textContainer, glyphRange, stop in
        let convertedRect = textView.convert(usedRect, from: textContainer)
        newTextPath.append(UIBezierPath(rect: convertedRect))
    }
    (visualEffectViewDescription.layer.mask as? CAShapeLayer)?.path = newTextPath.cgPath
}

总结

最推荐第一种方法,既贴合你使用VisualEffectView库的需求,实现起来也最简单,能精准控制模糊范围在文本区域内。如果是动态编辑的文本,别忘了在文本变化时更新模糊视图的frame或者遮罩哦~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 15:57:45