英文:
Swift UIAlert doesn't waiting user response
问题
func permissionInit() {
Task {
addListViewModel?.cameraPermission.accept(await chkCameraPermission())
addListViewModel?.photoLibraryPermission.accept(await chkPhotoLibraryPermission())
addListViewModel?.motionPermission.accept(await chkMotionPermission())
}
}
private func chkCameraPermission() async -> Bool {
let mediaType = AVMediaType.video
await AVCaptureDevice.requestAccess(for: mediaType)
let mediaAuthoriztionStatus = AVCaptureDevice.authorizationStatus(for: mediaType)
switch mediaAuthoriztionStatus {
case .authorized:
print("ShopUp Camera Permission True")
return true
case .denied, .restricted, .notDetermined:
print("ShopUp Camera Permission False")
warningAlert(title: "권한 오류", infoMativeMsg: "")
return false
default:
return false
}
}
private func chkPhotoLibraryPermission() async -> Bool {
let status = await PHPhotoLibrary.requestAuthorization(for: .readWrite)
switch status {
case .authorized:
print("ShopUp Photo Permission True")
return true
case .denied, .restricted, .notDetermined:
print("ShopUp Photo Permission False")
warningAlert(title: "권한 오류", infoMativeMsg: "")
return false
default:
return false
}
}
private func chkMotionPermission() async -> Bool {
let coreMotionGranted = CMPedometer.authorizationStatus()
switch coreMotionGranted {
case .authorized:
print("ShopUp CoreMotion Permission True")
return true
case .notDetermined, .restricted, .denied:
print("ShopUp CoreMotion Permission False")
warningAlert(title: "권한 오류", infoMativeMsg: "")
return false
default:
return false
}
}
func warningAlert(title: String, infoMativeMsg: String, completionHandler: (() -> Void)? = nil) {
let alert = UIAlertController(title: title, message: infoMativeMsg, preferredStyle: .alert)
if let completionHandler = completionHandler {
let okAction = UIAlertAction(title: "확인", style: .default) { _ in
completionHandler()
}
alert.addAction(okAction)
} else {
let okAction = UIAlertAction(title: "확인", style: .default)
alert.addAction(okAction)
}
if presentedViewController == nil {
present(alert, animated: true, completion: nil)
} else {
presentedViewController?.dismiss(animated: false) {
self.present(alert, animated: true, completion: nil)
}
}
}
请注意,我对 warningAlert
函数进行了一些更改,以确保在有其他弹出窗口时,先将其关闭再显示新的 UIAlertController
。这应该有助于按照您希望的顺序显示 UIAlert
。
英文:
func permissionInit() {
Task{
addListViewModel?.cameraPermission.accept(await chkCameraPermission())
addListViewModel?.photoLibraryPermission.accept(await chkPhotoLibraryPermission())
addListViewModel?.motionPermission.accept(await chkMotionPermission())
}
}
private func chkCameraPermission() async -> Bool{
let mediaType = AVMediaType.video
await AVCaptureDevice.requestAccess(for: mediaType)
let mediaAuthoriztionStatus = AVCaptureDevice.authorizationStatus(for: mediaType)
switch mediaAuthoriztionStatus{
case .authorized:
print("ShopUp Camera Permission True")
return true
case .denied, .restricted, .notDetermined:
print("ShopUp Camera Permission False")
warningAlert(title: "권한 오류", infoMativeMsg: "")
return false
default:
return false
}
}
private func chkPhotoLibraryPermission() async -> Bool {
let status = await PHPhotoLibrary.requestAuthorization(for: .readWrite)
switch status {
case .authorized:
print("ShopUp Photo Permission True")
return true
case .denied, .restricted, .notDetermined:
print("ShopUp Photo Permission False")
warningAlert(title: "권한 오류", infoMativeMsg: "")
return false
default:
return false
}
}
private func chkMotionPermission() async -> Bool{
let coreMotionGranted = CMPedometer.authorizationStatus()
switch coreMotionGranted {
case .authorized:
print("ShopUp CoreMotion Permission True")
return true
case .notDetermined, .restricted, .denied :
print("ShopUp CoreMotion Permission False")
warningAlert(title: "권한 오류", infoMativeMsg: "")
return false
default:
return false
}
}
func warningAlert(title: String, infoMativeMsg: String, completionHandler: Void? = nil) {
let alert = UIAlertController(title: title, message: infoMativeMsg, preferredStyle: .alert)
if completionHandler != nil {
let okAction = UIAlertAction(title: "확인", style: .default, handler: {_ in completionHandler})
alert.addAction(okAction)
}else {
let okAction = UIAlertAction(title: "확인", style: .default)
alert.addAction(okAction)
}
self.present(alert, animated: true, completion: completionHandler != nil ? {completionHandler!} : nil)
}
I added UIAlert in ViewController but it doesn't wait user response and showing error.
I also tried await on self.present but not working too.
permissionInit has an await but it doesn't seem to work.
2023-01-09 14:45:37.015435+0900 ShopUp[544:94537] [Presentation] Attempt to present <UIAlertController: 0x12c03e000> on <UINavigationController: 0x12d019c00> (from <ShopUp.AddListViewController: 0x12ce08350>) while a presentation is in progress.
2023-01-09 14:45:37.015644+0900 ShopUp[544:94537] [Presentation] Attempt to present <UIAlertController: 0x12d07b800> on <UINavigationController: 0x12d019c00> (from <ShopUp.AddListViewController: 0x12ce08350>) while a presentation is in progress.
I would like to show UIAlert in oder.
I would be grateful if you could let me know which part is wrong.
答案1
得分: 1
以下是翻译好的部分:
你正在调用三个函数,它们都试图同时(或几乎同时)显示警报。只有第一个成功,因为视图控制器只能呈现一个其他视图控制器。其他两个失败,这就是为什么你会收到两个错误消息的原因。
以下是一种将这三个请求连接起来的方法,以便每个请求都会等待前一个请求完成:
关键在于使用concat
运算符,它只会一次订阅一个Observable。它会等待一个Observable停止后再订阅下一个Observable。
在这篇文章中了解更多信息:RxSwift中组合Observables的方法
最后,我也同意HangarRash的观点。
英文:
You are calling three functions, all of which try to present an alert at the same time (or nearly so.) Only the first one succeeds because a view controller can only present a single other view controller. The other two fail which is why you get the two error messages.
Here is one way to concatenate the three requests so that each one will wait until the previous ones are complete:
extension UIViewController {
func permissionInit() {
let avCapture = Observable.createAsync { await AVCaptureDevice.requestAccess(for: .video) }
.filter { !$0 }
.observe(on: MainScheduler.instance)
.flatMap { [weak self] _ in self?.warningAlert(title: "권한 오류", infoMativeMsg: "") ?? Observable.empty() }
let phPhoto = Observable.createAsync { await PHPhotoLibrary.requestAuthorization(for: .readWrite) }
.filter { $0 != .authorized }
.observe(on: MainScheduler.instance)
.flatMap { [weak self] _ in self?.warningAlert(title: "권한 오류", infoMativeMsg: "") ?? Observable.empty() }
let cmPedo = Observable.just(CMPedometer.authorizationStatus())
.filter { $0 != .authorized }
.flatMap { [weak self] _ in self?.warningAlert(title: "권한 오류", infoMativeMsg: "") ?? Observable.empty() }
_ = Observable.concat(avCapture, phPhoto, cmPedo)
.subscribe(onNext: {
print("all requests complete.")
})
}
func warningAlert(title: String, infoMativeMsg: String) -> Observable<Void> {
Observable.deferred {
let result = PublishSubject<Void>()
let alert = UIAlertController(title: title, message: infoMativeMsg, preferredStyle: .alert)
let okAction = UIAlertAction(title: "확인", style: .default, handler: { _ in result.onSuccess(()) })
alert.addAction(okAction)
self.present(alert, animated: true, completion: nil)
return result
}
}
}
extension Observable {
static func createAsync(_ asyncFunc: @escaping () async throws -> Element) -> Observable<Element> {
Observable.create { observer in
let task = Task {
do {
observer.onSuccess(try await asyncFunc())
} catch {
observer.onError(error)
}
}
return Disposables.create { task.cancel() }
}
}
}
public extension ObserverType {
func onSuccess(_ element: Element) -> Void {
onNext(element)
onCompleted()
}
}
The key is in using the concat
operator which will only subscribe to one Observable at a time. It waits until an Observable stops before subscribing to the next one.
Learn more in this article: Recipes for Combining Observables in RxSwift
Lastly, I also agree with HangarRash.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论