Prawidłowa prezentacja UIAlertController na iPadzie z iOS 8

194

W systemie iOS 8.0 firma Apple wprowadziła UIAlertController, aby zastąpić UIActionSheet . Niestety, Apple nie dodał żadnych informacji, jak to przedstawić. Znalazłem wpis na ten temat na blogu hayaGeek, jednak wydaje się, że nie działa na iPadzie. Widok jest całkowicie niewłaściwy:

Niewłaściwie umieszczone: Niewłaściwie umieszczony obraz

Poprawny: wprowadź opis zdjęcia tutaj

Używam następującego kodu, aby pokazać go w interfejsie:

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

Czy istnieje inny sposób dodania go do iPada? A może Apple po prostu zapomniało iPada lub nie zostało jeszcze zaimplementowane?

Matt3o12
źródło

Odpowiedzi:

284

Możesz zaprezentować UIAlertControllerpopovera za pomocą UIPopoverPresentationController.

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

Edytujesz dla Swift 4.2, chociaż istnieje wiele blogów dla tego samego, ale może to zaoszczędzić czas na ich szukanie.

 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
źródło
Użyj [alertController.view setTintColor: [UIColor blackColor]]; jeśli nie widzisz tekstu. UIAlertController domyślnie używa koloru odcienia okna, który w tym przykładzie może być biały i niewidoczny.
dlaczegooz
2
Przycisk Anuluj nie wyświetla się na iPadzie
Bhavin Ramani
14
@BhavinRamani Przyciski anulowania są automatycznie usuwane z okien popover, ponieważ stuknięcie poza okienko popover oznacza „anuluj” w kontekście popover.
christopherdrum
to jest niesamowite, mój problem został rozwiązany! Dziękuję bardzo!
Mahir Tayir
109

Na iPadzie alert będzie wyświetlany jako popover przy użyciu nowego UIPopoverPresentationController , wymaga podania punktu kotwiczenia dla prezentacji popover przy użyciu sourceView i sourceRect lub barButtonItem

  • barButtonItem
  • sourceView
  • sourceRect

Aby określić punkt kontrolny, musisz uzyskać odwołanie do UIPopoverPresentationController UIAlertController i ustawić jedną z właściwości w następujący sposób:

alertController.popoverPresentationController.barButtonItem = button;

przykładowy kod:

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
źródło
28
To nie jest „jedna z trzech” właściwości punktu kontrolnego; jest to: „albo sourceView i sourceRect lub barButtonItem”.
Rolleric
2
+1 dla Rolleric. Dokumenty Apple dotyczące dokumentacji sourceRect: „Użyj tej właściwości w połączeniu z właściwością sourceView, aby określić lokalizację zakotwiczenia dla popovera. Alternatywnie możesz określić lokalizację zakotwiczenia dla popovera za pomocą właściwości barButtonItem”. - developer.apple.com/library/prerelease/ios/documentation/UIKit/…
Ben Patch
O stary. Po prostu zawiesił się bez żadnego komunikatu w dzienniku. Dlaczego nie zapewniałby przynajmniej ostrzeżenia podczas kompilacji (dla aplikacji uniwersalnych)?
Mike Keskinov
85

W Swift 2 chcesz zrobić coś takiego, aby poprawnie pokazać to na iPhonie i iPadzie:

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

Jeśli nie ustawisz prezentera, na iPadzie pojawi się wyjątek -[UIPopoverPresentationController presentationTransitionWillBegin]z następującym komunikatem:

Wyjątek krytyczny: NSGenericException Twoja aplikacja zaprezentowała UIAlertController (<UIAlertController: 0x17858a00>) w stylu UIAlertControllerStyleActionSheet. ModalPresentationStyle UIAlertController z tym stylem to UIModalPresentationPopover. Musisz podać informacje o lokalizacji dla tego popovera poprzez popoverPresentationController kontrolera alertów. Musisz podać sourceView i sourceRect lub barButtonItem. Jeśli te informacje nie są znane, gdy prezentujesz kontroler alertów, możesz podać je w metodzie UIPopoverPresentationControllerDelegate -prepareForPopoverPresentation.

kviksilver
źródło
26

Aktualizacja do wersji Swift 3.0 i nowszej

    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
źródło
Używam szuflady, próbuję użyć podanego rozwiązania, ale nie powiodło się.
Rana Ali Waseem
Nie mam kodu, ponieważ usuwam arkusz akcji i używam alertu. Ale w moim kodzie tylko jedna linia była inna, pozwól actionSheet = UIAlertController (title: "" ,, message: "", preferowany styl: .actionSheet) Ale pamiętam logi, Zawieszał się z powodu szuflady, myślę, że szuflada nie chce się otworzyć arkusz akcji. ponieważ otwierał się w lewym rogu ekranu. problem dotyczył tylko iPada.
Rana Ali Waseem
15

Aktualizacja 2018

Właśnie z tego powodu odrzuciłem aplikację, a bardzo szybkim rozwiązaniem było po prostu przejście z użycia arkusza akcji na alert.

Działał urok i zdał testy testerów App Store.

To może nie być odpowiednia odpowiedź dla wszystkich, ale mam nadzieję, że pomoże to niektórym z was szybko wyjść z zalewy.

SongBox
źródło
1
Działa doskonale zarówno na iPadzie, jak i iPhonie - Dzięki
Jeremy Andrews
To nie jest najlepsze rozwiązanie. Czasami chcesz użyć stylu ActionSheet, który jest nowoczesny.
ShadeToD
9

Swift 4 i wyżej

Utworzyłem rozszerzenie

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

Jak używać:

let actionSheetVC = UIAlertController(title: "Title", message: nil, preferredStyle: .actionSheet)
addActionSheetForIpad(actionSheet: actionSheetVC)
present(actionSheetVC, animated: true, completion: nil)
Siddhesh Mahadeshwar
źródło
Próbuję, ale nie mogę wywołać func addActionSheerForiPad w xcode 11.2.1
Rana Ali Waseem
@RanaAliWaseem wywołujesz to w klasie UIViewController?
ShadeToD
tak. Nazywam to w klasie UIViewController. Ale jest dziedziczony z klasą podstawową i klasą podstawową odziedziczoną z UIViewController.
Rana Ali Waseem
8

Oto szybkie rozwiązanie:

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
źródło
3
To pytanie dotyczy UIAlertController, a nie UIActivityViewController
Roman Truba
Czy możesz zaktualizować odpowiedź dla Swift 3 wraz z UIActivityViewController?
Dia
7

Szybki 5

Użyłem stylu „arkusz działań” dla iPhone'a i „alert” dla iPada. iPad wyświetla się na środku ekranu. Nie trzeba określać sourceView ani zakotwiczać widoku w dowolnym miejscu.

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

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

Edycja: Zgodnie z sugestią ShareToD, zaktualizowano przestarzałe „UI_USER_INTERFACE_IDIOM () == UIUserInterfaceIdiom.pad”

Greg T.
źródło
2
w iOS 13 „UI_USER_INTERFACE_IDIOM ()” jest przestarzałe w iOS 13.0: Użyj - [UIDevice userInterfaceIdiom] bezpośrednio. Powinieneś zmienić go na UIDevice.current.userInterfaceIdiom == .pad
ShadeToD
Wadą tego podejścia jest to, że alertu nie można
odrzucić
2

Dla mnie musiałem tylko dodać:

if let popoverController = alertController.popoverPresentationController {
    popoverController.barButtonItem = navigationItem.rightBarButtonItem
}
Matthew Becker
źródło
2
Można pominąć instrukcję if i użyć opcjonalnego łączenia: alertController.popoverPresentationController? .BarButtonItem = navigationItem.rightBarButtonItem
Dale
2

Po prostu dodaj następujący kod przed zaprezentowaniem swojego arkusza akcji:

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 = []
}
Tytuł Newt
źródło