Presentar un UIAlertController correctamente en un iPad con iOS 8

194

Con iOS 8.0, Apple introdujo UIAlertController para reemplazar UIActionSheet . Desafortunadamente, Apple no agregó ninguna información sobre cómo presentarlo. Encontré una entrada al respecto en el blog de hayaGeek, sin embargo, no parece funcionar en iPad. La vista está totalmente fuera de lugar:

Fuera de lugar: Imagen fuera de lugar

Correcto: ingrese la descripción de la imagen aquí

Utilizo el siguiente código para mostrarlo en la interfaz:

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

¿Hay otra forma de agregarlo para iPad? ¿O Apple simplemente olvidó el iPad, o aún no lo implementó?

Matt3o12
fuente

Respuestas:

284

Puede presentar a UIAlertControllerdesde un popover usando 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];

Edición para Swift 4.2, aunque hay muchos blogs disponibles para el mismo, pero puede ahorrarle tiempo para buscarlos.

 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
fuente
Utilice [alertController.view setTintColor: [UIColor blackColor]]; si no ves el texto UIAlertController utiliza el color de tinte de la ventana de forma predeterminada, que puede ser blanco e invisible en este ejemplo.
whyoz
2
El botón Cancelar no se muestra en iPad
Bhavin Ramani
14
@BhavinRamani Los botones de cancelación se eliminan automáticamente de los popovers, porque tocar fuera del popover representa "cancelar", en un contexto de popover.
christopherdrum
¡Esto es increíble, mi problema está resuelto! ¡Muchas gracias!
Mahir Tayir
109

En iPad, la alerta se mostrará como un popover usando el nuevo UIPopoverPresentationController , requiere que especifique un punto de anclaje para la presentación del popover usando sourceView y sourceRect o barButtonItem

  • barButtonItem
  • sourceView
  • sourceRect

Para especificar el punto de anclaje, necesitará obtener una referencia al UIPopoverPresentationController de UIAlertController y establecer una de las propiedades de la siguiente manera:

alertController.popoverPresentationController.barButtonItem = button;

Código de muestra:

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
fuente
28
No es "una de las tres" propiedades de punto de anclaje; es: "ya sea una sourceView y sourceRect o un barButtonItem".
Rolleric
2
+1 para Rolleric. La documentación de Apple establece respecto a sourceRect: "Use esta propiedad junto con la propiedad sourceView para especificar la ubicación de anclaje para el popover. Alternativamente, puede especificar la ubicación de anclaje para el popover usando la propiedad barButtonItem". - developer.apple.com/library/prerelease/ios/documentation/UIKit/…
Ben Patch
Oh hombre. Simplemente se bloqueó sin ningún mensaje de registro. ¿Por qué al menos no proporcionar una advertencia en tiempo de compilación (para aplicaciones universales)?
Mike Keskinov
85

En Swift 2, desea hacer algo como esto para mostrarlo correctamente en iPhone y 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 no configura el presentador, terminará con una excepción en el iPad -[UIPopoverPresentationController presentationTransitionWillBegin]con el siguiente mensaje:

Excepción grave: NSGenericException Su aplicación ha presentado un UIAlertController (<UIAlertController: 0x17858a00>) de estilo UIAlertControllerStyleActionSheet. El modalPresentationStyle de un UIAlertController con este estilo es UIModalPresentationPopover. Debe proporcionar información de ubicación para este popover a través del popoverPresentationController del controlador de alertas. Debe proporcionar sourceView y sourceRect o barButtonItem. Si no conoce esta información cuando presente el controlador de alertas, puede proporcionarlo en el método UIPopoverPresentationControllerDelegate -prepareForPopoverPresentation.

kviksilver
fuente
26

Actualización para Swift 3.0 y superior

    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
fuente
Estoy usando el cajón, trato de usar la solución dada pero fallé.
Rana Ali Waseem
No tengo código, porque elimino la hoja de acción y uso la alerta. Pero en mi código solo una línea era diferente let actionSheet = UIAlertController (title: "" ,, message: "", preferredStyle: .actionSheet) Pero recuerdo los registros, estaba fallando debido al cajón, creo que el cajón resiste abrirse hoja de acción porque se abría en la esquina izquierda de la pantalla. El problema solo estaba en el iPad.
Rana Ali Waseem
15

Actualización 2018

Acabo de rechazar una aplicación por este motivo y una resolución muy rápida fue simplemente cambiar de usar una hoja de acción a una alerta.

Funcionó de maravilla y pasó los probadores de App Store muy bien.

Puede que no sea una respuesta adecuada para todos, pero espero que esto ayude a algunos de ustedes a salir de apuros rápidamente.

SongBox
fuente
1
Funcionó perfectamente tanto en iPad como en iPhone - Gracias
Jeremy Andrews
No es la mejor solución. A veces quieres usar el estilo actionSheet, que es moderno.
ShadeToD
9

Swift 4 y superior

He creado una extensión

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

Cómo utilizar:

let actionSheetVC = UIAlertController(title: "Title", message: nil, preferredStyle: .actionSheet)
addActionSheetForIpad(actionSheet: actionSheetVC)
present(actionSheetVC, animated: true, completion: nil)
Siddhesh Mahadeshwar
fuente
Lo intento pero no puedo llamar a func addActionSheerForiPad en xcode 11.2.1
Rana Ali Waseem el
@RanaAliWaseem, ¿estás llamando a esto dentro de la clase UIViewController?
ShadeToD
si. Lo llamo en la clase UIViewController. Pero se hereda con una clase Base y una Clase Base heredada de UIViewController.
Rana Ali Waseem
8

Aquí hay una solución rápida:

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
fuente
3
Esta pregunta es sobre UIAlertController, no UIActivityViewController
Roman Truba
¿Puedes actualizar la respuesta para Swift 3 junto con UIActivityViewController?
Dia
7

Swift 5

Utilicé el estilo de "hoja de acciones" para iPhone y "alerta" para iPad. iPad se muestra en el centro de la pantalla. No es necesario especificar sourceView ni anclar la vista en ningún lado.

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

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

Editar: según la sugerencia de ShareToD, actualizado "UI_USER_INTERFACE_IDIOM () == UIUserInterfaceIdiom.pad" comprobar

Greg T
fuente
2
en iOS 13 'UI_USER_INTERFACE_IDIOM ()' quedó en desuso en iOS 13.0: Use - [UIDevice userInterfaceIdiom] directamente. Debe cambiarlo a UIDevice.current.userInterfaceIdiom == .pad
ShadeToD
2

Para mí solo necesitaba agregar lo siguiente:

if let popoverController = alertController.popoverPresentationController {
    popoverController.barButtonItem = navigationItem.rightBarButtonItem
}
Matthew Becker
fuente
2
Puede omitir la instrucción if y usar el encadenamiento opcional: alertController.popoverPresentationController? .BarButtonItem = navigationItem.rightBarButtonItem
Dale
2

Simplemente agregue el siguiente código antes de presentar su hoja de acción:

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 = []
}
Título Newt
fuente