Bootstrap

实现 iOS 自定义高斯模糊文字效果的 UILabel(文末有Demo)

引言

在实际的项目开发中,我们经常会遇到一些看似简单,但在实现时却充满挑战的需求。比如在开发付费通话功能时,我们需要展示最近通话的用户记录,其中包括用户的头像和昵称。为了保护用户隐私并且提升界面的美观性,我们希望对这些信息进行模糊处理。

头像的模糊处理是比较容易实现的,iOS 系统提供了丰富的图像处理工具。但当我们尝试对文本进行模糊处理时,问题就变得不那么简单了。文字的高斯模糊效果并不像图片那样直接应用已有的 API,想要达到类似的效果需要一些技巧。

本篇博客将介绍如何通过定义UILabel,实现对文字内容的高斯模糊效果,解决这一难题。

实现思路

要实现文字的高斯模糊效果,我们可以借鉴实现图片高斯模糊的思路。在处理图片模糊时,通常有两种常见的方案:

  1. 使用系统的UIBlurEffect 高斯模糊图层:这是最简单的方法,我们可以直接使用系统提供的 UIBlurEffect,将其作为一个覆盖层叠加在图片上。这种方法非常适合静态图像的模糊处理,系统会自动为我们处理模糊效果,简单且高效。
  2. 使用CIFilter 进行图像处理:另一种方法是利用Core Image 的 CIFilter,通过为图片应用高斯模糊滤镜来实现。这种方法允许我们对模糊的强度和效果进行更多的控制,也能处理动态变化的图像。

对于文字的高斯模糊效果,我们可以借用类似的思路。我们将通过自定义UILabel,在绘制文本时对其进行处理,通过借助Core Graphics和CIFilter等工具,在渲染文本时手动实现。首先将文字内容绘制成一个图片,然后再为图片添加滤镜效果,最后再将图片绘制到自定义UILabel中显示。

代码实现

代码实现我们可以分为三个部分:

  1. 首先是通过Core Graphics将文本绘制为图片。
  2. 然后是利用CIFilter为图片添加高斯模糊滤镜。
  3. 将高斯模糊后的图片绘制到自定的UILabel中。

我们创建一个PHBlurLabel集成自UILabel,然后重写它的draw(_ rect:CGRect)方法来一步步实现这一效果。

使用Core Graphics将文本绘制为图片

借助Core Graphics将文本内容根据文本的设置属性生成一张文本图片,具体代码如下:

class PHBlurLabel: UILabel {
    
    
    override func draw(_ rect: CGRect) {
        if rect.isEmpty {
            return
        }
        guard let text = self.text else {
            return
        }
        // 1.获取当前上下文
        UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
        guard UIGraphicsGetCurrentContext() != nil else {
            return
        }
        // 2.设置文本属性
        var attributes: [NSAttributedString.Key : Any] = [:]
        attributes[NSAttributedString.Key.font] = self.font
        attributes[NSAttributedString.Key.foregroundColor] = self.textColor
        // 3.绘制文本
        text.draw(in: rect, withAttributes: attributes)
        // 4.获取图片
        guard let textImage = UIGraphicsGetImageFromCurrentImageContext() else {
            return
        }
        UIGraphicsEndImageContext()
        ....
    }
    ....
}
  1. 我们只需要在文本有数据时再绘制,因此先加了两组判断。
  2. 获取绘图的上下文。
  3. 设置文本属性,开始绘制文本。
  4. 获取绘制好的图片,关闭绘图上下文。

使用CIFilter为图片添加高斯模糊滤镜

借助CIFilter的强大功能为图片添加CIGaussianBlur高斯模糊滤镜,并输出新的图片,具体代码如下:

        // 5.绘制模糊效果
        guard let ciImage = CIImage(image: textImage) else {
            return
        }
        let filter = CIFilter(name: "CIGaussianBlur")
        filter?.setValue(ciImage, forKey: kCIInputImageKey)
        filter?.setValue(10.0, forKey: kCIInputRadiusKey)
        guard let outputImage = filter?.outputImage else {
            return
        }
  1. 将UIImage转换为CIIMage。
  2. 选择合适滤镜。
  3. 设置图片的模糊程度。
  4. 获取滤镜后的图片。

重新绘制图片

接下来我们需要将已经获取的模糊图片再重新绘制到自定义的UILabel上,这时候需要注意Core Graphics的坐标系与我们通常所使用frame是相反的,因此我们需要做一点处理来使图片正常显示而不是颠倒的图片。

        // 6.绘制图片
        guard let contextRef = UIGraphicsGetCurrentContext() else {
            return
        }
        guard let cgImage = CIContext().createCGImage(outputImage, from: ciImage.extent) else {
            return
        }
        // 保存当前上下文状态
        contextRef.saveGState()
        // 翻转上下文坐标系,使其与图像坐标系对齐
        contextRef.translateBy(x: 0, y: rect.size.height)  // 平移
        contextRef.scaleBy(x: 1.0, y: -1.0)                // 翻转y轴
        // 在修正后的坐标系中绘制图片
        contextRef.draw(cgImage, in: rect)
        // 恢复上下文状态
        contextRef.restoreGState()

模糊文字UILabel使用

在使用时我们只需要想普通的UILabel一样进行创建,设置和添加即可。

        let blurLabel = PHBlurLabel()
        blurLabel.font = UIFont.systemFont(ofSize: 20)
        blurLabel.text = "Hello World"
        blurLabel.textColor = .black
        view.addSubview(blurLabel)
        blurLabel.snp.makeConstraints { make in
            make.top.equalTo(300)
            make.centerX.equalToSuperview()
        }
        

效果如下:

这是模糊程度为10的效果,可以根据需要调整数值。但是我们看见仍然有一个小瑕疵,文字的左右分界线过于明显,这通常不会被设计同学接受。

效果优化

由于文字被高斯模糊之后,不可避免的需要向四周扩散一些像素,这导致在UILabel的边缘会出现非常明显的接线。为了解决这一问题,我们不得不让UILabel的实际宽度比内容宽度稍微大上一些,也就是说在文字与组件之间有一些内边距。

为了实现这个效果我们可以通过重写UIlabel的intrinsicContentSize属性,具体代码如下:

    var textInsets = UIEdgeInsets(top: 0, left: 4, bottom: 0, right: 4)
    
    override var intrinsicContentSize: CGSize {
        let size = super.intrinsicContentSize
        return CGSize(width: size.width + textInsets.left + textInsets.right,
                      height: size.height + textInsets.top + textInsets.bottom)
    }

并在绘制时调整它的rect:

        // 2.设置文本属性
        var attributes: [NSAttributedString.Key : Any] = [:]
        attributes[NSAttributedString.Key.font] = self.font
        attributes[NSAttributedString.Key.foregroundColor] = self.textColor
        let rect = CGRect(x: 0 + 4.0, y: 0, width: rect.size.width - 8.0, height: rect.size.height)
        // 3.绘制文本
        text.draw(in: rect, withAttributes: attributes)

最终效果如下:

结语

通过本文的介绍,我们成功地实现了一个自定义的高斯模糊 UILabel,并展示了如何通过继承 UILabel 类来绘制带有模糊效果的文字。虽然系统默认的文本渲染并不直接支持模糊效果,但通过灵活运用 Core Graphics 和 Core Image 的滤镜,我们可以非常方便地对文本进行自定义处理,从而实现类似高斯模糊的视觉效果。

值得注意的是,虽然这种方法能够为我们带来很好的效果,但在实际应用中我们也要关注性能。高斯模糊可能会对渲染性能产生一定影响,尤其是在动态文本或高频率更新的场景中,因此我们需要根据实际需求进行优化。

总的来说,文本的高斯模糊处理虽然在 iOS 开发中并不常见,但通过一些巧妙的技巧,我们可以在不同的应用场景中灵活运用,为用户带来更丰富、更美观的界面体验。

希望本文能为你在 iOS 开发中解决类似问题提供一些帮助,如果你有更多的优化思路或实践经验,欢迎分享。

源码地址:

https://github.com/Louis1239/PHBlurLabel.githttps://github.com/Louis1239/PHBlurLabel.githttps://mp.csdn.net/mp_download/manage/download/UpDetailedhttps://mp.csdn.net/mp_download/manage/download/UpDetailed

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;