Apresentando um UIAlertController corretamente em um iPad usando o iOS 8

194

Com o iOS 8.0, a Apple apresentou o UIAlertController para substituir o UIActionSheet . Infelizmente, a Apple não adicionou nenhuma informação sobre como apresentá-lo. Eu encontrei uma entrada sobre isso no blog da hayaGeek, no entanto, ele não parece funcionar no iPad. A vista é totalmente equivocada:

Extraviado: Imagem extraviada

Corrigir: insira a descrição da imagem aqui

Eu uso o seguinte código para mostrá-lo na interface:

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

Existe outra maneira de adicioná-lo ao iPad? Ou a Apple esqueceu o iPad ou ainda não implementou?

Matt3o12
fonte

Respostas:

284

Você pode apresentar um UIAlertControllerde um popover usando UIPopoverPresentationController.

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

Edição para o Swift 4.2, embora existam muitos blogs disponíveis para o mesmo, mas isso pode economizar seu tempo para procurá-los.

 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
fonte
Use [alertController.view setTintColor: [UIColor blackColor]]; se você não vir o texto. UIAlertController usa a cor da janela por padrão, que pode ser branca e invisível neste exemplo.
whyoz
2
Botão Cancelar não está exibindo no iPad
Bhavin Ramani
14
Os botões @BhavinRamani Cancel são removidos dos popovers automaticamente, porque tocar fora do popover representa "cancelar", em um contexto popover.
Christopherdrum
isso é incrível, meu problema está resolvido! muito obrigado!
Mahir Tayir 31/01
109

No iPad, o alerta será exibido como um popover usando o novo UIPopoverPresentationController , requer que você especifique um ponto de ancoragem para a apresentação do popover usando um sourceView e sourceRect ou um barButtonItem

  • barButtonItem
  • sourceView
  • sourceRect

Para especificar o ponto de ancoragem, você precisará obter uma referência ao UIPopoverPresentationController do UIAlertController e definir uma das propriedades da seguinte maneira:

alertController.popoverPresentationController.barButtonItem = button;

Código de amostra:

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
fonte
28
Não é "uma das três" propriedades do ponto de ancoragem; é: "um sourceView e sourceRect ou um barButtonItem".
Rolleric
2
+1 para Rolleric. A documentação da Apple declara sobre o sourceRect: "Use esta propriedade em conjunto com a propriedade sourceView para especificar o local da âncora para o popover. Como alternativa, você pode especificar o local da âncora para o popover usando a propriedade barButtonItem." - developer.apple.com/library/prerelease/ios/documentation/UIKit/…
Ben Patch
Oh cara. Apenas travou sem nenhuma mensagem de log. Por que não forneceria pelo menos um aviso em tempo de compilação (para aplicativos universais)?
Mike Keskinov 19/01/19
85

No Swift 2, você deseja fazer algo assim para mostrá-lo corretamente no 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 você não definir o apresentador, você verá uma exceção no iPad -[UIPopoverPresentationController presentationTransitionWillBegin]com a seguinte mensagem:

Exceção fatal: NSGenericException Seu aplicativo apresentou um UIAlertController (<UIAlertController: 0x17858a00>) do estilo UIAlertControllerStyleActionSheet. O modalPresentationStyle de um UIAlertController com esse estilo é UIModalPresentationPopover. Você deve fornecer informações de localização para esse popover por meio do popoverPresentationController do controlador de alerta. Você deve fornecer um sourceView e sourceRect ou um barButtonItem. Se essas informações não forem conhecidas quando você apresentar o controlador de alerta, poderá fornecê-las no método UIPopoverPresentationControllerDelegate -prepareForPopoverPresentation.

kviksilver
fonte
26

Atualização para Swift 3.0 e 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
fonte
Estou usando a gaveta, tento usar a solução fornecida, mas falhei.
Rana Ali Waseem
Não tenho código, porque removo a folha de ação e uso o alerta. Mas no meu código apenas uma linha era diferente, deixe actionSheet = UIAlertController (title: "" ,, message: "", selectedStyle: .actionSheet) Mas eu lembro dos logs, estava travando devido à gaveta, acho que a gaveta resiste a abrir folha de ação. porque estava abrindo no canto esquerdo da tela. O problema ocorreu apenas no iPad.
Rana Ali Waseem
15

Atualização de 2018

Acabei de ter um aplicativo rejeitado por esse motivo e uma solução muito rápida foi simplesmente deixar de usar uma folha de ação para um alerta.

Trabalhou um charme e passou nos testadores da App Store muito bem.

Pode não ser uma resposta adequada para todos, mas espero que isso ajude alguns de vocês a sair rapidamente.

SongBox
fonte
1
Funcionou perfeitamente no iPad e no iPhone - Obrigado
Jeremy Andrews
Não é a melhor solução. Às vezes, você deseja usar o estilo actionSheet, que é moderno.
ShadeToD
9

Swift 4 e acima

Eu criei uma extensão

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

Como usar:

let actionSheetVC = UIAlertController(title: "Title", message: nil, preferredStyle: .actionSheet)
addActionSheetForIpad(actionSheet: actionSheetVC)
present(actionSheetVC, animated: true, completion: nil)
Siddhesh Mahadeshwar
fonte
Eu tento, mas não capaz de chamar func addActionSheerForiPad no Xcode 11.2.1
Rana Ali Waseem
@RanaAliWaseem você está chamando isso dentro da classe UIViewController?
ShadeToD
sim. Eu chamo isso na classe UIViewController. Mas é herdado com uma classe Base e uma Classe Base herdadas de UIViewController.
Rana Ali Waseem 03/03
8

Aqui está uma solução 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
fonte
3
Esta pergunta é sobre UIAlertController, não UIActivityViewController
Roman Truba
Você pode atualizar a resposta para o Swift 3 junto com o UIActivityViewController?
Dia
7

Swift 5

Eu usei o estilo "actionsheet" no iPhone e "alert" no iPad. O iPad é exibido no centro da tela. Não é necessário especificar o sourceView ou ancorar a visualização em qualquer lugar.

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: por sugestão do ShareToD, atualização obsoleta "UI_USER_INTERFACE_IDIOM () == UIUserInterfaceIdiom.pad"

Greg T
fonte
2
no iOS 13, 'UI_USER_INTERFACE_IDIOM ()' foi preterido no iOS 13.0: Use - [UIDevice userInterfaceIdiom] diretamente. Você deve alterá-lo para UIDevice.current.userInterfaceIdiom == .pad
ShadeToD
2

Para mim, eu só precisava adicionar o seguinte:

if let popoverController = alertController.popoverPresentationController {
    popoverController.barButtonItem = navigationItem.rightBarButtonItem
}
Matthew Becker
fonte
2
Você pode omitir a instrução if e usar o encadeamento opcional: alertController.popoverPresentationController? .BarButtonItem = navigationItem.rightBarButtonItem
Dale
2

Basta adicionar o seguinte código antes de apresentar sua folha de ações:

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 = []
}
Newt da legenda
fonte