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.