iOS 8 iPadでクラッシュするUIActivityViewController


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

他の1つのアプリでも同様のクラッシュが発生します。案内してもらえますか?iOS 8のUIActivityViewControllerで何か変更されましたか?チェックしましたが、何も見つかりませんでした


以下の答えはイディオムをテストします。使用しない@Galenの回答を使用する必要があります。
doozMen

回答:


462

iPadでは、アクティビティビューコントローラは新しいUIPopoverPresentationControllerを使用してポップオーバーとして表示されます。次の3つのプロパティのいずれかを使用して、ポップオーバーのプレゼンテーションのアンカーポイントを指定する必要があります。

アンカーポイントを指定するには、UIActivityControllerのUIPopoverPresentationControllerへの参照を取得し、次のようにいずれかのプロパティを設定する必要があります。

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

4
@Daljeetの簡単な方法は、ポップオーバーのポイントを表示したい場所に1x1の透明なビューを配置し、それをアンカービューとして使用することです。
メイソンクラウド

3
それはiOSの8に罰金を作品の上@Thanks mmccombは、私はあなたのコードを適用しているが、iOSの7.Iに私のアプリがクラッシュした場合、ここでStackOverflowの上で同じ問題を掲載しているリンクです:stackoverflow.com/questions/26034149/...の ヘルプは高く評価され
Daljeet 2014

48
@Daljeet UIActivityControllerにはpopoverPresentationControllerプロパティがないため、iOS7ではクラッシュします。それはiOS8とiOS7に動作させるために、このコードを使用します if ( [activityViewController respondsToSelector:@selector(popoverPresentationController)] ) { // iOS8 activityViewController.popoverPresentationController.sourceView = _shareItem; }
bluebamboo

4
iOS8では、sourceRectが機能しませんでした。私はそのrectでsouceViewを作成する必要があり、それはうまくいきました。
Harris

10
@bluebamboo回答の編集として提案を追加してみませんか。これらのコメントであなたの重要な情報を見逃すのはかなり簡単です。
Ayush Goel

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

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
「新しい」Swift構文の場合、UIPopoverArrowDirectionAnyはUIPopoverArrowDirection.Anyである必要があり、UIUserInterfaceIdiomPhoneはUIUserInterfaceIdiom.Phoneです
EPage_Ed

1
おかげで、これはEPage_Edのコメントと組み合わせて、XCode 7およびSWIFT 2で動作します。私はそれを支持しました。
Oliver Zhang、

1
UIPopoverControllerは、iOS 9で推奨されていません
cbartel

3
UIPopOverControllerは、iOS 9に廃止されました
レーナ

1
すばやく答えるよりもiosの答えを探す方が簡単なので、誰もがこのように答える必要があります。
MBH、2016

42

私は最近、この正確な問題(元の質問)をSwift 2.0で発生していました。 UIActivityViewController発生しました。Swift2.0では、iPhoneでは正常に機能しましたが、iPadをシミュレートするとクラッシュしました。

この回答のスレッドに追加したいのは、少なくともSwift 2.0では、ifステートメントは必要ないということです。あなたはちょうど作ることができますpopoverPresentationControllerオプションにです。

手短に言えば、受け入れられた答えは、sourceView、sourceRect、またはbarButtonItemのみを使用できるということですが、UIPopoverPresentationController に関するAppleのドキュメントによると、次のいずれかが必要です。

  • 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それを記述する2行のコードは基本的に無視されます。

iPadで機能させるために必要なのは、2行のコードを追加するか、barButtonItemを使用している場合は1行だけでいいのです。


2
このソリューションは、respondsToSelectorまたはを使用する必要のある他のソリューションよりもはるかにクリーンであるように見えます。またUIUserInterfaceIdiom、Swiftを言語としてはるかに優れているようです。が、respondsToSelectorこれは間違いなく行く方法であると思われるのiOSの新しいバージョンを使用している場合、それは、iOS7のために必要だように思えます。
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ではnilになるため、この行vc.popoverPresentationController?はiPhoneでは何もしません。


どのようにでしょう、このチュートリアルのコンテキストでその表情:hackingwithswift.com/read/3/2/...
デイブKliman

1
このチュートリアルではpopoverPresentationControllerについて言及していないため、そのコードはiPad iOS 9.xでクラッシュします。解決策はvc.popoverPresentationController?.barButtonItem = navigationItem.rightBarButtonItem、ビューコントローラーを表示する前に追加することです。
Martin Marconcini、2016

こんにちはマーティン...私はすでにこのようなラインを持っていますshareTapped()vc.popoverPresentationController?.barButtonItem = sender as? UIBarButtonItem両方がまだクラッシュしました。何が悪いのですか?ここにいる間、ポップオーバーにFacebook、Twitterなどのさまざまな共有サービスをどのように入力しますか?
Dave Kliman 2016年

そして、クラッシュは何ですか?あなた自身の質問を投稿したいかもしれません。それsenderが本当にであることを確認してくださいUIBarButtonItem。vcがでないことを確認してくださいnil。クラッシュ/コードを見ずにViewController…を提示できることを確認してください。ユーザーがアプリをインストールしている場合、サービスは自動的に入力されます(明示的に一部を除外することを決定した場合を除く)。
Martin Marconcini 2016年

1
@DaveKlimanはい、削除/名前変更されたIBOutletsとIBActionsに関しては、Xcodeは非常に悪く、起動時にクラッシュします。iOSでそれを行うための別の方法は実際にはありません。XcodeとInterfaceBuilderを使用する必要があります。「コード内」で多くのことを実行できますが、「ドラッグアンドドロップ」が必要なものもあります。Appleは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フォームで動作します
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

5

UIActivityViewControllerクリックしたときに表示される場合UIBarButtonItemは、次のコードを使用します。

activityViewController.popoverPresentationController?.barButtonItem = sender

それ以外の場合、たとえばのような別のコントロールを使用する場合UIButtonは、次のコードを使用します。

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

ドキュメントからUIPopoverPresentationController

var barButtonItem: UIBarButtonItem? { get set }

このプロパティに値を割り当てて、ポップオーバーを指定したバーボタン項目に固定します。提示されると、ポップオーバーの矢印は指定されたアイテムを指します。または、sourceViewプロパティとsourceRectプロパティを使用して、ポップオーバーのアンカーの場所を指定することもできます。


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

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

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

私はこの解決策を見つけましたまず、ポップオーバーを表示しているビューコントローラーが<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

Swift 4では、iPhoneとiPadで次のコードが機能しています。ドキュメントによると

指定されたデバイスイディオムに適切な手段を使用して、ビューコントローラーを提示および終了するのはユーザーの責任です。iPadでは、View Controllerをポップオーバーで提示する必要があります。他のデバイスでは、モーダルで提示する必要があります。

 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

Swift 5を使用しています。iPadのアプリで[共有]ボタンをクリックすると、同じ問題が発生しました。このソリューションを見つけました。手順1:Main.storyboardに「ビュー」オブジェクト(オブジェクトライブラリで「UIView」を検索)を追加します。ステップ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に非常に慣れておらず、これに1週間も行き詰っていました。これが誰かを助けることを願っています。このソリューションを共有します。


これはうまくいきました!
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で動作するようにするには-none、リリースに使用する迅速な最適化を無効にします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.