Presentare correttamente UIAlertController su un iPad usando iOS 8


194

Con iOS 8.0, Apple ha introdotto UIAlertController per sostituire UIActionSheet . Sfortunatamente, Apple non ha aggiunto alcuna informazione su come presentarla. Ho trovato una voce al riguardo sul blog di hayaGeek, tuttavia, non sembra funzionare su iPad. La vista è totalmente fuori luogo:

Misplaced: Immagine fuori posto

Corretta: inserisci qui la descrizione dell'immagine

Uso il seguente codice per mostrarlo sull'interfaccia:

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

C'è un altro modo per aggiungerlo per iPad? O Apple ha appena dimenticato l'iPad o non è ancora stata implementata?

Risposte:


284

È possibile presentare un UIAlertControllerda un popover usando UIPopoverPresentationController.

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

Modifica per Swift 4.2, anche se ci sono molti blog disponibili per lo stesso, ma potresti risparmiare tempo per andare a cercarli.

 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
            }

Utilizzare [alertController.view setTintColor: [UIColor blackColor]]; se non vedi il testo. UIAlertController utilizza il colore di tinta della finestra per impostazione predefinita, che in questo esempio potrebbe essere bianca e invisibile.
— whyoz,

2
Il pulsante Annulla non viene visualizzato in iPad
— Bhavin Ramani,

14
@BhavinRamani I pulsanti Annulla vengono rimossi automaticamente dai popover, poiché toccare all'esterno del popover rappresenta "annulla", in un contesto popover.
— christopherdrum,

è fantastico, il mio problema è risolto! grazie mille!
— Mahir Tayir,

109

Su iPad l'avviso verrà visualizzato come popover utilizzando il nuovo UIPopoverPresentationController , è necessario specificare un punto di ancoraggio per la presentazione del popover utilizzando sia sourceView e sourceRect o barButtonItem

  • barButtonItem
  • sourceView
  • sourceRect

Per specificare il punto di ancoraggio è necessario ottenere un riferimento a UIPopoverPresentationController di UIAlertController e impostare una delle proprietà come segue:

alertController.popoverPresentationController.barButtonItem = button;

codice di esempio:

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
Non è "una delle tre" proprietà del punto di ancoraggio; è: "o sourceView e sourceRect o barButtonItem".
— Rolleric,

2
+1 per Rolleric. Gli stati della documentazione di Apple relativi a sourceRect: "Utilizzare questa proprietà insieme alla proprietà sourceView per specificare la posizione di ancoraggio per il popover. In alternativa, è possibile specificare la posizione di ancoraggio per il popover utilizzando la proprietà barButtonItem." - developer.apple.com/library/prerelease/ios/documentation/UIKit/…
— Ben Patch

Oddio. Si è semplicemente schiantato senza alcun messaggio di registro. Perché non fornire almeno un avviso in fase di compilazione (per le app universali)?
— Mike Keskinov,

85

In Swift 2, vuoi fare qualcosa del genere per mostrarlo correttamente su iPhone e 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)
}

Se non imposti il ​​presentatore, finirai con un'eccezione su iPad -[UIPopoverPresentationController presentationTransitionWillBegin]con il seguente messaggio:

Eccezione irreversibile: NSGenericException La tua applicazione ha presentato un UIAlertController (<UIAlertController: 0x17858a00>) di stile UIAlertControllerStyleActionSheet. Il modalPresentationStyle di un UIAlertController con questo stile è UIModalPresentationPopover. È necessario fornire informazioni sulla posizione per questo popover tramite popoverPresentationController del controller di avviso. È necessario fornire sourceView e sourceRect o barButtonItem. Se queste informazioni non sono note quando si presenta il controller di avviso, è possibile fornirle nel metodo UIPopoverPresentationControllerDelegate -prepareForPopoverPresentation.


26

Aggiornamento per Swift 3.0 e versioni successive

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

Sto usando drawer, provo ad usare la soluzione indicata ma non ci sono riuscito.
— Rana Ali Waseem,

Non ho codice, perché rimuovo il foglio di azione e utilizzo l'avviso. Ma nel mio codice solo una riga era diversa, lascia che actionSheet = UIAlertController (titolo: "" ,, messaggio: "", stile preferito: .actionSheet) Ma ricordo i registri, si stava bloccando a causa del cassetto, penso che il cassetto resistesse ad aprirsi foglio d'azione. perché si apriva nell'angolo sinistro dello schermo. problema era solo su iPad.
— Rana Ali Waseem,

15

Aggiornamento 2018

Ho appena rifiutato un'app per questo motivo e una risoluzione molto rapida è stata semplicemente quella di passare dall'uso di un foglio di azione a un avviso.

Ha funzionato a meraviglia e superato i tester dell'App Store.

Potrebbe non essere una risposta adatta a tutti, ma spero che questo aiuti alcuni di voi a uscire rapidamente da un sottaceto.


1
Ha funzionato perfettamente su iPad e iPhone - Grazie
— Jeremy Andrews,

Non è la soluzione migliore. A volte vuoi usare lo stile del foglio di azione, che è moderno.
— ShadeToD

9

Swift 4 e versioni successive

Ho creato un'estensione

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 = []
    }
  }
}

Come usare:

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

Lo provo ma non riesco a chiamare func addActionSheerForiPad in xcode 11.2.1
— Rana Ali Waseem il

@RanaAliWaseem lo stai chiamando all'interno della classe UIViewController?
— ShadeToD

sì. Lo chiamo nella classe UIViewController. Ma è ereditato da una classe Base e base ereditata da UIViewController.
— Rana Ali Waseem,

8

Ecco una soluzione rapida:

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
Questa domanda riguarda UIAlertController, non UIActivityViewController
— Roman Truba

Puoi aggiornare la risposta per Swift 3 insieme a UIActivityViewController?
— Dia

8

Swift 5

Ho usato lo stile "actionsheet" per iPhone e "alert" per iPad. iPad viene visualizzato al centro dello schermo. Non è necessario specificare sourceView o ancorare la vista ovunque.

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

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

Modifica: su suggerimento di ShareToD, aggiornato "UI_USER_INTERFACE_IDIOM () == UIUserInterfaceIdiom.pad" obsoleto aggiornato


2
in iOS 13 'UI_USER_INTERFACE_IDIOM ()' è stato deprecato in iOS 13.0: Usa - [UIDevice userInterfaceIdiom] direttamente. Dovresti cambiarlo in UIDevice.current.userInterfaceIdiom == .pad
— ShadeToD

Uno svantaggio di questo approccio è che l'avviso non è
— ignorabile

2

Per me avevo solo bisogno di aggiungere quanto segue:

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

2
È possibile omettere l'istruzione if e utilizzare il concatenamento facoltativo: alertController.popoverPresentationController? .BarButtonItem = navigationItem.rightBarButtonItem
— Dale

2

Aggiungi il seguente codice prima di presentare il tuo foglio d'azione:

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 = []
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.