XestiMonitors

Swift 4.x License Platform

Build Status Code Coverage Documented

CocoaPods Carthage Swift Package Manager

概述

XestiMonitors 框架提供了超过六十个功能齐全的监视器类,开箱即用,使您的应用程序能够轻松检测和响应许多常见的系统生成的事件。

在其他方面,您可以将 XestiMonitors 视为管理最常见通知(主要在 iOS 和 tvOS 上)的更好方法。目前,XestiMonitors 提供了几乎所有UIKit通知(请参阅 UIKit 监视器)和许多Foundation通知(请参阅 Foundation 监视器)的“包装器”。

XestiMonitors 还围绕几个框架和编程接口提供了方便的“包装器”,使您的应用程序更容易使用

针对所有四个平台的更多部分的附加监视器将在 XestiMonitors 的未来版本中推出!

最后,XestiMonitors 是可扩展的——您可以轻松创建自己的自定义监视器。 有关详细信息,请参阅自定义监视器

参考文档

完整的参考文档Jazzy提供。

要求

安装

CocoaPods

CocoaPods 是 Cocoa 项目的依赖项管理器。 您可以使用以下命令安装它

$ gem install cocoapods

要使用 CocoaPods 将 XestiMonitors 集成到您的 Xcode 项目中,请在您的Podfile中指定它

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
use_frameworks!

target '<Your Target Name>' do
    pod 'XestiMonitors'
end

然后,运行以下命令

$ pod install

Carthage

Carthage 是一个分散式依赖项管理器,可以构建您的依赖项并为您提供二进制框架。

您可以使用Homebrew 使用以下命令安装 Carthage

$ brew update
$ brew install carthage

要使用 Carthage 将 XestiMonitors 集成到您的 Xcode 项目中,请在您的Cartfile中指定它

github "eBardX/XestiMonitors"

然后,运行以下命令

$ carthage update

最后,将构建的XestiMonitors.framework拖到您的 Xcode 项目中。

Swift Package Manager

Swift Package Manager 是一种用于自动化 Swift 代码分发的工具,并已集成到 swift 编译器中。 它还处于早期开发阶段,但 XestiMonitors 确实支持其在受支持平台上的使用。

设置好 Swift 包后,将 XestiMonitors 添加为依赖项就像将其添加到Package.swiftdependencies值一样简单。

dependencies: [
    .Package(url: "https://github.com/eBardX/XestiMonitors.git")
]

用法

所有监视器类都符合 Monitor 协议,因此使您可以创建可以统一启动或停止的监视器数组——代码行数更少!

例如,在视图控制器中,您可以延迟实例化几个监视器,此外,还可以延迟实例化包含这些监视器的数组变量

import XestiMonitors

lazy var keyboardMonitor = KeyboardMonitor { [unowned self] in
    // do something…
}
lazy var memoryMonitor = MemoryMonitor { [unowned self] in
    // do something…
}
lazy var orientationMonitor = OrientationMonitor { [unowned self] in
    // do something…
}
lazy var monitors: [Monitor] = [keyboardMonitor,
                                memoryMonitor,
                                orientationMonitor]

然后,在viewWillAppear(_:)viewWillDisappear(_:)方法中,您只需一行代码即可启动或停止所有这些监视器

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    monitors.forEach { $0.startMonitoring() }
}

override func viewWillDisappear(_ animated: Bool) {
    monitors.forEach { $0.stopMonitoring() }
    super.viewWillDisappear(animated)
}

易如反掌!

Core Location 监视器

XestiMonitors 提供了七个包装 Core Location 框架的监视器类,您可以使用它们来确定设备的地理位置、海拔或方向;或其相对于附近 iBeacon 的位置

Core Motion 监视器

XestiMonitors 提供了七个包装 Core Motion 框架的监视器类,您可以使用它们从设备获取原始和处理后的运动测量数据

Foundation 监视器

XestiMonitors 提供了十七个包装 Foundation 通知的监视器

UIKit 监视器

XestiMonitors 提供了许多包装 UIKit 通知的监视器。

辅助功能监视器

XestiMonitors 提供了三个监视器类,您可以使用它们来观察系统生成的辅助功能事件

应用程序监视器

XestiMonitors 提供了七个监视器类,可用于观察系统生成的关于应用程序的常见事件

设备监视器

XestiMonitors 提供了三个监视器类,可用于检测设备特征的变化

屏幕监视器

XestiMonitors 提供了四个监视器类,可用于检测与屏幕关联的属性更改

文本监视器

XestiMonitors 提供了四个监视器类,可用于检测文本输入模式和内容的变化

其他 UIKit 监视器

此外,XestiMonitors 还提供了其他九个 UIKit 监视器

KeyboardMonitor 在删除应用程序中的大量样板代码方面尤其方便。这是自定义视图控制器中键盘监视的典型处理方式

func keyboardWillHide(_ notification: Notification) {
    let userInfo = notification.userInfo
    var animationDuration: TimeInterval = 0
    if let value = (userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue {
        animationDuration = value
    }
    constraint.constant = 0
    UIView.animate(withDuration: animationDuration) {
        self.view.layoutIfNeeded()
    }
}

func keyboardWillShow(_ notification: Notification) {
    let userInfo = notification.userInfo
    var animationDuration: TimeInterval = 0
    if let value = (userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue {
        animationDuration = value
    }
    var frameEnd = CGRect.zero
    if let value = (userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        frameEnd = value
    }
    constraint.constant = frameEnd.height
    UIView.animate(withDuration: animationDuration) {
        self.view.layoutIfNeeded()
    }
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    let nc = NotificationCenter.`default`
    nc.addObserver(self, selector: #selector(keyboardWillHide(_:)),
                   name: .UIKeyboardWillHide, object: nil)
    nc.addObserver(self, selector: #selector(keyboardWillShow(_:)),
                   name: .UIKeyboardWillShow, object: nil)
}

override func viewWillDisappear(_ animated: Bool) {
    NotificationCenter.`default`.removeObserver(self)
    super.viewWillDisappear(animated)
}

这是使用 KeyboardMonitor 的 XestiMonitors 方式

import XestiMonitors

lazy var keyboardMonitor = KeyboardMonitor { [unowned self] event in
    guard let constraint = self?.constraint,
          let view = self?.view else { return }
    switch event {
    case let .willHide(info):
        constraint.constant = 0
        UIView.animate(withDuration: info.animationDuration) {
            view.layoutIfNeeded()
        }
    case let .willShow(info):
        constraint.constant = info.frameEnd.height
        UIView.animate(withDuration: info.animationDuration) {
            view.layoutIfNeeded()
        }
    default:
        break
    }
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    keyboardMonitor.startMonitoring()
}

override func viewWillDisappear(_ animated: Bool) {
    keyboardMonitor.stopMonitoring()
    super.viewWillDisappear(animated)
}

你的钱包里有什么?

其他监视器

此外,XestiMonitors 还提供了另外两个监视器

自定义监视器

最重要的是,XestiMonitors 框架提供了多种方法来轻松创建自己的自定义监视器。

实现监视器协议

您可以创建一个新类,或扩展一个现有类,使其符合 Monitor 协议。您只需实现 startMonitoring()stopMonitoring() 方法,以及 isMonitoring 属性

import XestiMonitors

extension MegaHoobieWatcher: Monitor {
    var isMonitoring: Bool { return watchingForHoobiesCount() > 0 }

    func startMonitoring() -> Bool {
        guard !isMonitoring else { return }
        beginWatchingForHoobies()
    }

    func stopMonitoring() -> Bool {
        guard isMonitoring else { return }
        endWatchingForHoobies()
    }
}

注意: startMonitoring()stopMonitoring() 中的 guard 语句可以防止在监视器处于不正确状态时启动或停止监视器。这被认为是良好的编码实践。

子类化 BaseMonitor 类

通常,您需要创建一个 BaseMonitor 的子类。使用此抽象基类的优点是可以为您处理基本的 guard 逻辑。具体来说,如果 startMonitoring() 方法已激活,则不会尝试启动监视器;如果 stopMonitoring() 方法 *未* 激活,则不会尝试停止监视器。您只需重写此基类的 configureMonitor()cleanupMonitor() 方法,而不是直接实现所需的协议方法和属性。实际上,您*不能*重写 startMonitoring()stopMonitoring() 方法或 isMonitoring 属性——它们在 BaseMonitor 中声明为 final

import XestiMonitors

class GigaHoobieMonitor: BaseMonitor {
    let handler: (Float) -> Void
    @objc let hoobie: GigaHoobie
    private var observation: NSKeyValueObservation?

    init(_ hoobie: GigaHoobie, handler: @escaping (Float) -> Void) {
        self.handler = handler
        self.hoobie = hoobie
    }

    override func configureMonitor() -> Bool {
        super.configureMonitor()
        observation = hoobie.observe(\.nefariousActivityLevel) { [unowned self] hoobie, _ in
            self.handler(hoobie.nefariousActivityLevel) }
    }

    override func cleanupMonitor() -> Bool {
        observation?.invalidate()
        observation = nil
        super.cleanupMonitor()
    }
}

注意: 务必调用 configureMonitor()cleanupMonitor() 的超类实现。

子类化 BaseNotificationMonitor 类

如果您的自定义监视器通过观察通知来确定事件,则应考虑创建一个 BaseNotificationMonitor 的子类。在大多数情况下,您只需重写 addNotificationObservers(_:) 方法。如果当通知观察器在停止监视器时被删除时需要额外的清理,您还可以重写 removeNotificationObservers(_:) 方法。虽然这个基类继承自 BaseMonitor,但您*不能*重写 configureMonitor()cleanupMonitor() 方法——它们在 BaseNotificationMonitor 中声明为 final

import XestiMonitors

class TeraHoobieMonitor: BaseNotificationMonitor {
    let handler: (Bool) -> Void
    let hoobie: TeraHoobie

    init(hoobie: TeraHoobie, queue: OperationQueue = .main,
         handler: @escaping (Bool) -> Void) {
        self.handler = handler
        self.hoobie = hoobie
        super.init(queue: queue)
    }

    override func addNotificationObservers() -> Bool {
        super.addNotificationObservers()
        observe(.teraHoobieDidChange) { [unowned self] _ in
            self.handler(self.hoobie.value) }
    }
}

注意: 请务必在您的重写中调用父类的 addNotificationObservers(_:)removeNotificationObservers(_:) 方法实现。

鸣谢

J. G. Pusey (ebardx@gmail.com)

许可证

XestiMonitors 基于 MIT 许可证 发布。