Trình bày một UIAlertContoder đúng cách trên iPad bằng iOS 8


194

Với iOS 8.0, Apple đã giới thiệu UIAlertControll để thay thế UIActionSheet . Thật không may, Apple đã không thêm bất kỳ thông tin nào về cách trình bày nó. Tôi đã tìm thấy một mục về nó trên blog của hayaGeek, tuy nhiên, nó dường như không hoạt động trên iPad. Khung nhìn hoàn toàn bị đặt sai chỗ:

Đặt nhầm chỗ: Hình ảnh bị thất lạc

Chính xác: nhập mô tả hình ảnh ở đây

Tôi sử dụng đoạn mã sau để hiển thị nó trên giao diện:

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

Có cách nào khác để thêm nó cho iPad không? Hay Apple chỉ quên iPad, hoặc chưa thực hiện?

Câu trả lời:


284

Bạn có thể trình bày UIAlertControllertừ một popover bằng cách sử dụng UIPopoverPresentationController.

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

Chỉnh sửa cho Swift 4.2, mặc dù có nhiều blog có sẵn cho cùng nhưng nó có thể tiết kiệm thời gian của bạn để đi và tìm kiếm chúng.

 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
            }

Sử dụng [alertControll.view setTintColor: [UIColor blackColor]]; nếu bạn không nhìn thấy văn bản. Theo mặc định, UIAlertControll sử dụng màu sắc của cửa sổ, có thể là màu trắng và vô hình trong ví dụ này.
whyoz

2
Nút
hủy

14
Các nút Hủy @BhavinRamani được tự động xóa khỏi cửa sổ bật lên, vì việc nhấn bên ngoài cửa sổ bật lên thể hiện "hủy", trong ngữ cảnh popover.
christopherdrum

Điều này thật tuyệt vời, vấn đề của tôi đã được giải quyết! cảm ơn bạn rất nhiều!
Mahir Tayir

109

Trên iPad, cảnh báo sẽ được hiển thị dưới dạng popover bằng UIPopoverPftimeationContoder mới , nó yêu cầu bạn chỉ định một điểm neo để trình bày popover bằng cách sử dụng sourceView và sourceRect hoặc barButtonItem

  • barButtonItem
  • nguồn xem
  • nguồnRect

Để chỉ định điểm neo, bạn sẽ cần có được một tham chiếu đến UIPopoverPftimeationContoder của UIAlertContoder và thiết lập một trong các thuộc tính như sau:

alertController.popoverPresentationController.barButtonItem = button;

mã mẫu:

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
Đây không phải là "một trong ba" thuộc tính điểm neo; đó là: "hoặc là mộtViewView và sourceRect hoặc barButtonItem".
Rolleric

2
+1 cho Rolleric. Tài liệu của Apple tuyên bố liên quan đến sourceRect: "Sử dụng thuộc tính này cùng với thuộc tính sourceView để chỉ định vị trí neo cho cửa sổ bật lên. Ngoài ra, bạn có thể chỉ định vị trí neo cho cửa sổ bật lên bằng thuộc tính barButtonItem." - developer.apple.com/library/prerelease/ios/documentation/UIKit/...
Bến vá

Trời ơi. Nó chỉ bị sập mà không có bất kỳ thông điệp tường trình. Tại sao ít nhất sẽ không cung cấp cảnh báo thời gian biên dịch (cho các ứng dụng phổ quát)?
Mike Keskinov

84

Trong Swift 2, bạn muốn làm một cái gì đó như thế này để hiển thị đúng trên iPhone và 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)
}

Nếu bạn không đặt người thuyết trình, bạn sẽ có một ngoại lệ trên iPad -[UIPopoverPresentationController presentationTransitionWillBegin]với thông báo sau:

Ngoại lệ gây tử vong: NSGenericException Ứng dụng của bạn đã trình bày một UIAlertControll (<UIAlertControll: 0x17858a00>) của kiểu UIAlertControllStyleActionSheet. ModalPftimeationStyle của một UIAlertControll với kiểu này là UIModalPftimeationPopover. Bạn phải cung cấp thông tin vị trí cho cửa sổ bật lên này thông qua popoverPftimeationContoder của bộ điều khiển cảnh báo. Bạn phải cung cấp sourceView và sourceRect hoặc barButtonItem. Nếu thông tin này không được biết khi bạn trình bày bộ điều khiển cảnh báo, bạn có thể cung cấp thông tin đó trong phương thức UIPopoverPftimeationControllDelegate -prepareForPopoverPftimeation.


26

Cập nhật cho Swift 3.0 trở lên

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

Tôi đang sử dụng ngăn kéo, tôi cố gắng sử dụng giải pháp đã cho nhưng không thành công.
Rana Ali Waseem

Tôi không có mã, vì tôi xóa trang tính hành động và sử dụng cảnh báo. Nhưng trong mã của tôi chỉ có một dòng là khác nhau, hãy để actionSheet = UIAlertControll (title: "" ,, message: "", preferStyle: .actionSheet) Nhưng tôi nhớ các bản ghi, Nó bị sập do ngăn kéo, tôi nghĩ rằng ngăn kéo đó không mở được tờ hành động. bởi vì nó được mở ở góc trái màn hình vấn đề chỉ có ở iPad.
Rana Ali Waseem

15

Cập nhật 2018

Tôi vừa có một ứng dụng bị từ chối vì lý do này và một giải pháp rất nhanh chỉ đơn giản là thay đổi từ sử dụng bảng hành động sang cảnh báo.

Làm việc say mê và vượt qua những người thử nghiệm App Store tốt.

Có thể không phải là một câu trả lời phù hợp cho tất cả mọi người nhưng tôi hy vọng điều này sẽ giúp một số bạn nhanh chóng thoát khỏi tình trạng khó khăn.


1
Hoạt động hoàn hảo trên cả iPad và iPhone - Cảm ơn
Jeremy Andrew

Nó không phải là giải pháp tốt nhất. Đôi khi bạn muốn sử dụng phong cách actionSheet, hiện đại.
ShadowToD

9

Swift 4 trở lên

Tôi đã tạo một phần mở rộng

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ách sử dụng:

let actionSheetVC = UIAlertController(title: "Title", message: nil, preferredStyle: .actionSheet)
addActionSheetForIpad(actionSheet: actionSheetVC)
present(actionSheetVC, animated: true, completion: nil)

Tôi đã thử nhưng không thể gọi func addActionSheerForiPad trong xcode 11.2.1
Rana Ali Waseem

@RanaAliWaseem bạn có đang gọi cái này bên trong lớp UIViewControll không?
ShadowToD

Đúng. Tôi gọi nó trong lớp UIViewControll. Nhưng nó được kế thừa với một lớp Cơ sở và Lớp Cơ sở được kế thừa từ UIViewControll.
Rana Ali Waseem

8

Đây là một giải pháp nhanh chóng:

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
Câu hỏi này là về UIAlertControll, không phải UIActivityViewControll
Roman Truba

Bạn có thể cập nhật câu trả lời cho Swift 3 cùng với UIActivityViewControll không?
Dia

7

Swift 5

Tôi đã sử dụng kiểu "hành động" cho iPhone và "cảnh báo" cho iPad. iPad hiển thị ở giữa màn hình. Không cần chỉ định sourceView hoặc neo view ở bất cứ đâu.

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

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

Chỉnh sửa: Đề xuất của Per ShareToD, đã cập nhật "UI_USER_INTERFACE_IDIOM () == UIUserInterfaceIdiom.pad"


2
trong iOS 13 'UI_USER_INTERFACE_IDIOM ()' không được dùng nữa trong iOS 13.0: Sử dụng - [UIDevice userInterfaceIdiom]. Bạn nên đổi nó thành UIDevice.cản.userInterfaceIdiom == .pad
ShadowToD

2

Đối với tôi, tôi chỉ cần thêm vào như sau:

if let popoverController = alertController.popoverPresentationController {
    popoverController.barButtonItem = navigationItem.rightBarButtonItem
}

2
Bạn có thể bỏ qua các câu lệnh if và sử dụng tùy chọn chain: alertController.popoverPresentationController .barButtonItem = navigationItem.rightBarButtonItem
Dale

2

Chỉ cần thêm mã sau đây trước khi trình bày bảng hành động của bạ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 = []
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.