Ambience
不编程的情况下实现亮度感知的可访问性主题切换。
特别感谢
我想感谢 Meng To、Marcos Griselli 以及 Design+Code 团队对这个项目的支持。Design+Code 是了解设计和 iOS 开发的理想之处。该应用程序是 Ambience 在 App Store 的首次亮相。
您也可以在 Design+Code 上查看 Ambience 教程。
感谢大家!
示例
要运行示例项目,请克隆仓库,然后首先在 Example 目录中运行 pod install
如果发现 pod install
返回“无法找到 Ambience 的规范”,您可能需要运行 pod repo update
。
安装
Ambience 可通过 CocoaPods 获得,强烈推荐使用。要安装它,只需将以下行添加到 Podfile 即可
pod 'Ambience'
要启用它,您需要在 App Delegate 中调用 Ambience 单例,如下所示
import UIKit
import Ambience
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
_ = Ambience.shared
return true
}
}
这只是很普通的事情,对吧?
环境非常方便,因为它在幕后做了一点“黑魔法”。如果你对它是如何工作的感到好奇,请向下滚动到细节部分。
内置支持
环境内置了对以下界面构建器对象的背景颜色支持
- 视图及其所有子视图;
- 搜索、导航和标签栏及其所有子视图;
- 文本视图、按钮和标签及其所有子视图。
还支持以下颜色文本
- 文本视图、按钮和标签及其所有子视图。
并在以下项目上支持暗色和亮色栏样式
- 搜索、导航和标签栏及其所有子视图。
自定义视图
要自定义界面构建器视图,请在属性检查器上使用可检查属性。别忘了为该视图开启环境。
搜索、导航和标签需要开启,但它们各自的外观样式不可自定义。
自定义行为
在继承自 NSObject
的任何对象上,也可以定义自定义的环境行为。按照以下说明操作。
定义 Ambience 方法的覆盖实现
在这个例子中,我们正在实现搜索、导航和标签栏的当前行为。它将当前状态作为 Ambience 状态
保护通知数据并相应地设置栏样式。
public override func ambience(_ notification : Notification) {
super.ambience(notification)
guard let currentState = notification.userInfo?["currentState"] as? AmbienceState else { return }
barStyle = currentState == .invert ? .black : .default
}
通知用户信息字典还包含了上一次状态,以便实现更复杂的状态行为。它可能还包含一个动画
布尔属性,通常设置为 true,在第一次运行时设置为 false,以便在视图出现时不进行动画。
开启环境光
如果您的对象设置为 界面构建器,请使用 属性检查器,并将 环境光 值设置为 开启。
如果您是编程设置此对象,请先将其 ambience
布尔值设置为 true
,然后再放置它。
已知问题
集合内的文本视图
如果您正在使用一个在 Table View Cell 或 Collection View Cell 内的 Text View,并在解引用其过程中设置了其属性文本,请注意。在分配新的 属性文本 之后,将需要编写一条必需的单行代码,以便在 Text View 中正确执行 环境光。
请遵循示例
// Inside the respective Table View Controller
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : TextTableCell! = tableView.dequeueReusableCell(withIdentifier: "Cell with text") as! TextTableCell
cell.textView?.reinstateAmbience()
return cell
}
简单但必需。
我可以在 Text View 内部设置一些观察者,以便它在运行时自行执行,但我不会这样做,因为这可能导致循环引用和混淆。
繁琐细节
是的。您只需要一行代码即可让环境光正常工作。但有一个细节。为此,在调用 Ambience.shared 时,需要对 Awake From Nib 实施swizzle。
如果您不知道 swizzle 是什么,这里有一个简单的解释。
交换两个方法是交换两个 方法地址。我说的地址是什么?为什么我要这样做?
我希望每个 UIView 都能够访问到 Ambience。为了实现这一点,我必须首先在 UIView 放到设备屏幕上之前进行一些配置。我选择在 Awake From Nib 方法中这样做,因为它保证在视图到达屏幕之前被调用,并且大多数 UIView 对象,例如 导航栏、搜索栏 和 标签栏,它们的生命周期与普通 UIView 不同。
static let classInit : Void = {
swizzling(forClass: UIView.self, originalSelector: #selector(awakeFromNib), swizzledSelector: #selector(swizzled_awakeFromNib))
}()
@objc open func swizzled_awakeFromNib () {
let name = String(describing:type(of: self))
guard !NSObject.forbiddenNames.contains(name) else { return }
swizzled_awakeFromNib()
if ambience {
_ = notificationManager
}
}
对 "从Nib唤醒" 的地址将会进行转义操作,但这不会影响内部调用。让我来解释一下。当 UI Kit 在一个 UIView 上调用 awakeFromNib
方法时,实际发生的是 view.swizzled_awakeFromNib
。不过,当 swizzled_awakeFromNib
在其内部被调用时,实际上它会调用 awakeFromNib
,这样我们就能访问默认实现了。
换句话说,这是通过一种复杂的方式将这几行代码添加到 每个UIView及其所有子视图 的一种方法。
if ambience {
_ = notificationManager
}
最终注意事项
如果您有一个好主意或者认为对该内容的某些编辑可能适用于更广泛的受众,请随时创建一个 pull request。
我希望苹果能开放 Trait Environment API,这样我可以申请扩展 Trait Collection,并删除数百行代码。如果苹果在这里:请,打开它。
作者
许可证
Ambience 在 MIT 许可下可用。有关更多信息,请参阅 LICENSE 文件。