高斯模糊原理、以及如何使用UIVisualEffectView实现模糊效果

这篇文章将介绍如何使用UIVisualEffectView实现 iOS 系统中各种模糊效果(blur effect)、鲜艳度效果(vibrancy effect),以及模糊效果的原理。

VisualEffectPreview.png

自 iOS 7 设计风格改变后,模糊效果在 app 中越来越常见。合理运用可以改善 app 可用性和外观。

iOS 系统中很多地方使用了模糊效果,最为明显的有控制中心、通知中心。控制中心的模糊显示了当前活跃的app,而非控制中心自身。通知中心没有给整个背景添加模糊,而是为每条通知添加了模糊。这样不仅看起来更美,也让元素更为突出。

VisualEffectControlNotification.png

使用UIVisualEffectView可以实现上述模糊效果。

1. 模糊的原理

模糊起始于图像。要实现模糊,需对每一像素执行模糊算法。最终,得到原始图像的均匀模糊版本。模糊算法的样式、复杂度各不相同,这里介绍一种常见的模糊算法:高斯模糊(gaussian blur)。

高斯模糊将正态分布用于图像处理。本质上,它是一种数据平滑技术(data smoothing),适用于多个场合,图像处理恰好提供了一个直观的应用实例。

1.1 高斯模糊原理

模糊可以理解成每一个像素都取周边像素的平均值。

VisualEffectBeforeValue.png

上图中,2是中间点,周边都是1。中间点取周围点的平均值,就会变成1。如下图所示:

VisualEffectAfterValue.png

在数值上,这是一种平滑化。在图像上,就相当于产生模糊效果,中间点失去细节。

VisualEffectGaussianVS.jpg

计算平均值时,取值范围越大,模糊效果越强烈。

VisualEffectGaussianRadius.png

上面分别是原图、模糊半径3像素、10像素的效果。模糊半径越大,图像就越模糊。从数值角度看,就是数值越平滑。

通常,模糊半径越大,处理图片所需资源越多。iOS 为了避免主线程拥堵,会将尽可能多的任务移交给 GPU 处理。

既然每个点都要取周边像素的平均值,那应如何分配权重呢?如果简单平均,显然不尽合理。因为图像都是连续的,越靠近的点关系越紧密;反之则疏远。因此,加权平均更合理,距离越近的点权重越大,距离越远的点权重越小。

正态分布就是一种可取的权重分配模式。

VisualEffectGaussianCurve.png

正态分布是一维的,图像是二维的,因此我们需要二维的正态分布。

VisualEffectGaussianDimenson.png

正态分布的密度函数叫做高斯函数,可以根据高斯函数计算每个点的权重,然后根据每个点的权重计算高斯模糊值。对所有点重复这个过程,就得到了高斯模糊后的图像。如果原图是彩色图像,可以对RGB三个通道分别做高斯模糊。

2. 模糊效果

VisualEffectMind.png

这里使用WebKit浏览网页,点击皇冠时在底部弹出一个视图,为弹出的视图添加模糊效果。这样后续滑动 webview 时可以看到滚动的模糊效果。

VisualEffectDemo.gif

2.1 UIBlurEffect

UIBlurEffect继承自UIVisualEffect,导航栏、通知中心、控制中心的模糊效果都由UIBlurEffect提供。

下面为bottomView添加模糊效果:

        // 选用 dark 类型 blur
        let blurEffect = UIBlurEffect(style: .dark)
        // blur effect 需要添加到 UIVisualEffectView
        let blurView = UIVisualEffectView(effect: blurEffect)
        bottomView.addSubview(blurView)
        
        blurView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            blurView.heightAnchor.constraint(equalTo: bottomView.heightAnchor),
            blurView.widthAnchor.constraint(equalTo: bottomView.widthAnchor),
        ])
        
        // 子视图不能直接添加到blurView,只能添加到contentView。
        blurView.contentView.addSubview(houseLabel)
        houseLabel.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            houseLabel.centerXAnchor.constraint(equalTo: blurView.centerXAnchor),
            houseLabel.centerYAnchor.constraint(equalTo: blurView.centerYAnchor),
        ])

UIBlurEffect.Style有以下这些类型:

  • extraLight:视图比底层视图颜色浅。
  • light:视图与底层视图亮度类似。
  • dark:视图比底层视图颜色深。
  • extraDark:视图比底层视图颜色更深。
  • regular:自适应用户界面。
  • prominent:根据背景色自动使内容更加突出。

此外,iOS 13 中还添加了一些自适应浅色、深色的模糊效果,如systemUltraThinMaterialLight、systemChromeMaterialDark等。

UIVisualEffectView继承自UIView,需将UIVisualEffectView添加到要添加模糊效果的视图,视图层级中其它子视图需添加到UIVisualEffectViewcontentView中,直接添加到UIVisualEffectView会导致闪退。

运行后效果如下:

VisualEffectBlurEffect.png

2.2 UIVibrancyEffect

UIVibrancyEffect继承自UIVisualEffectUIVibrancyEffectUIVisualEffectView组合使用,可以让内容颜色更鲜艳。

下图显示了UIVibrancyEffect让文本、图标更易识别:

VisualEffectVibrancyVS.png

使用以下代码添加UIVibrancyEffect效果:

        // 使用之前的blurEffect创建UIVibrancyEffect
        let vibrancyEffect = UIVibrancyEffect(blurEffect: blurEffect)
        let vibrancyView = UIVisualEffectView(effect: vibrancyEffect)
        // UIVibrancyEffect 必须添加到配置了UIBlurEffect的UIVisualEffectView的contentView上,否则不会有效果。
        blurView.contentView.addSubview(vibrancyView)

        vibrancyView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            vibrancyView.heightAnchor.constraint(equalTo: blurView.heightAnchor),
            vibrancyView.widthAnchor.constraint(equalTo: blurView.widthAnchor),
            vibrancyView.centerXAnchor.constraint(equalTo: blurView.centerXAnchor),
            vibrancyView.centerYAnchor.constraint(equalTo: blurView.centerYAnchor),
        ])
        
        // 将文本添加到 UIVibrancyEffect 视图的contentView上。添加到vibrancyView上的所有控件都会具有 vibrancy effect。
        vibrancyView.contentView.addSubview(houseLabel)
        houseLabel.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            houseLabel.centerXAnchor.constraint(equalTo: vibrancyView.centerXAnchor),
            houseLabel.centerYAnchor.constraint(equalTo: vibrancyView.centerYAnchor),
        ])

houseLabel添加到vibrancyView前,需先注释掉将其添加到blurView的代码:

        blurView.contentView.addSubview(houseLabel)
        houseLabel.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            houseLabel.centerXAnchor.constraint(equalTo: blurView.centerXAnchor),
            houseLabel.centerYAnchor.constraint(equalTo: blurView.centerYAnchor),
        ])

运行后如下:

VisualEffectVibrancyEffect.png

2.3 UIVisualEffectView

使用UIVisualEffectView时,不能直接向其添加子视图,子视图需添加到其contentView

2.3.1 alpha

应避免设置alpha小于1。半透明视图会导致视图、子视图离屏渲染,进而影响性能。UIVisualEffectView或其父视图alpha小于1会导致模糊效果显示错误,甚至没有模糊效果。

2.3.2 mask

UIVisualEffectView设置mask后,visual effect view 会将mask转发给它的内部视图,包括contentView。也可以直接为contentView添加 mask。为UIVisualEffectView的父视图设置 mask 会导致特效失效,且会抛出异常。

2.3.3 截屏

UIVisualEffectView的许多特效都需要包含该视图的 window 支持。尝试只截取UIVisualEffectView会导致截屏不包含任何特效。截取包含UIVisualEffectView特效的视图层级时,必须截取包含特效的整个UIWindowUIScreen

3. 降低透明度

使用模糊效果时,需考虑用户在系统设置中开启了「降低透明度」时怎么办?

进入系统设置 > 辅助功能 > 显示与文字大小 > 降低透明度,开启降低透明度。返回app后,再次弹出底部视图,效果如下:

VisualEffectReduceTransparency.jpeg

开启「降低透明度」后,模糊效果会消失。使用UIAccessibility.isReduceTransparencyEnabled方法可以查看当前是否开启了「降低透明度」。

在设置透明效果前添加以下代码,即如果开启了「降低透明度」,就不使用模糊效果。

        // 系统开启了「降低透明度」
        guard !UIAccessibility.isReduceTransparencyEnabled else {
            bottomView.backgroundColor = .lightGray
            bottomView.addSubview(houseLabel)
            houseLabel.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                houseLabel.centerXAnchor.constraint(equalTo: bottomView.centerXAnchor),
                houseLabel.centerYAnchor.constraint(equalTo: bottomView.centerYAnchor),
            ])
            return
        }

运行app后,开启「降低透明度」后app也可以正常显示了。

Demo名称:VisualEffectView
源码地址:https://github.com/pro648/BasicDemos-iOS/tree/master/VisualEffectView

参考资料:

  1. 高斯模糊的算法
  2. UIVisualEffectView Tutorial: Getting Started

欢迎更多指正:https://github.com/pro648/tips

本文地址:https://github.com/pro648/tips/blob/master/sources/高斯模糊原理、以及如何使用UIVisualEffectView实现模糊效果.md

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,313评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,369评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,916评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,333评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,425评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,481评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,491评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,268评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,719评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,004评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,179评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,832评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,510评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,153评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,402评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,045评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,071评论 2 352

推荐阅读更多精彩内容