使用iOS 8在iPad上正确显示UIAlertController


194

在iOS 8.0中,Apple引入了UIAlertController来代替UIActionSheet。不幸的是,Apple没有添加有关如何呈现它的任何信息。我在hayaGeek的博客上找到了关于它的条目,但是,它似乎在iPad上不起作用。该视图完全放错了位置:

放错位置: 图片放错位置

正确: 在此处输入图片说明

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

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

是否有另一种方法可以将其添加到iPad?还是苹果只是忘记了iPad,或者尚未实现?

Answers:


284

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

在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默认使用窗口的色调颜色,在此示例中,该颜色可能为白色且不可见。
whyoz 2014年

2
取消按钮上不显示在iPad上
Bhavin拉马尼

14
@BhavinRamani取消按钮会自动从弹出窗口中删除,因为在弹出窗口上下文中,在弹出窗口外部点击表示“取消”。
christopherdrum

这太棒了,我的问题解决了!非常感谢!
Mahir Tayir

109

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

  • barButtonItem
  • sourceView
  • sourceRect

为了指定锚点,您将需要获取对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];

28
它不是“三个”锚点属性中的一个。它是:“ sourceView和sourceRect或barButtonItem”。
Rolleric'1

2
+1为Rolleric。Apple的文档指出了有关sourceRect的信息:“将此属性与sourceView属性一起使用以指定弹出窗口的锚点位置。或者,您可以使用barButtonItem属性为弹出窗口指定锚点位置。” - developer.apple.com/library/prerelease/ios/documentation/UIKit/...
本补丁

天啊。它只是崩溃而没有任何日志消息。为什么至少不提供编译时警告(针对通用应用程序)?
Mike Keskinov

84

在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)
}

如果未设置演示者,则会在iPad -[UIPopoverPresentationController presentationTransitionWillBegin]上出现以下消息,最终导致异常:

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


26

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")
    }

我正在使用抽屉,我尝试使用给定的解决方案,但失败了。
拉娜·阿里·瓦瑟姆

我没有代码,因为我删除了操作表并使用了警报。但在我的代码只是一条线是不同的让利actionSheet = UIAlertController(标题:“” ,,消息:“”,preferredStyle:.actionSheet) ,但我记得,那是崩溃,由于抽屉的日志,我认为抽屉抵抗开放行动表。因为它在屏幕的左上角打开。问题只发生在iPad。
拉娜·阿里·沃瑟姆

15

2018更新

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

很有魅力,并且顺利通过了App Store测试人员。

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


1
在iPad和iPhone上均可完美运行-谢谢
Jeremy Andrews

这不是最佳解决方案。有时您想使用actionSheet样式,这是现代的。
ShadeToD

9

Swift 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
Rana Ali Waseem,

你在UIViewController类中调用@RanaAliWaseem吗?
ShadeToD

是。我在UIViewController类中调用它。但是它继承自一个基类,而基类则继承自UIViewController。
Rana Ali Waseem

8

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

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];

3
这个问题是关于UIAlertController的,而不是关于UIActivityViewController的
Roman Truba

您可以将Swift 3的答案与UIActivityViewController一起更新吗?
直径

6

斯威夫特5

我为iPhone使用了“动作表”样式,为iPad使用了“警报”样式。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”检查


2
在iOS 13中不推荐使用iOS 13'UI_USER_INTERFACE_IDIOM()':直接使用-[UIDevice userInterfaceIdiom]。您应该将其更改为UIDevice.current.userInterfaceIdiom == .pad
ShadeToD

2

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

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

2
您可以省略if语句,并使用可选的链接:alertController.popoverPresentationController?.barButtonItem = navigationItem.rightBarButtonItem
Dale,

2

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

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 = []
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.