【译】UIVisualEffectView教程

9,360 阅读9分钟

前言

最近感觉自己总是学习网络相关的, 有点"疲劳"了. 所以今天换个口味, 试着翻译下国外大神的博客. 有不好的地方还请赐教!

原文链接: www.raywenderlich.com/178486/uivi…

正文

从iOS7开始, 系统风格有了令人激动的改变, 模糊效果在APP设计中扮演了重要的角色. 若你使用适当, 模糊效果可以显著的提高APP的视觉感受.

先让我们一起看看苹果是如何在系统中使用的.

其中, 控制中心是一个值得关注的示例. 模糊背景为控制中心的操作提供环境 - 虽然控制中心不属于某一个APP, 但它却显示在这些活动APP的上方.

通知中心也使用了该效果, 但是, 这里不是让整个背景都模糊了, 每一个extension或通知都有自己模糊的背景. 这不仅看上去漂亮, 而且使每一个元素都很显眼.

那么如何在自己的APP中再现这些效果呢? 没错, 就是使用iOS内建的UIVisualEffectView! 该篇, 你将学到关于使用"模糊效果"的一切, 以使你的APP更出众.

关于"模糊效果"你需要了解的事实

如何优雅,高效的使用需要一定的技巧. 这里会了解到"模糊效果"使用到的常规算法.

模糊效果是如何工作的

所有的模糊效果都始于一张图片. 为了实现该效果, 你需要对图片的每一个像素应用模糊算法(blurring algorithm), 这样可以得到一个均匀的带有模糊效果的副本. 不同种类的模糊算法有很大差异, 并且错综复杂, 这里我们仅仅讨论一个常用的Gaussian blur(高斯模糊).

通常, 模糊算法会根据一个像素周围的像素为其生成一个新的色值.考虑下面的网格图片:

上面的每一个网格单元代表了一个独立的像素, 每一个像素都有一个1~10之间的数字. 当模糊算法在计算中间像素对应的新值时, 会取其周围数字的平均值填入其中. 得到下面的结果:

你可以对原始图片的每一个像素都执行上述过程. 上述过程只是对某一个像素周围的每个方向取一个像素计算新的值. 你可以扩大模糊半径(上述案例的中间像素的外围像素个数)从而增加模糊程度. 下面是个例子:

注意事项: 通常, 更大的模糊半径意味着处理图片需要更多的资源. iOS通常会将处理图片的任务转交给GPU, 去保证主线程的通畅.

模糊效果设计策略

人们更倾向于把注意力集中在清晰的元素上, 而不是不清晰的. 不管你信还是不信, 这就是我们的眼睛工作的结果. 随着物体变近或远, 眼睛随之聚焦, 这就是适应(Focusing on an object as it moves closer or further away from the eye is known as accommodation), 是他帮助你观察周围事物的深度和距离.

APP的设计师们运用了这个事实, 将屏幕上那些不重要的元素变得模糊, 从而使人们的实现保留在清晰的元素上.下面是一个示例, Twitter客户端的截屏:

上面的图片中, 背后的用户界面几乎是不能辨别的, 这为用户提供了一个环境, 去识别他们处在哪个层级关系中.例如, 你可能会意识到, 一旦你选择了列出账号中的一个, 你就会返回到后面带有模糊效果的视图中.

注意: 避免在你的APP中滥用模糊效果.即使模糊能提供很好看的效果, 如果你使用不当或使用过于频繁, 它也会令人烦恼.

跟随设计标准去使用模糊可以直接吸引用户注意力, 那样你几乎不会失败.查看Apple开发者中心的iOS Human Interface Guidelines document文档中Designing for iOS部分可以获取更多信息.

开始动手

为了学习如何实现模糊效果, 你需要在GrimmAPP中添加一些.

这个APP向用户展示了很多童话故事. 当用户点击其中一个时, APP会在屏幕上展现整个故事.用户可以自定义展现的字体, 文本的对其方式, 或颜色主题(白天或夜晚).

你可以下载这个工程开始, 在Xcode中打开Grimm.xcodeproj. 点击Main.storyboard你会看到下面的视图:

你可以忽略最开始的controller, 它是APP的根导航控制器. 依次点击带有编号的控制器, 你将看到如下内容:

  • 第一个控制器是StoryListController, 它是数据库中所有故事的列表.
  • 点击列表中的一个故事, 将来到StoryViewController, 它展示了点击故事的标题和具体内容.
  • OptionsController是包含在StoryViewController控制器中的, 展示了支持的字体, 文本对其方式, 颜色等. 若想展示它, 只需点击详情页右上角省略号的按钮.

编译并运行, 你将看到下面的初始画面:

探索下APP, 选择不同的故事, 点击省略号按钮, 滑动选择不用的字体和阅读模式, 以便理解这些用户界面是如何工作的.

一旦你理解了该APP是如何工作的, 就直接进入下一部分. 向该APP添加模糊效果吧.

使用UIVisualEffectView添加模糊效果

UIKit提供了一整套视觉效果.UIBlurEffect(它是UIVisualEffect的子类), 是和你的兴趣相关的.UIBlurEffect提供了很漂亮的视觉效果, 就像你在navigation bars,Notification Center,Control Center中看到的那样. 你也可以将其用到你的APP中.

添加UIBlurEffect

在这个工程中, 你将使用模糊效果使OptionsController在故事的顶部显得更加突出.让我们投入其中吧!

打开OptionsController.swift, 添加下面的代码到viewDidLoad:方法的结尾处:

// 1
view.backgroundColor = .clear
// 2
let blurEffect = UIBlurEffect(style: .light)
// 3
let blurView = UIVisualEffectView(effect: blurEffect)
// 4
blurView.translatesAutoresizingMaskIntoConstraints = false
view.insertSubview(blurView, at: 0)

依次解释下:

  1. 为了能够让UIVisualEffectView视图内容模糊起来, 它的父视图必须是透明的. 为了做到这点, 你将self.view的背景改成了clear
  2. 使用UIBlurEffectStyle.light创建了一个UIBlurEffect. 这定义了模糊的风格. 其他支持的风格还有.extraLight,.dark, .extraDark, regular, prominent(实际上我并没有找到extraDark).
  3. 使用上一步创建的UIBlurEffect来创建UIVisualEffectView. 它是 UIView的子类.它唯一的目的是描绘复杂模糊的外形和显示它.
  4. 关闭了blurViewauto-resizing而使用constraints, 稍后你将手动添加constraints到它上面. 添加它到视图栈的底端. 若你在顶端添加blurView, 这将使下面的所有元素变得模糊.

现在, 你要确定blurView被安放在合适的位置.添加下面的代码到viewDidLoad:方法的结尾处:

NSLayoutConstraint.activate([
  blurView.heightAnchor.constraint(equalTo: view.heightAnchor),
  blurView.widthAnchor.constraint(equalTo: view.widthAnchor),
  ])

这些约束使blurViewframeOptionsController的view是一样的.

编译并运行, 选择一个故事, 点击省略号的按钮, 滚动文本, 你会发现模糊效果在实时的更新.

现在, 你在你的APP中实现了一个动态的模糊效果, 它不仅实现简单, 而且看上去很漂亮.

使你的模糊效果更有活力

模糊效果很好, 但Apple使用UIVibrancyEffect将其带入了下一个等级, 使用它可以使内容色彩更生动.

如下图:

注意: UIVibrancyEffect必须添加到UIVisualEffectViewcontentView中(它已经被正确放置, 使用UIBlurEffect配置过的)!否则不会向任何模糊的效果上添加"生动效果".

打开OptionsController.swift, 添加下面的代码到viewDidLoad:方法的结尾处:

// 1
let vibrancyEffect = UIVibrancyEffect(blurEffect: blurEffect)
// 2
let vibrancyView = UIVisualEffectView(effect: vibrancyEffect)
vibrancyView.translatesAutoresizingMaskIntoConstraints = false
// 3
vibrancyView.contentView.addSubview(optionsView)
// 4
blurView.contentView.addSubview(vibrancyView)

依次解释下:

  1. 使用之前配置好的blurEffect创建一个UIVibrancyEffect. UIVibrancyEffectUIVisualEffect的另一个子类.
  2. 创建一个UIVisualEffectView用来包含UIVibrancyEffect效果. 这个步骤和之前创建模糊效果是一样的. 一旦你使用了Auto Layout, 确定将auto-resizing关闭.
  3. optionsView作为vibrancyView中contentView的子视图. 这将确保生动效果应用到其包含的每一个元素中.
  4. 最后, 将vibrancyView添加到blurView的contentView中, 完成最终的效果.

接下来, 你要为vibrancyView设置自动布局的约束, 以便和blurView的尺寸一样, 以及确保optionsView在vibrancyView的中心位置.

添加下面的代码到viewDidLoad:方法的结尾处:

NSLayoutConstraint.activate([
  vibrancyView.heightAnchor.constraint(equalTo: blurView.contentView.heightAnchor),
  vibrancyView.widthAnchor.constraint(equalTo: blurView.contentView.widthAnchor),
  vibrancyView.centerXAnchor.constraint(equalTo: blurView.contentView.centerXAnchor),
  vibrancyView.centerYAnchor.constraint(equalTo: blurView.contentView.centerYAnchor)
  ])

NSLayoutConstraint.activate([
  optionsView.centerXAnchor.constraint(equalTo: vibrancyView.contentView.centerXAnchor),
  optionsView.centerYAnchor.constraint(equalTo: vibrancyView.contentView.centerYAnchor),
  ])

你还有一个事情需要注意, 看下viewDidLoad:的开始部分, 你以及将optionsView作为self.view的子视图了, 而一个视图只能有一个父视图.

viewDidLoad:的开始部分注释掉下面的代码:

view.addSubview(optionsView)
NSLayoutConstraint.activate([
  view.centerXAnchor.constraint(equalTo: optionsView.centerXAnchor),
  view.centerYAnchor.constraint(equalTo: optionsView.centerYAnchor)
  ])

编译并运行, 你将看到新的效果已经生效:

除非有较大差异的版本, 否则这个效果会各个元素难以辨认. 这发生了什么?

告诉你吧! 在blurView下一层的视图是偏亮的, 而又使用了UIBlurEffectStyle.light的效果. 这将适得其反, 就像上面的效果.

修改初始化blurEffect的代码如下:

let blurEffect = UIBlurEffect(style: .dark)

这个改变使背景和文字又来较大的差异.编译运行, 你将看到下面的效果:

更进一步

在使用模糊效果时, 还有最后一点需要注意: 如果用户将模糊效果禁用了, 将会发生什么?

在模拟器或你的设备中, 打开设置, 进入General\Accessibility\Increase Contrast, 打开Reduce Transparency.(真机中应该是 通用\辅助功能\增强对比度\降低透明度 开关).返回到这个APP中, 再次打开optionsView, 你将看到下面的结果:

可以看到它没有正常工作. 这种情况下, 你最好回到开始的地方.

幸运的是, 你可以使用UIAccessibilityIsReduceTransparencyEnabled()方法判断辅助功能是否打开.你可以像下面这样修改:

guard UIAccessibilityIsReduceTransparencyEnabled() == false else {
  view.addSubview(optionsView)
      
  NSLayoutConstraint.activate([
    view.centerXAnchor.constraint(equalTo: optionsView.centerXAnchor),
    view.centerYAnchor.constraint(equalTo: optionsView.centerYAnchor)
    ])
      
  return
}

OK, That's all!

多说一句, 你可以进入UIAccessibility类中, 看看可以检测哪些开关的状态的.

结语

整个过程中, 最大的感触是国内外的文风相差不是一丁半点啊. 国外的偏向故事风格有木有. 收获还是不少的, 感谢大家的陪伴, 让我们一起进步!

  • 部分图片来源于网络,如有侵权,请告知。
  • 如有错误,还请指出。共勉!
  • 您的喜欢是最大的赞赏。