【问题标题】:UIActivityViewController crashing on iOS 8 iPadsUIActivityViewController 在 iOS 8 iPad 上崩溃
【发布时间】:2014-10-27 22:31:23
【问题描述】:

我目前正在使用 Xcode 6(Beta 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's answer that doesn't.

标签: ios objective-c swift uiactivityviewcontroller


【解决方案1】:

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

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

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

【讨论】:

  • @Daljeet 一种简单的方法是将 1x1 透明视图放置在您希望弹出框出现的任何位置,并将其用作锚视图。
  • @Thanks mmccomb,我在上面应用了您的代码,它在 iOS 8 上运行良好,但我的应用程序在 iOS 7 上崩溃。我在 stackoverflow 上发布了同样的问题,链接如下:stackoverflow.com/questions/26034149/… help赞赏
  • @Daljeet 这将在 iOS7 上崩溃,因为 UIActivityController 那里没有属性 popoverPresentationController。使用此代码使其适用于 iOS8 和 iOS7:if ( [activityViewController respondsToSelector:@selector(popoverPresentationController)] ) { // iOS8 activityViewController.popoverPresentationController.sourceView = _shareItem; }
  • 在 iOS8 中,sourceRect 对我不起作用。我必须用那个矩形创建一个 souceView,而且效果很好。
  • @bluebamboo 为什么不将您的建议添加为答案的编辑。在这些 cmets 中很容易错过您的重要信息。
【解决方案2】:

我的项目也遇到了同样的问题,然后我找到了在 iPad 中打开 UIActivityViewController 的解决方案,我们必须使用 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];
}

适用于 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)
}

【讨论】:

  • 对于“较新”的 Swift 语法,UIPopoverArrowDirectionAny 应该是 UIPopoverArrowDirection.Any 并且 UIUserInterfaceIdiomPhone 是 UIUserInterfaceIdiom.Phone
  • 非常感谢,这与 EPage_Ed 的 cmets 相结合,适用于 XCode 7 和 SWIFT 2。我已赞成。
  • UIPopoverController 在 iOS 9 中已弃用。
  • UIPopOverController 在 iOS 9 中已弃用。
  • 每个人都应该这样回答,因为 ios 答案比 swift 更容易导航。谢谢你让我开心。
【解决方案3】:

我最近在 Swift 2.0 中遇到了这个确切的问题(原始问题),UIActivityViewController 在 iPhone 上运行良好,但在模拟 iPad 时导致崩溃。

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

顺便说一句,接受的答案似乎是说您可能只有一个 sourceView、一个 sourceRect 或一个 barButtonItem,但根据Apple's documentation for 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,则提及它的两行代码基本上会被忽略。

您只需添加两行代码,如果您使用的是 barButtonItem,您只需添加两行代码就可以让它在 iPad 上运行,这真是太好了!

【讨论】:

  • 这个解决方案似乎比必须使用respondsToSelectorUIUserInterfaceIdiom 的其他解决方案更干净,而且它似乎更适合Swift 作为一种语言。尽管respondsToSelector 似乎对于 iOS7 来说是必需的,但如果使用更新版本的 iOS,这绝对是可行的方法。
【解决方案4】:

我看到很多人在使用 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/…
  • 教程没有提到popoverPresentationController,所以代码会在iPad iOS 9.x 上崩溃。解决方案是在呈现视图控制器之前添加 vc.popoverPresentationController?.barButtonItem = navigationItem.rightBarButtonItem
  • 嗨马丁...我已经有一条看起来像这样的线,在shareTapped():vc.popoverPresentationController?.barButtonItem = sender as? UIBarButtonItem 仍然崩溃。我究竟做错了什么?当我在这里时,如何使用 Facebook、Twitter 等不同的共享服务填充弹出框?
  • 什么是崩溃?您可能想发布自己的问题。检查sender 确实是UIBarButtonItem。检查 vc 是否不是nil。检查你是否可以呈现一个 ViewController……不看崩溃/代码很难说。如果用户安装了应用程序,服务将自动填充(除非您明确决定排除某些服务)。
  • @DaveKliman 是的,Xcode 对于已删除/重命名的 IBOutlets 和 IBActions 非常糟糕,它会在启动时崩溃。在 iOS 上真的没有什么不同的方法,你需要使用 Xcode 和 InterfaceBuilder。您可以“在代码中”做很多事情,但有些需要“拖放”。 Apple 希望你尽可能多地使用 Storyboards 和 InterfaceBuilder……所以要习惯它:)
【解决方案5】:

使用 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 表单一起工作
【解决方案6】:

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)

【讨论】:

  • Swift 3 不支持这个
【解决方案7】:

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

activityViewController.popoverPresentationController?.barButtonItem = sender

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

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

从文档到UIPopoverPresentationController

var barButtonItem: UIBarButtonItem? { get set }

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

【讨论】:

    【解决方案8】:

    在 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+ 时没有 niet 成语检查
    【解决方案9】:

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

    【讨论】:

      【解决方案10】:

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

      【讨论】:

      • UIPopoverController 已被弃用。
      • 谢谢!!这对我帮助很大。
      【解决方案11】:

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

      【讨论】:

        【解决方案12】:

        SwiftUI 版本

        func presentActivityView(items: [Any]){
            let activityController = UIActivityViewController(activityItems: items, applicationActivities: nil)
            if UIDevice.current.userInterfaceIdiom == .pad{
                activityController.popoverPresentationController?.sourceView = UIApplication.shared.windows.first
                activityController.popoverPresentationController?.sourceRect = CGRect(x:  UIScreen.main.bounds.width / 3, y:  UIScreen.main.bounds.height / 1.5, width: 400, height: 400)
            }
            UIApplication.shared.windows.first?.rootViewController?.present(activityController, animated: true, completion: nil)
        }
        

        【讨论】:

        • 它正在工作,但在范围内找不到“ScreenSize”,这就是使用 UIScreen.main.bounds.height 和 width 的原因。
        • 哦!我已经忘记了。 ScreenSize 是我用于调整大小的自定义类,但它非常简单。 ScreenSize.width 等于 UIScreen.main.bounds.height。我创建了该类以使其更清晰和更短。我会尽快重新安排答案
        【解决方案13】:

        斯威夫特:

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

        【讨论】:

          【解决方案14】:

          在 swift 4 中,以下代码在 iphone 和 ipad 中运行。 根据文档

          您有责任使用给定设备惯用语的适当方法来呈现和关闭视图控制器。 在 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)
          

          【讨论】:

            【解决方案15】:

            我尝试了下一个代码,它可以工作:

            首先在您的视图控制器中放置一个条形按钮项 然后创建一个 IBOutlet:

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

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

            【讨论】:

              【解决方案16】:

              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)
              

              【讨论】:

                【解决方案17】:

                我正在使用 Swift 5。 当我在 iPad 上的应用程序中单击“共享按钮”时,我遇到了同样的崩溃问题。找到了这个解决方案。 第 1 步:将“view”对象(在对象库中搜索“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)
                
                
                }
                

                我对 swift 很陌生,并且在这个问题上停留了一周。希望这会对某人有所帮助。所以分享这个解决方案。

                【讨论】:

                  【解决方案18】:

                  我找到了这个解决方案 首先,呈现弹出框的视图控制器应该实现&lt;UIPopoverPresentationControllerDelegate&gt; 协议。

                  接下来,您需要设置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;
                  }
                  

                  【讨论】:

                    【解决方案19】:

                    最好的解决这个问题

                    let urlstring = "https://apps.apple.com/ae/app/"
                    let text = "some text for your app"
                    let url = NSURL(string: urlstring)
                    let textToShare = [url!,text] as [Any]
                    let activityViewController = UIActivityViewController(activityItems: textToShare as [Any], applicationActivities: nil)
                        
                    activityViewController.excludedActivityTypes = [ UIActivity.ActivityType.airDrop, UIActivity.ActivityType.postToFacebook ,UIActivity.ActivityType.postToFlickr,UIActivity.ActivityType.postToTwitter,UIActivity.ActivityType.postToVimeo,UIActivity.ActivityType.mail,UIActivity.ActivityType.addToReadingList]
                    
                    activityViewController.popoverPresentationController?.sourceView = self.view
                    activityViewController.popoverPresentationController?.sourceRect = view.bounds
                    activityViewController.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection.down
                    UIApplication.shared.windows.first?.rootViewController?.present(activityViewController, animated: true, completion: nil)
                    

                    【讨论】:

                      【解决方案20】:

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

                      【讨论】:

                        【解决方案21】:

                        如果您正在使用 swift 为 iPad 开发,请小心,它在调试时可以正常工作,但在发布时会崩溃。为了使其与 testFlight 和 AppStore 一起使用,请使用 -none 禁用 swift 优化以进行发布。

                        【讨论】:

                          猜你喜欢
                          • 2015-04-17
                          • 2015-06-15
                          • 2016-02-29
                          • 1970-01-01
                          • 2013-09-27
                          • 1970-01-01
                          • 2015-04-01
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多