RxModal
RxModal 强调了这样一个简单观念:模态流程可以被视为一种简单的异步事件
- 视图控制器在订阅时显示
- 用户在他们想要的模态视图中做他想做的事情
- 视图控制器在取消订阅时消失,并最终发出一个值或错误
用法
这里有一个示例 | 操作过程 |
---|---|
let mailComposer = RxModal.mailComposer {
$0.setToRecipients([
"[email protected]"
])
$0.setMessageBody(
"Hello World!",
isHTML: false
)
}
let messageComposer = RxModal.messageComposer {
$0.recipients = ["0639981337"]
$0.body = "Hello World!"
}
contactUsButton
.rx.tap
.flatMapFirst { [unowned contactUsButton] in
RxModal.actionSheet(
source: .bounds(contactUsButton),
actions: [
.default(
title: "Mail",
flatMapTo: mailComposer
),
.default(
title: "Message",
flatMapTo: messageComposer
),
.cancel(title: "Cancel")
])
}
.subscribe()
.disposed(by: disposeBag)
|
支持的模态
// MFMailComposeViewController
RxModal.mailComposer() -> Single<MFMailComposeResult>
// MFMessageComposeViewController
RxModal.messageComposer() -> Single<MessageComposeResult>
// MPMediaPickerController
RxModal.mediaPicker() -> Single<MPMediaItemCollection>
// PHPickerViewController
RxModal.photoPicker() -> Single<[PHPickerResult]>
// ASWebAuthenticationSession
RxModal.webAuthenticationSession(url:callbackURLScheme:) -> Single<URL>
// UIAlertController
RxModal.alert<T>(title:message:textFields:actions:) -> Observable<T>
RxModal.actionSheet<T>(source:title:message:actions:) -> Observable<T>
展示器
所有这些功能还包括一个 presenter: Presenter
参数,允许您选择模态将在何处显示。展示器只是懒加载的 UIViewController
获取器
.viewController(_:) -> $0
.view(_:) -> $0.window?.rootViewController
.window(_:) -> $0.rootViewController
.scene(_:) -> $0.windows.first?.rootViewController
.keyWindow -> UIApplication.shared.keyWindow?.rootViewController
默认为 .keyWindow
。在 iPad 或 macCatalyst 上允许多个窗口时,我们不建议您使用 .keyWindow
或 .scene(_:)
,因为它可能会选择错误的窗口。
配置
这些函数还包括一个配置闭包: (ViewController) -> Void
,它将允许您在展示之前配置视图控制器。
如果模态需要在 init
时刻一些参数,它们将包含在 RxModal
函数中(例如: ASWebAuthenticationSession
、UIAlertController
)。
前置条件
一些 RxModals 在展示模态之前会执行前置条件检查,如果不满足则发出 RxModalError.unsupported
。
RxModal.mailComposer()
→MFMailComposeViewController.canSendMail()
RxModal.messageComposer()
→MFMessageComposeViewController.canSendText()
一些 RxModals 在展示模态之前会执行授权状态检查,要么请求授权,要么在授权被拒绝时发出 RxModalError.authorizationStatusDenied(Any)
。
RxModal.mediaPicker()
→MPMediaLibrary.authorizationStatus()
对话框
RxModal.alert()
和 RxModal.actionSheet()
允许您定义要将操作转换为新的 Observable 流、值或错误的操作。
DialogAction<T>.default(title:flatMapTo: Observable<T>)
DialogAction<T>.default(title:mapTo: T) // == flatMapTo: Observable.just(T)
DialogAction<T>.default(title:throw: Error) // == flatMapTo: Observable.error(Error)
DialogAction<T>.default(title:) // == flatMapTo: Observable.empty()
RxModal.alert()
还允许您配置警告文本字段。
RxModal.alert(
title: "Sign in",
message: "Please sign in using your credentials",
textFields: [
DialogTextField.email { $0.placeholder = "e-mail" },
DialogTextField.password { $0.placeholder = "password" }
],
actions: [
.cancel(title: "Cancel"),
.default(title: "Sign In") { textFields in
Credentials(
email: textFields[0].text ?? "",
password: textFields[1].text ?? ""
)
},
]
)
扩展 RxModal
您可以轻松地使用自己的控制器/模态流程扩展 RxModal。
如果您的控制器已经使用 Rx 返回其输出,这将很容易。
class MyModalViewController: UIViewController {
let myResult = PublishSubject<MyResult>()
// ...
}
extension RxModal {
func myModal(
presenter: Presenter = .keyWindow,
configuration: @escaping (MyModalViewController) -> Void
) -> Single<MyResult> {
RxModalCoordinator<MyModalViewController>.present(using: presenter) { _ in
let modal = MyModalViewController()
configuration(modal)
return modal
} sequence: {
$0.viewController.myResult.asSingle()
}
}
}
如果您的控制器更使用传统的 delegate
方法,您需要子类化 RxModalCoordinator
。
protocol MyModalViewControllerDelegate: AnyObject {
func myModal(_ myModal: MyModalViewController, didFinishWith result: MyResult)
func myModal(_ myModal: MyModalViewController, didFinishWithError error: Error)
}
class MyModalViewController: UIViewController {
weak var delegate: MyModalViewControllerDelegate?
// ...
}
extension RxModal {
func myModal(
presenter: Presenter = .keyWindow,
configuration: @escaping (MyModalViewController) -> Void
) -> Single<MyResult> {
MyModalViewControllerCoordinator.present(using: presenter) { coordinator in
let modal = MyModalViewController()
modal.delegate = coordinator
configuration(modal)
return modal
} sequence: {
$0.myResult.asSingle()
}
}
}
final class MyModalViewControllerCoordinator: RxModalCoordinator<MyModalViewController>, MyModalViewControllerDelegate {
required init(){}
let myResult = PublishSubject<MyResult>()
func myModal(_ myModal: MyModalViewController, didFinishWith result: MyResult) {
myResult.onNext(result)
myResult.onCompleted()
}
func myModal(_ myModal: MyModalViewController, didFinishWithError error: Error) {
myResult.onError(error)
}
}
如果您的控制器嵌入到非 UIViewController
对象中,您将无法利用 RxModalCoordinator
,并将需要处理所有展示/消失的样板代码。请参阅 ASWebAuthenticationSession.swift 作为示例。
作者
许可证
RxModal 在MIT许可证下可用。有关更多信息,请参阅LICENSE文件。