Présentation correcte d'un UIAlertController sur un iPad à l'aide d'iOS 8

194

Avec iOS 8.0, Apple a introduit UIAlertController pour remplacer UIActionSheet . Malheureusement, Apple n'a ajouté aucune information sur la façon de le présenter. J'ai trouvé une entrée à ce sujet sur le blog de hayaGeek, cependant, cela ne semble pas fonctionner sur iPad. La vue est totalement déplacée:

Mal placé: Image mal placée

Correct: entrez la description de l'image ici

J'utilise le code suivant pour l'afficher sur l'interface:

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

Existe-t-il une autre façon de l'ajouter pour iPad? Ou Apple a-t-il simplement oublié l'iPad, ou n'est pas encore implémenté?

Matt3o12
la source

Réponses:

284

Vous pouvez présenter un à UIAlertControllerpartir d'un popover en utilisant UIPopoverPresentationController.

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

Modification pour Swift 4.2, bien qu'il existe de nombreux blogs disponibles pour le même mais cela peut vous faire gagner du temps pour aller les chercher.

 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
            }
Peter Hajas
la source
Utilisez [alertController.view setTintColor: [UIColor blackColor]]; si vous ne voyez pas le texte. UIAlertController utilise la couleur de teinte de la fenêtre par défaut, qui peut être blanche et invisible dans cet exemple.
whyoz
2
Le bouton Annuler ne s'affiche pas sur l'iPad
Bhavin Ramani
14
@BhavinRamani Les boutons Annuler sont supprimés automatiquement des fenêtres contextuelles, car le fait de taper en dehors de la fenêtre contextuelle représente «annuler», dans un contexte de fenêtre contextuelle.
christopherdrum
c'est génial, mon problème est résolu! Merci beaucoup!
Mahir Tayir
109

Sur iPad, l'alerte sera affichée sous forme de popover à l'aide du nouveau UIPopoverPresentationController , elle nécessite que vous spécifiiez un point d'ancrage pour la présentation du popover à l'aide de sourceView et sourceRect ou de barButtonItem

  • barButtonItem
  • sourceView
  • sourceRect

Afin de spécifier le point d'ancrage, vous devrez obtenir une référence au UIPopoverPresentationController d'UIAlertController et définir l'une des propriétés comme suit:

alertController.popoverPresentationController.barButtonItem = button;

exemple de code:

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
la source
28
Ce n'est pas "l'une des trois" propriétés du point d'ancrage; c'est: "soit un sourceView et un sourceRect ou un barButtonItem".
Rolleric
2
+1 pour Rolleric. La documentation d'Apple indique concernant sourceRect: "Utilisez cette propriété conjointement avec la propriété sourceView pour spécifier l'emplacement d'ancrage pour le popover. Vous pouvez également spécifier l'emplacement d'ancrage pour le popover à l'aide de la propriété barButtonItem." - developer.apple.com/library/prerelease/ios/documentation/UIKit/…
Ben Patch
Oh, mec. Il s'est juste écrasé sans aucun message de journal. Pourquoi ne fournirait-il pas au moins un avertissement au moment de la compilation (pour les applications universelles)?
Mike Keskinov
85

Dans Swift 2, vous voulez faire quelque chose comme ça pour l'afficher correctement sur iPhone et 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)
}

Si vous ne définissez pas le présentateur, vous vous retrouverez avec une exception sur iPad -[UIPopoverPresentationController presentationTransitionWillBegin]avec le message suivant:

Exception fatale: NSGenericException Votre application a présenté un UIAlertController (<UIAlertController: 0x17858a00>) de style UIAlertControllerStyleActionSheet. Le modalPresentationStyle d'un UIAlertController avec ce style est UIModalPresentationPopover. Vous devez fournir des informations d'emplacement pour ce popover via le popoverPresentationController du contrôleur d'alerte. Vous devez fournir un sourceView et un sourceRect ou un barButtonItem. Si ces informations ne sont pas connues lorsque vous présentez le contrôleur d'alerte, vous pouvez les fournir dans la méthode UIPopoverPresentationControllerDelegate -prepareForPopoverPresentation.

kviksilver
la source
26

Mise à jour pour Swift 3.0 et supérieur

    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
la source
J'utilise tiroir, j'essaie d'utiliser la solution donnée mais j'ai échoué.
Rana Ali Waseem du
Je n'ai pas de code, car je supprime la feuille d'action et j'utilise alert. Mais dans mon code, une seule ligne était différente, laissez actionSheet = UIAlertController (title: "" ,, message: "", preferStyle: .actionSheet) Mais je me souviens des journaux, il plantait à cause du tiroir, je pense que le tiroir résiste à l'ouverture fiche d'action. car il s'ouvrait dans le coin gauche de l'écran. le problème ne concernait que l'iPad.
Rana Ali Waseem
15

Mise à jour 2018

Je viens de faire rejeter une application pour cette raison et une résolution très rapide consistait simplement à passer d'une feuille d'action à une alerte.

A fonctionné un charme et a passé les testeurs de l'App Store très bien.

Peut ne pas être une réponse appropriée pour tout le monde, mais j'espère que cela aidera rapidement certains d'entre vous à sortir d'un cornichon.

SongBox
la source
1
Fonctionne parfaitement sur iPad et iPhone - Merci
Jeremy Andrews
Ce n'est pas la meilleure solution. Parfois, vous souhaitez utiliser le style actionSheet, qui est moderne.
ShadeToD
9

Swift 4 et supérieur

J'ai créé une extension

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

Comment utiliser:

let actionSheetVC = UIAlertController(title: "Title", message: nil, preferredStyle: .actionSheet)
addActionSheetForIpad(actionSheet: actionSheetVC)
present(actionSheetVC, animated: true, completion: nil)
Siddhesh Mahadeshwar
la source
Je l'essaye mais je ne peux pas appeler func addActionSheerForiPad dans xcode 11.2.1
Rana Ali Waseem
@RanaAliWaseem appelez-vous cela à l'intérieur de la classe UIViewController?
ShadeToD
Oui. Je l'appelle dans la classe UIViewController. Mais il est hérité d'une classe de base et d'une classe de base héritées de UIViewController.
Rana Ali Waseem
8

Voici une solution rapide:

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];
Serge Gerasimenko
la source
3
Cette question concerne UIAlertController, pas UIActivityViewController
Roman Truba
Pouvez-vous mettre à jour la réponse pour Swift 3 avec UIActivityViewController?
Dia
7

Swift 5

J'ai utilisé le style "feuille d'actions" pour iPhone et "alerte" pour iPad. L'iPad s'affiche au centre de l'écran. Pas besoin de spécifier sourceView ou d'ancrer la vue n'importe où.

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

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

Edit: par suggestion de ShareToD, mise à jour obsolète de la vérification "UI_USER_INTERFACE_IDIOM () == UIUserInterfaceIdiom.pad"

Greg T
la source
2
dans iOS 13, «UI_USER_INTERFACE_IDIOM ()» était déconseillé dans iOS 13.0: utiliser - [UIDevice userInterfaceIdiom] directement. Vous devez le changer en UIDevice.current.userInterfaceIdiom == .pad
ShadeToD
2

Pour moi, j'avais juste besoin d'ajouter ce qui suit:

if let popoverController = alertController.popoverPresentationController {
    popoverController.barButtonItem = navigationItem.rightBarButtonItem
}
Matthew Becker
la source
2
Vous pouvez omettre l'instruction if et utiliser le chaînage facultatif: alertController.popoverPresentationController? .BarButtonItem = navigationItem.rightBarButtonItem
Dale
2

Ajoutez simplement le code suivant avant de présenter votre feuille d'action:

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 = []
}
Légende Newt
la source