Правильно представить UIAlertController на iPad с помощью iOS 8

194

В iOS 8.0 Apple представила UIAlertController вместо UIActionSheet . К сожалению, Apple не добавила никакой информации о том, как это представить. Я нашел запись об этом в блоге hayaGeek, однако, это, кажется, не работает на iPad. Представление совершенно неуместно:

неуместный: Неверное изображение

Верный: введите описание изображения здесь

Я использую следующий код, чтобы показать его на интерфейсе:

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

Есть ли другой способ добавить его для iPad? Или Apple просто забыла iPad или еще не реализована?

Matt3o12
источник

Ответы:

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 по умолчанию использует цвет оттенка окна, который может быть белым и невидимым в этом примере.
Почему
2
Кнопка «Отмена» не отображается в iPad
Bhavin Ramani
14
@BhavinRamani Кнопки отмены удаляются из всплывающих окон автоматически, потому что нажатие за пределы поповера означает «отмена» в контексте всплывающего окна.
Христофордрум
это круто, моя проблема решена! Спасибо большое!
Махир Тайир
109

На iPad оповещение будет отображаться как всплывающее окно с использованием нового UIPopoverPresentationController , для этого необходимо указать опорную точку для представления всплывающего окна с использованием либо sourceView и sourceRect, либо barButtonItem.

  • barButtonItem
  • SourceView
  • sourceRect

Для того чтобы указать точку привязки, вам нужно получить ссылку на UIPler-Control UIAlertController и установить одно из свойств следующим образом:

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];
Sabareesh
источник
28
Это не «одно из трех» свойств точки привязки; это: «либо sourceView и sourceRect, либо barButtonItem».
Rolleric
2
+1 за роллерик. В документации Apple говорится о sourceRect: «Используйте это свойство вместе со свойством sourceView, чтобы указать местоположение привязки для всплывающего окна. В качестве альтернативы вы можете указать местоположение привязки для всплывающего окна, используя свойство barButtonItem». - developer.apple.com/library/prerelease/ios/documentation/UIKit/…
Бен Патч
О чувак. Это просто разбилось без какого-либо сообщения журнала. Почему бы по крайней мере не предоставить предупреждение во время компиляции (для универсальных приложений)?
Майк Кескинов
85

В 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 Ваше приложение представило UIAlertController (<UIAlertController: 0x17858a00>) стиля UIAlertControllerStyleActionSheet. ModalPresentationStyle UIAlertController с этим стилем - UIModalPresentationPopover. Вы должны предоставить информацию о местоположении для этого всплывающего окна через контроллер popoverPresentationController контроллера предупреждений. Вы должны предоставить либо sourceView и sourceRect, либо barButtonItem. Если эта информация неизвестна при представлении контроллера предупреждений, вы можете предоставить ее в методе UIPopoverPresentationControllerDelegate -prepareForPopoverPresentation.

kviksilver
источник
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")
    }
IAJ
источник
Я использую ящик, я пытаюсь использовать данное решение, но не получилось.
Рана Али Васим
У меня нет кода, потому что я удаляю лист действий и использую оповещение. Но в моем коде только одна строка была другой, пусть actionSheet = UIAlertController (title: "" ,, message: "", favouriteStyle: .actionSheet) Но я помню журналы, это было сбой из-за ящика, я думаю, что ящик сопротивляется открытию лист действий. потому что он открывался в левом углу экрана. выпуск был только на iPad.
Рана Али Васим
15

2018 Обновление

Я только что отклонил приложение по этой причине, и очень быстрое решение состояло в том, чтобы просто перейти от использования листа действий к предупреждению.

Сработал шарм и прошел тестеры App Store просто отлично.

Возможно, это не самый подходящий ответ для всех, но я надеюсь, что это быстро поможет некоторым из вас.

SongBox
источник
1
Отлично работал на iPad и iPhone - спасибо
Джереми Эндрюс
Это не лучшее решение. Иногда вы хотите использовать стиль 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)
Сиддеш Махадешвар
источник
Я пытаюсь это, но не могу вызвать func addActionSheerForiPad в Xcode 11.2.1
Рана Али Waseem
@RanaAliWaseem вы вызываете это внутри класса UIViewController?
ShadeToD
да. Я называю это в классе UIViewController. Но он наследуется базовым классом и базовым классом, наследуемым от UIViewController.
Рана Али Васим
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
Роман Труба
Можете ли вы обновить ответ для Swift 3 вместе с UIActivityViewController?
Dia
8

Swift 5

Я использовал стиль «Actionsheet» для iPhone и «alert» для 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 «UI_USER_INTERFACE_IDIOM ()» устарела в iOS 13.0: Использование - [UIDevice userInterfaceIdiom] напрямую. Вы должны изменить его на UIDevice.current.userInterfaceIdiom == .pad
ShadeToD
Одним из недостатков этого подхода является то, что предупреждение не может быть отклонено при щелчке за его пределами
Mliu
2

Для меня мне просто нужно было добавить следующее:

if let popoverController = alertController.popoverPresentationController {
    popoverController.barButtonItem = navigationItem.rightBarButtonItem
}
Мэтью Беккер
источник
2
Вы можете опустить оператор if и использовать необязательную цепочку: alertController.popoverPresentationController? .BarButtonItem = navigationItem.rightBarButtonItem
Дейл,
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 = []
}
Подпись Тритон
источник