UIActivityViewControll bị sập trên iPad iOS 8


288

Tôi hiện đang thử nghiệm ứng dụng của mình với Xcode 6 (Beta 6). UIActivityViewContoder hoạt động tốt với các thiết bị và trình giả lập iPhone nhưng gặp sự cố với trình giả lập và thiết bị iPad (iOS 8) với các bản ghi sau

Terminating app due to uncaught exception 'NSGenericException', 
reason: 'UIPopoverPresentationController 
(<_UIAlertControllerActionSheetRegularPresentationController: 0x7fc7a874bd90>) 
should have a non-nil sourceView or barButtonItem set before the presentation occurs.

Tôi đang sử dụng mã sau cho iPhone và iPad cho cả iOS 7 cũng như iOS 8

NSData *myData = [NSData dataWithContentsOfFile:_filename];
NSArray *activityItems = [NSArray arrayWithObjects:myData, nil];
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:nil applicationActivities:nil];
activityViewController.excludedActivityTypes = @[UIActivityTypeCopyToPasteboard];
[self presentViewController:activityViewController animated:YES completion:nil];

Tôi cũng đang gặp một sự cố tương tự trong một ứng dụng khác của mình. Bạn có thể vui lòng hướng dẫn cho tôi ? có gì thay đổi với UIActivityViewControll trong iOS 8 không? Tôi đã kiểm tra nhưng tôi không tìm thấy gì về điều này


Câu trả lời dưới đây kiểm tra cho thành ngữ. Bạn không nên sử dụng câu trả lời của @ Galen.
doozMen

Câu trả lời:


462

Trên iPad, bộ điều khiển chế độ xem hoạt động sẽ được hiển thị dưới dạng popover bằng cách sử dụ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 một trong ba thuộc tính sau:

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

if ( [activityViewController respondsToSelector:@selector(popoverPresentationController)] ) { 
// iOS8
 activityViewController.popoverPresentationController.sourceView =
parentView;
 }

4
@Daljeet một cách đơn giản sẽ là đặt chế độ xem trong suốt 1x1 bất cứ nơi nào bạn muốn điểm của cửa sổ bật lên xuất hiện và sử dụng chế độ xem đó làm chế độ xem neo.
Mason Cloud

3
@Cảm ơn mmccomb, tôi đã áp dụng mã của bạn ở trên nó hoạt động tốt trên iOS 8, nhưng ứng dụng của tôi gặp sự cố trên iOS 7. Tôi đã đăng một vấn đề tương tự trên stackoverflow ở đây là liên kết: stackoverflow.com/questions/26034149/ Trợ giúp được đánh giá cao
Daljeet

48
@Daljeet Điều này sẽ sụp đổ trên iOS7 vì UIActivityControll không có thuộc tính popoverPftimeationContoder ở đó. Sử dụng mã này để làm cho nó hoạt động trên iOS8 và iOS7: if ( [activityViewController respondsToSelector:@selector(popoverPresentationController)] ) { // iOS8 activityViewController.popoverPresentationController.sourceView = _shareItem; }
blueblyn

4
trong iOS8, sourceRect không hoạt động với tôi. tôi đã phải tạo một souceView với chỉnh lưu đó, và nó hoạt động tốt.
Harris

10
@blueblyn Tại sao bạn không thêm đề xuất của mình dưới dạng chỉnh sửa vào câu trả lời. Nó khá dễ dàng để bỏ lỡ thông tin quan trọng của bạn trong những bình luận này.
Ayush Goel

203

Vấn đề tương tự xảy ra với dự án của tôi sau đó tôi tìm thấy giải pháp để mở UIActivityViewControlleriPad trong chúng ta phải sử dụngUIPopoverController

Đây là một mã để sử dụng nó trong cả iPhone và iPad:

//to attach the image and text with sharing 
UIImage *image=[UIImage imageNamed:@"giraffe.png"];
NSString *str=@"Image form My app";
NSArray *postItems=@[str,image];

UIActivityViewController *controller = [[UIActivityViewController alloc] initWithActivityItems:postItems applicationActivities:nil];

//if iPhone
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
    [self presentViewController:controller animated:YES completion:nil];
}
//if iPad
else {
    // Change Rect to position Popover
    UIPopoverController *popup = [[UIPopoverController alloc] initWithContentViewController:controller];
    [popup presentPopoverFromRect:CGRectMake(self.view.frame.size.width/2, self.view.frame.size.height/4, 0, 0)inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}

Đối với swift 4.2 / swift 5

func openShareDilog() {
    let text = "share text will goes here"

    // set up activity view controller
    let textToShare = [text]
    let activityViewController = UIActivityViewController(activityItems: textToShare, applicationActivities: nil)
    activityViewController.excludedActivityTypes = [.airDrop]

    if let popoverController = activityViewController.popoverPresentationController {
        popoverController.sourceRect = CGRect(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height / 2, width: 0, height: 0)
        popoverController.sourceView = self.view
        popoverController.permittedArrowDirections = UIPopoverArrowDirection(rawValue: 0)
    }

    self.present(activityViewController, animated: true, completion: nil)
}

5
Đối với cú pháp Swift "mới hơn", UIPopoverArrowDirectionAny phải là UIPopoverArrowDirection.Any và UIUserInterfaceIdiomPhone là UIUserInterfaceIdiom.Phone
EPage_Ed

1
Cảm ơn rất nhiều, điều này, kết hợp với các bình luận của EPage_Ed, hoạt động trên XCode 7 và SWIFT 2. Tôi đã nâng cấp nó.
Oliver Zhang

1
UIPopoverControll không dùng nữa với iOS 9.
cbartel

3
UIPopOverControll không được dùng nữa trong iOS 9.
Leena

1
Mọi người nên trả lời theo cách này vì nó dễ dàng điều hướng cho câu trả lời của ios hơn là nhanh chóng .. Cảm ơn người đàn ông bạn đã thực hiện ngày của tôi.
MBH

42

Tôi đã gặp vấn đề chính xác này gần đây (câu hỏi ban đầu) trong Swift 2.0, ở đâu UIActivityViewController hoạt động tốt cho iPhone, nhưng gây ra sự cố khi mô phỏng iPad.

Tôi chỉ muốn thêm vào chuỗi câu trả lời này ở đây rằng, ít nhất là trong Swift 2.0, bạn không cần một câu lệnh if. Bạn chỉ có thể làm cho các popoverPresentationControllertùy chọn.

Nói một cách nhanh chóng, câu trả lời được chấp nhận dường như nói rằng bạn có thể chỉ có một SourceView, chỉ là một sourceRect hoặc chỉ là một barButtonItem, nhưng theo tài liệu của Apple cho UIPopoverPftimeationContoder bạn cần một trong những điều sau đây:

  • barButtonItem
  • sourceView sourceRect

Ví dụ cụ thể mà tôi đang làm việc ở bên dưới, nơi tôi đang tạo một hàm có trong UIView(đối với sourceView và sourceRect) và String(hoạt động duy nhất của UIActivityViewContoder).

func presentActivityViewController(sourceView: UIView, activityItem: String ) {

    let activityViewController = UIActivityViewController(activityItems: [activityItem], applicationActivities: [])

    activityViewController.popoverPresentationController?.sourceView = sourceView
    activityViewController.popoverPresentationController?.sourceRect = sourceView.bounds

    self.presentViewController(activityViewController, animated: true, completion: nil)
}

Mã này hoạt động trên iPhone và iPad (và thậm chí cả tvOS tôi nghĩ) - nếu thiết bị không hỗ trợ popoverPresentationController , hai dòng mã đề cập đến nó về cơ bản bị bỏ qua.

Thật tuyệt khi tất cả những gì bạn cần làm để làm cho nó hoạt động cho iPad chỉ là thêm hai dòng mã hoặc chỉ một dòng nếu bạn đang sử dụng barButtonItem!


2
Giải pháp này có vẻ sạch sẽ hơn nhiều so với các giải pháp khác đang phải sử dụng respondsToSelectorhoặc UIUserInterfaceIdiom, cũng có vẻ phù hợp với Swift như một ngôn ngữ tốt hơn nhiều. Mặc dù respondsToSelectorcó vẻ như nó cần thiết cho iOS7, nhưng nếu sử dụng các phiên bản iOS mới hơn thì đây chắc chắn là cách tốt nhất.
Marcus

18

Tôi thấy rất nhiều người cứng mã hóa iPhone / iPad, v.v. trong khi sử dụng mã Swift.

Điều này là không cần thiết, bạn phải sử dụng các tính năng ngôn ngữ. Đoạn mã sau giả định rằng bạn sẽ sử dụng UIBarButtonItem và sẽ hoạt động trên cả iPhone và iPad.

@IBAction func share(sender: AnyObject) {
    let vc = UIActivityViewController(activityItems: ["hello"], applicationActivities: nil)
    vc.popoverPresentationController?.barButtonItem = sender as? UIBarButtonItem
    self.presentViewController(vc, animated: true, completion: nil)
 }

Lưu ý làm thế nào không có câu lệnh If hoặc bất kỳ điều điên rồ nào khác. Việc hủy tùy chọn sẽ không có trên iPhone, vì vậy dòng vc.popoverPresentationController?này sẽ không làm gì trên iPhone.


Điều đó sẽ trông như thế nào trong bối cảnh của hướng dẫn này: hackingwithswift.com/read/3/2/ trên
Dave Kliman

1
hướng dẫn không đề cập đến popoverPftimeationControll, do đó mã sẽ bị sập trên iPad iOS 9.x. Giải pháp sẽ là thêm vc.popoverPresentationController?.barButtonItem = navigationItem.rightBarButtonItemtrước khi bạn trình bày bộ điều khiển xem.
Martin Marconcini

chào Martin ... Tôi đã có một dòng trông như thế này, trong shareTapped(): vc.popoverPresentationController?.barButtonItem = sender as? UIBarButtonItemcả hai vẫn bị hỏng. Tôi đang làm gì sai? Trong khi tôi ở đây, làm thế nào để tôi tạo ra popover với các dịch vụ chia sẻ khác nhau như Facebook, twitter, v.v?
Dave Kliman

Và vụ tai nạn là gì? Bạn có thể muốn gửi câu hỏi của riêng bạn. Kiểm tra đó senderthực sự là một UIBarButtonItem. Kiểm tra xem vc không nil. Kiểm tra xem bạn có thể trình bày một ViewContoder không mà không nhìn vào sự cố / mã rất khó để nói. Các dịch vụ sẽ được tự động điền nếu người dùng đã cài đặt ứng dụng (trừ khi bạn quyết định loại trừ một số rõ ràng).
Martin Marconcini

1
@DaveKliman có, Xcode rất tệ khi nói đến IBOutlets và IBActions đã bị xóa / đổi tên và nó sẽ bị sập khi khởi động. Thực sự không có cách nào khác để làm điều đó trên iOS, bạn cần sử dụng Xcode và InterfaceBuilder. Bạn có thể làm rất nhiều thứ "trong mã" nhưng một số yêu cầu "kéo và thả". Apple muốn bạn sử dụng Storyboards và InterfaceBuilder càng nhiều càng tốt, vì vậy hãy làm quen với nó :)
Martin Marconcini

10

Giải pháp sử dụng Xamarin.iOS.

Trong ví dụ của tôi, tôi đang thực hiện chụp màn hình, tạo hình ảnh và cho phép người dùng chia sẻ hình ảnh. Cửa sổ bật lên trên iPad được đặt ở giữa màn hình.

var activityItems = new NSObject[] { image };
var excludedActivityTypes = new NSString[] {
    UIActivityType.PostToWeibo,
    UIActivityType.CopyToPasteboard,
    UIActivityType.AddToReadingList,
    UIActivityType.AssignToContact,
    UIActivityType.Print,
};
var activityViewController = new UIActivityViewController(activityItems, null);

//set subject line if email is used
var subject = new NSString("subject");
activityViewController.SetValueForKey(NSObject.FromObject("Goal Length"), subject);

activityViewController.ExcludedActivityTypes = excludedActivityTypes;
//configure for iPad, note if you do not your app will not pass app store review
if(null != activityViewController.PopoverPresentationController)
{
    activityViewController.PopoverPresentationController.SourceView = this.View;
    var frame = UIScreen.MainScreen.Bounds;
    frame.Height /= 2;
    activityViewController.PopoverPresentationController.SourceRect = frame;
}
this.PresentViewController(activityViewController, true, null);

Cảm ơn bạn rất nhiều :) làm việc với Xamarin Forms bằng Dịch vụ phụ thuộc
Ricardo Romo

7

Swift, iOS 9/10 (sau khi UIPopoverControll không dùng nữa)

let activityViewController = UIActivityViewController(activityItems: sharingItems, applicationActivities: nil)

    if UIDevice.currentDevice().userInterfaceIdiom == .Pad {

       if activityViewController.respondsToSelector(Selector("popoverPresentationController")) {
          activityViewController.popoverPresentationController?.sourceView = self.view
        }
    }

    self.presentViewController(activityViewController, animated: true, completion: nil)

3
Swift 3 không hỗ trợ điều này
Maksim Kniazev

5

Trong Swift để sửa lỗi này cho iPad, cách tốt nhất là làm như thế này tôi đã tìm thấy.

    let things = ["Things to share"]
    let avc = UIActivityViewController(activityItems:things, applicationActivities:nil)
    avc.setValue("Subject title", forKey: "subject")
    avc.completionWithItemsHandler = {
        (s: String!, ok: Bool, items: [AnyObject]!, err:NSError!) -> Void in
    }

    self.presentViewController(avc, animated:true, completion:nil)
    if let pop = avc.popoverPresentationController {
        let v = sender as! UIView // sender would be the button view tapped, but could be any view
        pop.sourceView = v
        pop.sourceRect = v.bounds
    }

Sử dụng câu trả lời @Galen. Không có kiểm tra thành ngữ khi nhắm mục tiêu iOS8 +
doozMen

5

Nếu bạn hiển thị UIActivityViewControllerkhi bạn nhấp vào UIBarButtonItemsử dụng mã sau đây:

activityViewController.popoverPresentationController?.barButtonItem = sender

Mặt khác, nếu bạn sử dụng điều khiển khác, ví dụ a UIButton, hãy sử dụng mã sau:

activityViewController.popoverPresentationController?.sourceView = sender
activityViewController.popoverPresentationController?.sourceRect = sender.bounds

Từ tài liệu đến UIPopoverPresentationController:

var barButtonItem: UIBarButtonItem? { get set }

Gán một giá trị cho thuộc tính này để neo popover vào mục nút thanh được chỉ định. Khi được trình bày, mũi tên của cửa sổ bật lên chỉ vào mục được chỉ định. Ngoài ra, bạn có thể chỉ định vị trí neo cho cửa sổ bật lên bằng cách sử dụng các thuộc tính sourceView và sourceRect.


4

Sửa lỗi cho Swift 2.0

    if UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.Phone {
        self.presentViewController(activityVC, animated: true, completion: nil)
    }
    else {
        let popup: UIPopoverController = UIPopoverController(contentViewController: activityVC)
        popup.presentPopoverFromRect(CGRectMake(self.view.frame.size.width / 2, self.view.frame.size.height / 4, 0, 0), inView: self.view, permittedArrowDirections: UIPopoverArrowDirection.Any, animated: true)
    }

2
UIPopoverControll đã không được chấp nhận.
LevinsonTechnology

1
Cảm ơn bạn!! Nó giúp tôi rất nhiều.
Oscar

4

Swift 3:

class func openShareActions(image: UIImage, vc: UIViewController) {
    let activityVC = UIActivityViewController(activityItems: [image], applicationActivities: nil)
    if UIDevice.current.userInterfaceIdiom == .pad {
        if activityVC.responds(to: #selector(getter: UIViewController.popoverPresentationController)) {
            activityVC.popoverPresentationController?.sourceView = vc.view
        }
    }
    vc.present(activityVC, animated: true, completion: nil)
}

3

Nhanh:

    let activityViewController = UIActivityViewController(activityItems: sharingItems, applicationActivities: nil)

    //if iPhone
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.Phone) {
        self.presentViewController(activityViewController, animated: true, completion: nil)
    } else { //if iPad
        // Change Rect to position Popover
        var popoverCntlr = UIPopoverController(contentViewController: activityViewController)
        popoverCntlr.presentPopoverFromRect(CGRectMake(self.view.frame.size.width/2, self.view.frame.size.height/4, 0, 0), inView: self.view, permittedArrowDirections: UIPopoverArrowDirection.Any, animated: true)

    }

2

Giải pháp cho Objective-C và với việc sử dụng UIPopoverPftimeationControll

    UIActivityViewController *controller = /*Init your Controller*/;
    //if iPhone
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
        [self presentViewController:controller animated:YES completion:nil];
    }
    //if iPad
    else {
        UIPopoverPresentationController* popOver = controller.popoverPresentationController
        if(popOver){
            popOver.sourceView = controller.view;
            popOver.sourceRect = CGRectMake(self.view.frame.size.width/2, self.view.frame.size.height/4, 0, 0);
            [self presentViewController:controller animated:YES completion:nil];
        }
    }

1

Tôi đã thử mã tiếp theo và nó hoạt động:

trước tiên hãy đặt một mục nút thanh trong Trình điều khiển xem của bạn sau đó tạo IBOutlet:

@property(weak,nonatomic)IBOutlet UIBarButtonItem *barButtonItem;

tiếp theo trong tệp .m: yourUIActivityViewController.popoverPresentationController.barButtonItem = self.barButtonItem;


1

swift = ios7 / ios8

let activityViewController = UIActivityViewController(activityItems: sharingItems, applicationActivities: nil)

//if iPhone
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.Phone) {
    // go on..
} else {
    //if iPad
    if activityViewController.respondsToSelector(Selector("popoverPresentationController")) {
        // on iOS8
        activityViewController.popoverPresentationController!.barButtonItem = self.shareButtonItem;
    }
}
self.presentViewController(activityViewController, animated: true, completion: nil)

0

Tôi đã tìm thấy giải pháp này Trước tiên, bộ điều khiển xem của bạn trình bày popover sẽ thực hiện <UIPopoverPresentationControllerDelegate>giao thức.

Tiếp theo, bạn sẽ cần thiết lập popoverPresentationControllerđại biểu của.

Thêm các chức năng này:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Assuming you've hooked this all up in a Storyboard with a popover presentation style
    if ([segue.identifier isEqualToString:@"showPopover"]) {
        UINavigationController *destNav = segue.destinationViewController;
        PopoverContentsViewController *vc = destNav.viewControllers.firstObject;

        // This is the important part
        UIPopoverPresentationController *popPC = destNav.popoverPresentationController;
        popPC.delegate = self;
    }
}

- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController: (UIPresentationController *)controller {
    return UIModalPresentationNone;
}

0

Trong swift 4 mã sau làm việc trong iphone và ipad. Theo tài liệu

Bạn có trách nhiệm trình bày và loại bỏ bộ điều khiển xem bằng các phương tiện thích hợp cho thành ngữ thiết bị đã cho. Trên iPad, bạn phải trình bày bộ điều khiển xem trong cửa sổ bật lên. Trên các thiết bị khác, bạn phải trình bày nó một cách vừa phải.

 let activityViewController = UIActivityViewController(activityItems: activityitems, applicationActivities: nil)

    if UIDevice.current.userInterfaceIdiom == .pad {

        if activityViewController.responds(to: #selector(getter: UIViewController.popoverPresentationController)) {
            activityViewController.popoverPresentationController?.sourceView = self.view
        }
    }

    self.present(activityViewController, animated: true, completion: nil)

0

Tôi đang sử dụng Swift 5. Tôi gặp vấn đề tương tự khi gặp sự cố khi nhấp vào "Nút chia sẻ" trong ứng dụng của mình trên iPad. Tìm thấy một giải pháp này. Bước 1: Thêm đối tượng "xem" (tìm kiếm "UIView" trong thư viện đối tượng) vào Main.storyboard. Bước 2: Tạo một @IBOutlet trong ViewControll.swift và gán bất kỳ tên nào (ví dụ: view1)

bước 3: thêm tên ở trên (ví dụ: view1) làm sourceView. đây là hành động "Nút chia sẻ" của tôi.

@IBAction func Share(_ sender: Any) {
    let activityVC = UIActivityViewController(activityItems: ["www.google.com"], applicationActivities: nil)
    activityVC.popoverPresentationController?.sourceView = view1

    self.present(activityVC, animated: true, completion: nil)


}

Tôi rất mới với swift và bị mắc kẹt trong điều này trong một tuần. hy vọng điều này sẽ giúp được ai đó. để chia sẻ giải pháp này.


Cảm ơn điều này đã làm việc!
Jnguyen22

-1

Đối với Swift 2.0. Tôi thấy rằng điều này hoạt động nếu bạn đang cố gắng neo popover vào nút chia sẻ trên iPad. Điều này giả định rằng bạn đã tạo một ổ cắm cho nút chia sẻ trong thanh công cụ của mình.

func share(sender: AnyObject) {
    let firstActivityItem = "test"

    let activityViewController = UIActivityViewController(activityItems: [firstActivityItem], applicationActivities: nil)

    if UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.Phone {
        self.presentViewController(activityViewController, animated: true, completion: nil)
    }
    else {            
        if activityViewController.respondsToSelector("popoverPresentationController") {
            activityViewController.popoverPresentationController!.barButtonItem = sender as? UIBarButtonItem
            self.presentViewController(activityViewController, animated: true, completion: nil)
        }

    }
}

-5

Hãy cẩn thận nếu bạn đang phát triển cho iPad bằng cách sử dụng swift, nó sẽ hoạt động tốt trong gỡ lỗi, nhưng sẽ bị sập khi phát hành. Để làm cho nó hoạt động với testFlight và AppStore, hãy tắt tối ưu hóa cho swift bằng cách sử dụng -noneđể phát hành.

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.