นำเสนอ UIAlertController อย่างถูกต้องบน iPad โดยใช้ iOS 8


194

กับ iOS 8.0 Apple เปิดUIAlertControllerเพื่อแทนที่UIActionSheet น่าเสียดายที่ Apple ไม่ได้เพิ่มข้อมูลใด ๆ เกี่ยวกับวิธีการนำเสนอ ฉันพบรายการเกี่ยวกับเรื่องนี้ในบล็อกของ hayaGeek แต่ดูเหมือนว่าจะไม่ทำงานบน iPad มุมมองถูกใส่ผิดที่:

ใส่ผิด: รูปภาพถูกวางผิดที่

แก้ไข: ป้อนคำอธิบายรูปภาพที่นี่

ฉันใช้รหัสต่อไปนี้เพื่อแสดงบนอินเตอร์เฟส:

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

มีวิธีอื่นในการเพิ่มลงใน iPad หรือไม่ หรือว่า Apple ลืม iPad หรือยังไม่ได้ใช้เลย

คำตอบ:


284

คุณสามารถนำเสนอUIAlertControllerจาก popover UIPopoverPresentationControllerโดยใช้

ใน 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];

การแก้ไข Swift 4.2 แม้ว่าจะมีบล็อกจำนวนมาก แต่ก็เหมือนกัน แต่อาจช่วยประหยัดเวลาของคุณในการค้นหา

 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
            }

ใช้ [alertController.view setTintColor: [UIColor blackColor]]; หากคุณไม่เห็นข้อความ UIAlertController ใช้สีอ่อนของหน้าต่างโดยค่าเริ่มต้นซึ่งอาจเป็นสีขาวและมองไม่เห็นในตัวอย่างนี้
Whyoz

2
ปุ่มยกเลิกไม่แสดงใน iPad
Bhavin Ramani

14
@BhavinRamani ปุ่มยกเลิกจะถูกลบออกจาก popovers โดยอัตโนมัติเนื่องจากการแตะนอก popover หมายถึง "ยกเลิก" ในบริบทของ popover
christopherdrum

นี่มันยอดเยี่ยมปัญหาของฉันได้รับการแก้ไขแล้ว! ขอบคุณมาก!
Mahir Tayir

109

บน iPad การแจ้งเตือนจะแสดงเป็น popover โดยใช้UIPopoverPresentationControllerใหม่คุณต้องระบุจุดยึดสำหรับการนำเสนอของ popover โดยใช้ sourceView และ sourceRect หรือ barButtonItem

  • barButtonItem
  • sourceView
  • sourceRect

เพื่อระบุจุดยึดคุณจะต้องได้รับการอ้างอิงถึง UIPopoverPresentationController ของ UIAlertController และตั้งค่าคุณสมบัติอย่างใดอย่างหนึ่งดังนี้:

alertController.popoverPresentationController.barButtonItem = button;

รหัสตัวอย่าง:

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
ไม่ใช่คุณสมบัติจุดยึด "หนึ่งในสาม" มันคือ: "ไม่ว่าจะเป็น sourceView และ sourceRect หรือ barButtonItem"
Rolleric

2
+1 สำหรับ Rolleric สถานะเอกสารของ Apple เกี่ยวกับ sourceRect: "ใช้คุณสมบัตินี้ร่วมกับคุณสมบัติ sourceView เพื่อระบุตำแหน่งจุดยึดสำหรับ popover หรือคุณอาจระบุตำแหน่งจุดยึดสำหรับ popover โดยใช้คุณสมบัติ barButtonItem" - developer.apple.com/library/prerelease/ios/documentation/UIKit/…
Ben Patch

โอ้มนุษย์ มันพังโดยไม่มีข้อความบันทึกใด ๆ ทำไมอย่างน้อยจะไม่มีการเตือนเวลาคอมไพล์ (สำหรับแอพสากล)
Mike Keskinov

85

ใน Swift 2 คุณต้องการทำสิ่งนี้เพื่อแสดงบน iPhone และ 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)
}

หากคุณไม่ได้ตั้งค่าผู้นำเสนอคุณจะจบลงด้วยการยกเว้นใน iPad -[UIPopoverPresentationController presentationTransitionWillBegin]ด้วยข้อความต่อไปนี้:

ข้อยกเว้นร้ายแรง: NSGenericException แอปพลิเคชันของคุณแสดง UIAlertController (<UIAlertController: 0x17858a00>) ของรูปแบบ UIAlertControllerStyleActionSheet modalPresentationStyle ของ UIAlertController ที่มีสไตล์นี้คือ UIModalPresentationPopover คุณต้องให้ข้อมูลตำแหน่งสำหรับ popover นี้ผ่านทาง popoverPresentationContController ของตัวแจ้งเตือน คุณต้องระบุ sourceView และ sourceRect หรือ barButtonItem หากไม่ทราบข้อมูลนี้เมื่อคุณแสดงตัวควบคุมการแจ้งเตือนคุณอาจระบุไว้ในวิธีการ UIPopoverPresentationControllerDelegate -prepareForPopoverPresentation


26

อัปเดตสำหรับ Swift 3.0 และสูงกว่า

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

ฉันกำลังใช้ลิ้นชักฉันพยายามใช้วิธีแก้ปัญหาที่กำหนด แต่ล้มเหลว
Rana Ali Waseem

ฉันไม่มีรหัสเพราะฉันลบแผ่นคำสั่งและใช้การแจ้งเตือน แต่ในรหัสของฉันมีเพียงหนึ่งบรรทัดที่แตกต่างกันให้ actionSheet = UIAlertController (ชื่อเรื่อง: "", message: "", ที่ต้องการสไตล์: .actionSheet)แต่ฉันจำบันทึกได้มันล้มเพราะลิ้นชักฉันคิดว่าลิ้นชักเปิดต่อต้าน แผ่นงาน เพราะมันเปิดที่มุมซ้ายของหน้าจอ ปัญหาเท่านั้นคือที่ iPad
Rana Ali Waseem

15

ปรับปรุง 2018

ฉันเพิ่งมีแอปพลิเคชันที่ถูกปฏิเสธด้วยเหตุผลนี้และการแก้ปัญหาอย่างรวดเร็วมากก็คือการเปลี่ยนจากการใช้แผ่นงานเป็นการแจ้งเตือน

ใช้งานได้อย่างมีเสน่ห์และผ่านการทดสอบจาก App Store ได้ดี

อาจไม่ใช่คำตอบที่เหมาะสมสำหรับทุกคน แต่ฉันหวังว่าสิ่งนี้จะช่วยให้คุณบางคนออกจากดองอย่างรวดเร็ว


1
ทำงานได้อย่างสมบูรณ์แบบทั้งบน iPad และ iPhone - ขอบคุณ
Jeremy Andrews

มันไม่ใช่ทางออกที่ดีที่สุด บางครั้งคุณต้องการใช้สไตล์ actionSheet ซึ่งทันสมัย
ShadeToD

9

Swift 4 ขึ้นไป

ฉันสร้างส่วนขยายแล้ว

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

วิธีใช้:

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

ฉันลองแล้ว แต่ไม่สามารถเรียก func addActionSheerForiPad ใน xcode 11.2.1
Rana Ali Waseem

@RanaAliWaseem คุณกำลังเรียกสิ่งนี้อยู่ในคลาส UIViewController?
ShadeToD

ใช่. ฉันเรียกมันว่าเป็นคลาส UIViewController แต่มันได้รับการสืบทอดมาพร้อมกับคลาสฐานและคลาสฐานที่สืบทอดมาจาก UIViewController
Rana Ali Waseem

8

นี่เป็นวิธีแก้ปัญหาอย่างรวดเร็ว:

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
คำถามนี้เกี่ยวกับ UIAlertController ไม่ใช่ UIActivityViewController
Roman Truba

คุณสามารถอัพเดตคำตอบสำหรับ Swift 3 พร้อมกับ UIActivityViewController ได้หรือไม่?
Dia

7

สวิฟท์ 5

ฉันใช้สไตล์ "actionsheet" สำหรับ iPhone และ "alert" สำหรับ iPad iPad แสดงที่กึ่งกลางของหน้าจอ ไม่จำเป็นต้องระบุ sourceView หรือยึดมุมมองไว้ที่ใดก็ได้

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

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

แก้ไข: ตามคำแนะนำของ ShareToD การอัปเดตเลิกใช้ "UI_USER_INTERFACE_IDIOM () == ตรวจสอบ UIUserInterfaceIdiom.pad"


2
ใน iOS 13 'UI_USER_INTERFACE_IDIOM ()' ถูกคัดค้านใน iOS 13.0: การใช้ - [UIDevice userInterfaceIdiom] โดยตรง คุณควรเปลี่ยนเป็น UIDevice.current.userInterfaceIdiom == .pad
ShadeToD

ข้อเสียอย่างหนึ่งของวิธีการนี้คือการแจ้งเตือนไม่สามารถยกเลิกได้เมื่อคลิกนอกขอบเขต
mliu

2

สำหรับฉันฉันแค่ต้องเพิ่มต่อไปนี้:

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

2
คุณสามารถละเว้นคำสั่ง if และใช้การเชื่อมโยงเสริม: alertController.popoverPresentationController? .barButtonItem = navigationItem.rightBarButtonItem
Dale

2

เพียงเพิ่มรหัสต่อไปนี้ก่อนที่จะนำเสนอแผ่นงานของคุณ:

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 = []
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.