UIActivityViewController在iOS 8 iPad上崩溃


288

我目前正在使用Xcode 6(测试版6)测试我的应用。UIActivityViewController可以在iPhone设备和模拟器上正常运行,但在iPad模拟器和设备(iOS 8)上崩溃并显示以下日志

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

我正在将以下代码用于iOS 7和iOS 8的iPhone和iPad

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

我的另一个应用程序也发生了类似的崩溃。你能指导我吗?iOS 8中的UIActivityViewController有什么变化吗?我检查了一下,但没有找到任何东西


下面的答案测试这个成语。您应该使用@Galen的答案,而不是。
doozMen

Answers:


462

在iPad上,活动视图控制器将使用新的UIPopoverPresentationController显示为弹出窗口,它要求您使用以下三个属性之一为弹出窗口的显示指定锚点:

为了指定锚点,您将需要获取对UIActivityController的UIPopoverPresentationController的引用,并按如下所示设置属性之一:

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

4
@Daljeet一种简单的方法是将1x1透明视图放置在您希望弹出点出现的任何位置,并将其用作锚点视图。
梅森云

3
@Thanks mmccomb,我已经申请代码上面它适用于iOS 8罚款,但在iOS 7.我我的应用程序崩溃已经张贴在这里计算器一个同样的问题是链接:stackoverflow.com/questions/26034149/... 帮助表示赞赏
Daljeet 2014年

48
@Daljeet这将在iOS7上崩溃,因为UIActivityController那里没有属性popoverPresentationController。使用此代码可使其在iOS8和iOS7上运行: if ( [activityViewController respondsToSelector:@selector(popoverPresentationController)] ) { // iOS8 activityViewController.popoverPresentationController.sourceView = _shareItem; }
bluebamboo 2014年

4
在iOS8中,sourceRect对我不起作用。我必须使用该rect创建一个souceView,并且工作正常。
哈里斯

10
@bluebamboo为什么不添加建议作为对答案的编辑。在这些评论中很容易错过您的重要信息。
Ayush Goel 2015年

203

同样的问题出现在我的项目中,然后我找到了UIActivityViewController在iPad中打开我们必须使用的解决方案UIPopoverController

这是在iPhone和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];
}

快速4.2 /快速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
对于“较新的” Swift语法,UIPopoverArrowDirectionAny应该是UIPopoverArrowDirection.Any,UIUserInterfaceIdiomPhone是UIUserInterfaceIdiom.Phone
EPage_Ed

1
非常感谢,此内容与EPage_Ed的注释结合使用,可在XCode 7和SWIFT 2上使用。我对此表示赞同。
奥利弗·张

1
UIPopoverController不支持
iOS9。– cbartel

3
UIPopOverController在iOS 9中已弃用。–
Leena

1
每个人都应该以这种方式回答,因为导航ios答案比迅速找到要容易。
MBH

42

我最近在Swift 2.0中遇到了这个确切的问题(原始问题),在UIActivityViewControlleriPhone上可以正常工作,但是在模拟iPad时导致崩溃。

我只是想在这里添加答案,至少在Swift 2.0中,您不需要if语句。您可以将其popoverPresentationController设为可选。

顺便说一句,可接受的答案似乎是说您可以只有一个sourceView,一个sourceRect或一个barButtonItem,但是根据Apple的UIPopoverPresentationController文档,您需要以下之一:

  • barButtonItem
  • sourceView sourceRect

我正在处理的特定示例如下所示,其中我正在创建一个函数,该函数接受UIView(用于sourceView和sourceRect)和String(UIActivityViewController的唯一activityItem)。

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

此代码可在iPhone和iPad(甚至我认为是tvOS)上运行-如果设备不支持 popoverPresentationController,则提及该代码的两行代码实际上将被忽略。

有点不错,要使其在iPad上运行,您需要做的只是添加两行代码,如果使用barButtonItem,则只需添加一行代码!


2
这个解决方案似乎比必须使用respondsToSelector或的其他解决方案干净得多UIUserInterfaceIdiom,而且似乎更适合Swift作为一种语言。尽管respondsToSelector似乎对于iOS7来说是必需的,但是如果使用更新版本的iOS,这无疑是可行的方法。
Marcus

18

我看到许多人在使用Swift代码时对iPhone / iPad等进行硬编码。

这不是必需的,您必须使用语言功能。以下代码假定您将使用UIBarButtonItem并将在iPhone和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)
 }

请注意,没有if语句或任何其他疯狂的东西。可选的拆包在iPhone上为零,因此该行在vc.popoverPresentationController?iPhone上不起作用。


如何将这项看看在本教程的上下文:hackingwithswift.com/read/3/2/...
戴夫Kliman

1
本教程未提及popoverPresentationController,因此代码将在iPad iOS 9.x上崩溃。解决方案是vc.popoverPresentationController?.barButtonItem = navigationItem.rightBarButtonItem在显示视图控制器之前添加。
Martin Marconcini

嗨,马丁...我已经有一行这样的文字了shareTapped()vc.popoverPresentationController?.barButtonItem = sender as? UIBarButtonItem两者仍然崩溃。我究竟做错了什么?当我在这里时,如何使用不同的共享服务(例如Facebook,Twitter等)填充弹出窗口?
戴夫·克里曼

崩溃是什么?您可能要发布自己的问题。检查sender确实是一个UIBarButtonItem。检查vc是否不是nil。检查您是否可以显示ViewController…而无需查看崩溃/代码很难分辨。如果用户安装了应用程序,则将自动填充服务(除非您明确决定排除某些应用程序)。
Martin Marconcini

1
@DaveKliman是的,对于已被删除/重命名的IBOutlets和IBAction,Xcode非常糟糕,并且在启动时会崩溃。在iOS上确实没有什么不同的方法,您需要使用Xcode和InterfaceBuilder。您可以在“代码中”做很多事情,但有些事情需要“拖放”。苹果希望您尽可能多地使用Storyboards和InterfaceBuilder…因此请习惯使用它:)
Martin Marconcini

10

使用Xamarin.iOS的解决方案。

在我的示例中,我正在截屏,生成图像并允许用户共享图像。iPad上的弹出窗口位于屏幕中间。

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

非常感谢您:)使用依赖服务与Xamarin Forms一起使用
Ricardo Romo

7

Swift,iOS 9/10(不建议使用UIPopoverController之后)

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不支持此功能
Maksim Kniazev

5

在Swift中为iPad修复此问题,最好的方法就是这样做。

    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
    }

使用@Galen答案。针对iOS8 +时没有成语检查的习惯
doozMen

4

修复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
UIPopoverController已弃用。
LevinsonTechnologies

1
谢谢!!它帮助了我很多。
奥斯卡

4

斯威夫特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)
}

4

如果UIActivityViewController单击时显示,请UIBarButtonItem使用以下代码:

activityViewController.popoverPresentationController?.barButtonItem = sender

否则,如果您使用其他控件(例如a)UIButton,请使用以下代码:

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

从文档到UIPopoverPresentationController

var barButtonItem: UIBarButtonItem? { get set }

为该属性分配一个值,以将弹出框锚定到指定的条形按钮项。出现时,弹出框的箭头指向指定的项目。或者,您可以使用sourceView和sourceRect属性指定弹出框的锚点位置。


3

迅速:

    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

适用于Objective-C并使用UIPopoverPresentationController的解决方案

    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

我尝试了下一个代码,它的工作原理是:

首先在您的View Controller中放置一个条形按钮项,然后创建一个IBOutlet:

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

.m文件中的下一个: yourUIActivityViewController.popoverPresentationController.barButtonItem = self.barButtonItem;


1

迅速= 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

我找到了这个解决方案首先,您的呈现弹出框的视图控制器应实现 <UIPopoverPresentationControllerDelegate>协议。

接下来,您需要设置 popoverPresentationController的代表。

添加以下功能:

- (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

在iphone和ipad中运行的以下4个代码中。根据文件

您有责任使用给定设备惯用法的适当方法来展示和关闭视图控制器。在iPad上,必须在弹出窗口中显示视图控制器。在其他设备上,您必须以模态显示。

 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

我使用的是Swift5。当我在iPad上的应用程序中单击“共享按钮”时,出现了同样的崩溃问题。找到了这个解决方案。步骤1:将“视图”对象(在对象库中搜索“ UIView”)添加到Main.storyboard。步骤2:在ViewController.swift中创建一个@IBOutlet并分配任何名称(例如:view1)

步骤3:将上述名称(例如:view1)添加为sourceView。这是我的“共享按钮”操作。

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


}

我是新手,很快就坚持了一周。希望这会帮助某人。所以分享这个解决方案。


谢谢你的工作!
Jnguyen22

-1

对于Swift 2.0。我发现,如果您尝试将弹出框锚定到iPad上的“共享”按钮,则此方法有效。假设您已经在工具栏中为共享按钮创建了一个出口。

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

如果您使用swift为iPad开发,请小心,它在调试中可以正常工作,但在发行时会崩溃。为了使其能够与testFlight和AppStore一起使用,请禁用针对swift use -nonefor release的优化。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.