RxModal 1.0.2

RxModal 1.0.2

Jérôme Alves 维护。



 
依赖
RxSwift~> 6.0
RxCocoa~> 6.0
 

RxModal 1.0.2

RxModal Icons

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 函数中(例如: ASWebAuthenticationSessionUIAlertController)。

前置条件

一些 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 作为示例。

作者

Jérôme Alves

许可证

RxModal 在MIT许可证下可用。有关更多信息,请参阅LICENSE文件。