Swift UIAlertController - ActionSheet在iPad iOS8上崩溃

63

目前我遇到了ActionSheet的大麻烦。在iPhone上它运行良好,但在iPad上只会崩溃。

我创建了一个只有一个按钮的新项目。

import UIKit

extension ViewController : UIActionSheetDelegate {

    func actionSheet(actionSheet: UIActionSheet, didDismissWithButtonIndex buttonIndex: Int) {

        if actionSheet.tag == 0 {
            if buttonIndex == 1 {
                // doing something for "product page"
            } else if (buttonIndex == 2) {
                // doing something for "video"
            }
        }
    }

}

class ViewController: UIViewController, UIActionSheetDelegate {
    @IBAction func test(sender: AnyObject) {

        let systemVersion: NSInteger = (UIDevice.currentDevice().systemVersion as NSString).integerValue
        if systemVersion < 8 {
            // iOS7:
            let action:UIActionSheet = UIActionSheet(title: "Change Map Type", delegate: self, cancelButtonTitle: "Back", destructiveButtonTitle: nil, otherButtonTitles: "Product Page", "Video")
            action.tag = 0
            action.showInView(self.view)
        } else {
            // iOS8:
            let alertController: UIAlertController = UIAlertController(title: "Change Map Type", message: nil, preferredStyle: UIAlertControllerStyle.ActionSheet)
            let cancelAction: UIAlertAction = UIAlertAction(title: "Back", style: UIAlertActionStyle.Cancel, handler: nil)
            let button1action: UIAlertAction = UIAlertAction(title: "Product Page", style: UIAlertActionStyle.Default, handler: { (action: UIAlertAction!) -> () in
                // doing something for "product page"
            })
            let button2action: UIAlertAction = UIAlertAction(title: "Video", style: UIAlertActionStyle.Default, handler: { (action: UIAlertAction!) -> () in
                // doing something for "video"
            })
            alertController.addAction(cancelAction)
            alertController.addAction(button1action)
            alertController.addAction(button2action)

            self.presentViewController(alertController, animated: true, completion: nil)
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

}
正如我之前所说,iPhone上它可以工作,但是如果我在iPad上点击按钮,应用程序将崩溃并出现以下错误信息:

* Terminating app due to uncaught exception 'NSGenericException', reason: 'Your application has presented a UIAlertController () of style UIAlertControllerStyleActionSheet. The modalPresentationStyle of a UIAlertController with this style is UIModalPresentationPopover. You must provide location information for this popover through the alert controller's popoverPresentationController. You must provide either a sourceView and sourceRect or a barButtonItem. If this information is not known when you present the alert controller, you may provide it in the UIPopoverPresentationControllerDelegate method -prepareForPopoverPresentation.' * 项目可以在https://www.dropbox.com/s/54jqd8nsc67ll5g/test.zip?dl=0 下载和尝试。

那篇帖子和我的答案非常相似...除了异常情况。 http://stackoverflow.com/questions/26037657/swift-uiactionsheet-crashes-on-ipad - holex
是的,但它仍然会崩溃。 - Fabian Boulegue
我很快会更新我的回答... - holex
好的,谢谢 :) 只有最佳答案在这里,其他主题都将被删除,因为它们充满了随机的东西。 - Fabian Boulegue
这是我的更新答案中的解决方案:http://stackoverflow.com/a/26038730/1214122 - holex
8个回答

173
错误信息告诉您需要给警报控制器的popoverPresentationController指定一个位置,以使其正确定位。这很容易做到--只需检查是否有弹出控制器并将发送者添加为源。

如果您的按钮是UIBarButtonItem

if let popoverController = alertController.popoverPresentationController {
    popoverController.barButtonItem = sender
}
self.presentViewController(alertController, animated: true, completion: nil)
否则:
if let popoverController = alertController.popoverPresentationController {
    popoverController.sourceView = sender
    popoverController.sourceRect = sender.bounds
}
self.presentViewController(alertController, animated: true, completion: nil)

6
在Swift 1.2中,现在要求执行以下操作: popoverController.sourceView = sender as! UIView - Xerion
1
popoverController.sourceRect = sender.bounds makes ActionSheet to appear on top left corner of the iPad. Instead you can use popoverController.sourceRect = sender.frame - Ashok R
sender.bounds 对我来说没问题 - sourceRect 是“在指定视图中用于锚定弹出窗口的矩形”。我理解这意味着它在源视图的坐标空间中,因此是 .bounds - leafcutter

11

尝试一下这个

alertController.popoverPresentationController?.sourceView = self.view

6

如果您想在iPad上居中显示并且没有箭头[Swift 3+]:

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

3
Nate Cook说得完全正确,但我会检测它是iPad还是iPhone。
这是针对barButtonItem的:
if ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.pad ){

            if let currentPopoverpresentioncontroller = alertController.popoverPresentationController{
                currentPopoverpresentioncontroller.barButtonItem = sender as! UIBarButtonItem
                currentPopoverpresentioncontroller.permittedArrowDirections = UIPopoverArrowDirection.down;
                self.present(alertController, animated: true, completion: nil)
            }
        }else{
            self.present(alertController, animated: true, completion: nil)
        }

类似这样的东西解决了我的问题。我进行了 .barButtonItem = sender 的赋值,但是我使用了 .sourceRect = sender.accessibilityFrame 而不是 permittedArrowDirections - Dale

2
var actionSheet = UIAlertController(title: "Please Select Camera or Photo Library", message: "", preferredStyle: .actionSheet)

if ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.pad ){
    actionSheet = UIAlertController(title: "Please Select Camera or Photo Library", message: "", preferredStyle: .alert)
}

actionSheet.addAction(UIAlertAction(title: "Upload a Photo", style: .default, handler: { (UIAlertAction) in
    self.openPhotoLibrary()
}))
actionSheet.addAction(UIAlertAction(title: "Take a Photo", style: .default, handler: { (UIAlertAction) in
    self.openCamera()
}))
actionSheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
self.present(actionSheet, animated: true, completion: nil)

2
如果您想在中心显示警报而没有箭头,我尝试了这个,在iOS 11.2上可以工作。
Swift 4.2版本:最初的回答。
let actionSheet = UIAlertController ......
if ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.pad ){

        if let currentPopoverpresentioncontroller = actionSheet.popoverPresentationController{
            currentPopoverpresentioncontroller.permittedArrowDirections = []
            currentPopoverpresentioncontroller.sourceRect = CGRect(x: (self.view.bounds.midX), y: (self.view.bounds.midY), width: 0, height: 0)
            currentPopoverpresentioncontroller.sourceView = self.view
            self.present(actionSheet, animated: true, completion: nil)
        }
    }else{
        self.present(actionSheet, animated: true, completion: nil)
    }

Objective C版本:

最初的回答
UIAlertController* actionSheet 
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
    NSArray *empty;
    UIPopoverPresentationController *currentPopOverPresentationController = [actionSheet popoverPresentationController];
    currentPopOverPresentationController.permittedArrowDirections = empty;
    currentPopOverPresentationController.sourceRect = CGRectMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds), 0, 0);
    currentPopOverPresentationController.sourceView = self.view;

    [self presentViewController:actionSheet animated:YES completion:nil];
}else{
    [self presentViewController:actionSheet animated:YES completion:nil];
}

1
如果有人使用 sender: UITapGestureRecognizer,这可能会有所帮助。
@objc func popupSettings(sender : UITapGestureRecognizer) {
    .....
    if let popoverPresentationController = alert.popoverPresentationController {
        popoverPresentationController.sourceView = self.view
        popoverPresentationController.sourceRect = CGRect(origin: sender.location(in: self.view), size: CGSize(width: 1.0, height: 1.0))
    }
    self.present(alert, animated: true, completion: nil)
}

0
跟进 Nate Cook 的回答。如果你的按钮是一个 UIBarButtonItem,那么可能需要对 sender 进行强制类型转换。
if let popoverController = alertController.popoverPresentationController {
    popoverController.barButtonItem = sender as! UIBarButtonItem
}
self.presentViewController(alertController, animated: true, completion: nil)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接