ChatGPT解决这个技术问题 Extra ChatGPT

使用 iOS 8 在 iPad 上正确呈现 UIAlertController

在 iOS 8.0 中,Apple 引入了 UIAlertController 来替换 UIActionSheet。不幸的是,Apple 没有添加任何关于如何呈现它的信息。我在 hayaGeek 的博客上找到了关于它的 entry,但是,它似乎不适用于 iPad。观点完全错位了:

https://i.stack.imgur.com/KhAaj.png

https://i.stack.imgur.com/lFd3S.png

我使用以下代码在界面上显示它:

    let alert = UIAlertController()
    // setting buttons
    self.presentModalViewController(alert, animated: true)

有没有其他方法可以为 iPad 添加它?还是 Apple 只是忘记了 iPad,或者还没有实施?


M
Marcus Adams

您可以使用 UIPopoverPresentationController 从弹出窗口中显示 UIAlertController

在 Obj-C 中:

UIViewController *self; // code assumes you're in a view controller
UIButton *button; // the button you want to show the popup sheet from

UIAlertController *alertController;
UIAlertAction *destroyAction;
UIAlertAction *otherAction;

alertController = [UIAlertController alertControllerWithTitle:nil
                                                      message:nil
                           preferredStyle:UIAlertControllerStyleActionSheet];
destroyAction = [UIAlertAction actionWithTitle:@"Remove All Data"
                                         style:UIAlertActionStyleDestructive
                                       handler:^(UIAlertAction *action) {
                                           // do destructive stuff here
                                       }];
otherAction = [UIAlertAction actionWithTitle:@"Blah"
                                       style:UIAlertActionStyleDefault
                                     handler:^(UIAlertAction *action) {
                                         // do something here
                                     }];
// note: you can control the order buttons are shown, unlike UIActionSheet
[alertController addAction:destroyAction];
[alertController addAction:otherAction];
[alertController setModalPresentationStyle:UIModalPresentationPopover];

UIPopoverPresentationController *popPresenter = [alertController 
                                              popoverPresentationController];
popPresenter.sourceView = button;
popPresenter.sourceRect = button.bounds;
[self presentViewController:alertController animated:YES completion:nil];

为 Swift 4.2 进行编辑,虽然有很多博客可用于相同的内容,但它可以节省您搜索它们的时间。

if let popoverController = yourAlert.popoverPresentationController {
    popoverController.sourceView = self.view //to set the source of your alert
    popoverController.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0) // you can set this as per your requirement.
    popoverController.permittedArrowDirections = [] //to hide the arrow of any particular direction
}

使用 [alertController.view setTintColor:[UIColor blackColor]];如果你没有看到文字。 UIAlertController 默认使用窗口的 tint 颜色,在本例中可能是白色且不可见。
iPad 中不显示取消按钮
@BhavinRamani 取消按钮会自动从弹出窗口中删除,因为在弹出窗口外部点击代表“取消”,在弹出窗口上下文中。
这太棒了,我的问题解决了!太感谢了!
S
Sabareesh

在 iPad 上,警报将使用新的 UIPopoverPresentationController 显示为弹出框,它要求您使用 sourceView 和 sourceRect 或 barButtonItem 为弹出框的呈现指定一个锚点

barButtonItem

源视图

源矩形

为了指定锚点,您需要获取对 UIAlertController 的 UIPopoverPresentationController 的引用并设置以下属性之一:

alertController.popoverPresentationController.barButtonItem = button;

示例代码:

UIAlertAction *actionDelete = nil;
UIAlertAction *actionCancel = nil;

// create action sheet
UIAlertController *alertController = [UIAlertController
                                      alertControllerWithTitle:actionTitle message:nil
                                      preferredStyle:UIAlertControllerStyleActionSheet];

// Delete Button
actionDelete = [UIAlertAction
                actionWithTitle:NSLocalizedString(@"IDS_LABEL_DELETE", nil)
                style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {

                    // Delete
                    // [self deleteFileAtCurrentIndexPath];
                }];

// Cancel Button
actionCancel = [UIAlertAction
                actionWithTitle:NSLocalizedString(@"IDS_LABEL_CANCEL", nil)
                style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
                    // cancel
                    // Cancel code
                }];

// Add Cancel action
[alertController addAction:actionCancel];
[alertController addAction:actionDelete];

// show action sheet
alertController.popoverPresentationController.barButtonItem = button;
alertController.popoverPresentationController.sourceView = self.view;

[self presentViewController:alertController animated:YES
                 completion:nil];

它不是“三个”锚点属性之一;它是:“sourceView 和 sourceRect 或 barButtonItem”。
+1 为 Rolleric。 Apple 的文档说明了 sourceRect:“将此属性与 sourceView 属性结合使用来指定弹出框的锚点位置。或者,您可以使用 barButtonItem 属性指定弹出框的锚点位置。” - developer.apple.com/library/prerelease/ios/documentation/UIKit/…
天啊。它只是在没有任何日志消息的情况下崩溃了。为什么至少不提供编译时警告(对于通用应用程序)?
上面代码中,如果指定了barButtonItem,就不用指定sourceView,没有效果。
P
Pang

在 Swift 2 中,您想要执行以下操作以在 iPhone 和 iPad 上正确显示它:

func confirmAndDelete(sender: AnyObject) {
    guard let button = sender as? UIView else {
        return
    }

    let alert = UIAlertController(title: NSLocalizedString("Delete Contact?", comment: ""), message: NSLocalizedString("This action will delete all downloaded audio files.", comment: ""), preferredStyle: .ActionSheet)
    alert.modalPresentationStyle = .Popover

    let action = UIAlertAction(title: NSLocalizedString("Delete", comment: ""), style: .Destructive) { action in
        EarPlaySDK.deleteAllResources()
    }
    let cancel = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .Cancel) { action in

    }
    alert.addAction(cancel)
    alert.addAction(action)

    if let presenter = alert.popoverPresentationController {
        presenter.sourceView = button
        presenter.sourceRect = button.bounds
    }
    presentViewController(alert, animated: true, completion: nil)
}

如果您不设置演示者,您将在 -[UIPopoverPresentationController presentationTransitionWillBegin] 中在 iPad 上遇到异常,并显示以下消息:

致命异常:NSGenericException 您的应用程序出现了 UIAlertController () 样式 UIAlertControllerStyleActionSheet。具有此样式的 UIAlertController 的 modalPresentationStyle 是 UIModalPresentationPopover。您必须通过警报控制器的 popoverPresentationController 提供此弹出窗口的位置信息。您必须提供 sourceView 和 sourceRect 或 barButtonItem。如果在您呈现警报控制器时不知道此信息,您可以在 UIPopoverPresentationControllerDelegate 方法 -prepareForPopoverPresentation 中提供它。


G
Greg T

斯威夫特 5

我对 iPhone 使用“actionsheet”样式,对 iPad 使用“alert”样式。 iPad 显示在屏幕中央。无需在任何地方指定 sourceView 或锚定视图。

var alertStyle = UIAlertController.Style.actionSheet
if (UIDevice.current.userInterfaceIdiom == .pad) {
  alertStyle = UIAlertController.Style.alert
}

let alertController = UIAlertController(title: "Your title", message: nil, preferredStyle: alertStyle)

编辑:根据 ShareToD 的建议,更新了已弃用的“UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.pad”检查


在 iOS 13 中,'UI_USER_INTERFACE_IDIOM()' 在 iOS 13.0 中已弃用:直接使用 -[UIDevice userInterfaceIdiom]。您应该将其更改为 UIDevice.current.userInterfaceIdiom == .pad
这种方法的一个缺点是,当点击超出其范围时,警报不会被关闭
不错的解决方案!谢谢!
繁荣!又好又简单!谢谢!
接近完美:) 如果您尝试使用 alertController 作为类属性,则不能使用“alertStyle”作为参数,因为它尚未初始化。相反,我建议使用三元运算符来检查当前设备是否为 .pad 并决定是否应使用 .actionSheet 或 .alert 样式。
i
iAj

Swift 3.0 及更高版本的更新

    let actionSheetController: UIAlertController = UIAlertController(title: "SomeTitle", message: nil, preferredStyle: .actionSheet)

    let editAction: UIAlertAction = UIAlertAction(title: "Edit Details", style: .default) { action -> Void in

        print("Edit Details")
    }

    let deleteAction: UIAlertAction = UIAlertAction(title: "Delete Item", style: .default) { action -> Void in

        print("Delete Item")
    }

    let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .cancel) { action -> Void in }

    actionSheetController.addAction(editAction)
    actionSheetController.addAction(deleteAction)
    actionSheetController.addAction(cancelAction)

//        present(actionSheetController, animated: true, completion: nil)   // doesn't work for iPad

    actionSheetController.popoverPresentationController?.sourceView = yourSourceViewName // works for both iPhone & iPad

    present(actionSheetController, animated: true) {
        print("option menu presented")
    }

我正在使用抽屉,我尝试使用给定的解决方案但失败了。
我没有代码,因为我删除了操作表并使用了警报。但是在我的代码中只有一行不同 let actionSheet = UIAlertController(title: "",, message: "", preferredStyle: .actionSheet ) 但我记得日志,由于抽屉而崩溃,我认为抽屉拒绝打开行动单。因为它在屏幕的左角打开。问题只在 iPad 上。
J
Jonah Katz

斯威夫特 4 及以上

我创建了一个扩展

extension UIViewController {
  public func addActionSheetForiPad(actionSheet: UIAlertController) {
    if let popoverPresentationController = actionSheet.popoverPresentationController {
      popoverPresentationController.sourceView = self.view
      popoverPresentationController.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0)
      popoverPresentationController.permittedArrowDirections = []
    }
  }
}

如何使用:

let actionSheetVC = UIAlertController(title: "Title", message: nil, preferredStyle: .actionSheet)
addActionSheetForiPad(actionSheet: actionSheetVC)
present(actionSheetVC, animated: true, completion: nil)

我尝试了一下,但无法在 xcode 11.2.1 中调用 func addActionSheerForiPad
@RanaAliWaseem 你在 UIViewController 类中调用它吗?
是的。我在 UIViewController 类中调用它。但是它是继承自 UIViewController 的一个基类和基类。
S
SongBox

2018 更新

由于这个原因,我刚刚拒绝了一个应用程序,一个非常快速的解决方案就是从使用操作表更改为警报。

发挥了魅力并顺利通过了 App Store 测试人员。

可能不是适合每个人的答案,但我希望这可以帮助你们中的一些人快速摆脱困境。


在 iPad 和 iPhone 上完美运行 - 谢谢
这不是最好的解决方案。有时您想使用现代的 actionSheet 样式。
A
Ahmed

Swift 4.2 你可以使用这样的条件:

let alert = UIAlertController(title: nil, message: nil, preferredStyle: UIDevice.current.userInterfaceIdiom == .pad ? .alert : .actionSheet)

多谢。它对我有用
Z
Zoey Mertes

这是一个快速的解决方案:

NSString *text = self.contentTextView.text;
NSArray *items = @[text];

UIActivityViewController *activity = [[UIActivityViewController alloc]
                                      initWithActivityItems:items
                                      applicationActivities:nil];

activity.excludedActivityTypes = @[UIActivityTypePostToWeibo];

if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
    //activity.popoverPresentationController.sourceView = shareButtonBarItem;

    activity.popoverPresentationController.barButtonItem = shareButtonBarItem;

    [self presentViewController:activity animated:YES completion:nil];

}
[self presentViewController:activity animated:YES completion:nil];

这个问题是关于 UIAlertController,而不是 UIActivityViewController
你能更新 Swift 3 和 UIActivityViewController 的答案吗?
s
shim

对我来说,我只需要添加以下内容:

if let popoverController = alertController.popoverPresentationController {
    popoverController.barButtonItem = navigationItem.rightBarButtonItem
}

您可以省略 if 语句并使用可选链接:alertController.popoverPresentationController?.barButtonItem = navigationItem.rightBarButtonItem
c
craft

斯威夫特 5

适用于 iPad 和 iPhone。

悬停在按钮下方的操作表

对于 iPhone,操作表将正常显示,而 iPad 将显示在按钮下方。

actionSheet.popoverPresentationController?.sourceView = fooButton
actionSheet.popoverPresentationController?.sourceRect = fooButton.bounds

屏幕/视图中间的操作表

对于 iPhone,操作表将正常显示,iPad 将显示在屏幕/视图的中间。

actionSheet.popoverPresentationController?.sourceView = view
actionSheet.popoverPresentationController?.sourceRect = CGRect(x: view.bounds.midX, y: view.bounds.midY, width: 0, height: 0)
actionSheet.popoverPresentationController?.permittedArrowDirections = []

s
shim

只需在展示您的操作表之前添加以下代码:

if let popoverController = optionMenu.popoverPresentationController {
    popoverController.sourceView = self.view
    popoverController.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0)
    popoverController.permittedArrowDirections = []
}

A
Ali Aqdas

它适用于 iphone 和 ipad

func showImagePicker() {
    var alertStyle = UIAlertController.Style.actionSheet
    if (UIDevice.current.userInterfaceIdiom == .pad) {
      alertStyle = UIAlertController.Style.alert
    }
    let alert = UIAlertController(title: "", message: "Upload Attachment", preferredStyle: alertStyle)
    alert.addAction(UIAlertAction(title: "Choose from gallery", style: .default , handler:{ (UIAlertAction) in
        self.pickPhoto(sourceType: .photoLibrary)
    }))
    alert.addAction(UIAlertAction(title: "Take Photo", style: .default, handler:{ (UIAlertAction) in
        self.pickPhoto(sourceType: .camera)
    }))
    alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler:{ (UIAlertAction) in
    }))
    present(alert, animated: true, completion: nil)
}

这就是它的工作方式。神奇之处在于: var alertStyle = UIAlertController.Style.actionSheet if (UIDevice.current.userInterfaceIdiom == .pad) { alertStyle = UIAlertController.Style.alert } let alert = UIAlertController(title: "", message: "Upload附件”,preferredStyle: alertStyle)
R
Rinni

如果您的代码库同时支持 iPhone 和 iPad 设备,请考虑以下

在以下情况下经常使用 present(_ viewControllerToPresent:animated:completion:)

呈现具有 .alert 的首选样式的 UIAlertController

使用 .modalPresentationStyle 呈现 UIViewController: .overFullScreen .formSheet .automatic 如果未指定 modalPresentationStyle 的默认值 .currentContext .fullScreen .custom .overCurrentContext

.overFullScreen

.formSheet

.automatic modalPresentationStyle 未指定时的默认值

.currentContext

。全屏

。风俗

.overCurrentContext

在演示之前配置 popoverPresentationControllersourceRectsourceView

使用 .actionSheet 的首选样式呈现 UIAlertController

使用 .modalPresentationStyle 呈现 UIViewController:.popover .none 这将在 iPhone 和 iPad 上崩溃,并出现错误“指定的模态呈现样式没有相应的呈现控制器”。

.popover

.none 这将在 iPhone 和 iPad 上崩溃,并出现错误“指定的模态演示样式没有相应的演示控制器”。

呈现 UIActivityViewController(基于 https://developer.apple.com/documentation/uikit/uiactivityviewcontroller ;“在 iPad 上,您必须在弹出窗口中呈现视图控制器。在 iPhone 和 iPod touch 上,您必须以模态方式呈现它。”)

这是配置 popoverPresentationController 的示例

if let popoverController = popoverPresentationController {
  popoverController.sourceView = view
  popoverController.sourceRect = CGRect(x: view.bounds.maxX, y: 40, width: 0, height: 0)
}

如果您发现此处未列出的任何其他案例,请告诉我!