UIActivityViewController mogok di iOS 8 iPads

288

Saat ini saya sedang menguji aplikasi saya dengan Xcode 6 (Beta 6). UIActivityViewController berfungsi dengan baik dengan perangkat dan simulator iPhone tetapi macet dengan simulator dan perangkat iPad (iOS 8) dengan log berikut

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

Saya menggunakan kode berikut untuk iPhone dan iPad untuk iOS 7 dan juga 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];

Saya juga mengalami crash yang sama pada salah satu aplikasi saya yang lain. Bisakah Anda membimbing saya? apakah ada yang berubah dengan UIActivityViewController di iOS 8? Saya memeriksa tetapi saya tidak menemukan apa pun dalam hal ini

Bhumit Mehta
sumber
Jawaban tes di bawah untuk idiom. Anda harus menggunakan jawaban @ Galen yang tidak.
doozMen

Jawaban:

462

Di iPad, pengontrol tampilan aktivitas akan ditampilkan sebagai popover menggunakan UIPopoverPresentationController baru , ia mengharuskan Anda menentukan titik jangkar untuk presentasi popover menggunakan salah satu dari tiga properti berikut:

Untuk menentukan titik jangkar, Anda harus mendapatkan referensi ke UIPopoverPresentationController UIActivityController's dan mengatur salah satu properti sebagai berikut:

if ( [activityViewController respondsToSelector:@selector(popoverPresentationController)] ) { 
// iOS8
 activityViewController.popoverPresentationController.sourceView =
parentView;
 }
mmccomb
sumber
4
@ Daljeet cara sederhana adalah menempatkan tampilan transparan 1x1 di mana pun Anda ingin titik popover muncul dan menggunakannya sebagai tampilan jangkar.
Mason Cloud
3
@Terima kasih mmccomb, saya telah menerapkan kode Anda di atas berfungsi dengan baik di iOS 8, tetapi aplikasi saya mogok di iOS 7.Saya telah memposting masalah yang sama pada stackoverflow di sini adalah tautannya: stackoverflow.com/questions/26034149/… bantuan dihargai
Daljeet
48
@Daljeet Ini akan macet di iOS7 karena UIActivityController tidak memiliki popover propertiPresentationController di sana. Gunakan kode ini untuk membuatnya berfungsi di iOS8 dan iOS7: if ( [activityViewController respondsToSelector:@selector(popoverPresentationController)] ) { // iOS8 activityViewController.popoverPresentationController.sourceView = _shareItem; }
bluebamboo
4
di iOS8, sourceRect tidak berfungsi untuk saya. saya harus membuat souceView dengan kotak itu, dan itu berfungsi dengan baik.
Harris
10
@bluebamboo Mengapa Anda tidak menambahkan saran Anda sebagai edit pada jawabannya. Cukup mudah untuk melewatkan informasi penting Anda dalam komentar ini.
Ayush Goel
204

Masalah yang sama datang ke proyek saya maka saya menemukan solusi untuk membuka UIActivityViewControlleriPad yang harus kita gunakanUIPopoverController

Berikut adalah kode untuk menggunakannya di iPhone dan 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];
}

Untuk 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)
}
Hardik Thakkar
sumber
5
Untuk sintaks Swift "baru", UIPopoverArrowDirectionAny harus UIPopoverArrowDirection. Setiap dan UIUserInterfaceIdiomPhone adalah UIUserInterfaceIdiom.Phone
EPage_Ed
1
Terima kasih banyak, ini, dikombinasikan dengan komentar EPage_Ed, bekerja di XCode 7 dan SWIFT 2. Saya telah memutakhirkannya.
Oliver Zhang
1
UIPopoverController sudah tidak digunakan lagi dengan iOS 9.
cbartel
3
UIPopOverController sudah tidak digunakan lagi di iOS 9.
Leena
1
Semua orang harus menjawab seperti ini karena lebih mudah untuk menavigasi untuk jawaban ios daripada untuk cepat .. Terima kasih kawan, kamu telah membuat hari saya.
MBH
42

Saya menemukan masalah yang tepat baru-baru ini (pertanyaan asli) di Swift 2.0, di mana UIActivityViewControllerbekerja dengan baik untuk iPhone, tetapi menyebabkan crash ketika mensimulasikan iPads.

Saya hanya ingin menambahkan utas jawaban ini di sini bahwa, setidaknya di Swift 2.0, Anda tidak perlu pernyataan if. Anda bisa membuatnyapopoverPresentationController opsional.

Di samping itu, jawaban yang diterima tampaknya mengatakan bahwa Anda bisa saja memiliki sourceView, hanya sourceRect, atau barButtonItem, tetapi menurut dokumentasi Apple untuk UIPopoverPresentationController Anda memerlukan salah satu dari yang berikut:

  • barButtonItem
  • sourceView dan sourceRect

Contoh khusus yang saya kerjakan adalah di bawah ini, di mana saya membuat fungsi yang mengambil UIView(untuk sourceView dan sourceRect) dan String(satu-satunya activityItem item UIActivityViewController).

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

Kode ini berfungsi di iPhone dan iPad (dan bahkan TVOS saya pikir) - jika perangkat tidak mendukung popoverPresentationController, dua baris kode yang menyebutkannya pada dasarnya diabaikan.

Agak baik bahwa semua yang perlu Anda lakukan untuk membuatnya berfungsi untuk iPad hanya menambahkan dua baris kode, atau hanya satu jika Anda menggunakan barButtonItem!

Galen
sumber
2
Solusi ini tampaknya jauh lebih bersih daripada solusi lain yang harus digunakan respondsToSelectoratau UIUserInterfaceIdiom, juga tampaknya cocok dengan Swift sebagai bahasa yang jauh lebih baik. Meskipun respondsToSelectorsepertinya itu diperlukan untuk iOS7, jika menggunakan versi iOS yang lebih baru, ini sepertinya merupakan cara yang harus dilakukan.
Marcus
18

Saya melihat banyak orang meng-hardcoding iPhone / iPad dll. Saat menggunakan kode Swift.

Ini tidak diperlukan, Anda harus menggunakan fitur bahasa. Kode berikut mengasumsikan Anda akan menggunakan UIBarButtonItem dan akan bekerja pada kedua iPhone dan 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)
 }

Perhatikan bagaimana tidak ada pernyataan If atau hal gila lainnya. Bungkusan opsional akan nol pada iPhone, sehingga saluran vc.popoverPresentationController?tidak akan melakukan apa pun pada iPhone.

Martin Marconcini
sumber
bagaimana hal itu terlihat dalam konteks tutorial ini: hackingwithswift.com/read/3/2/…
Dave Kliman
1
tutorial tidak menyebutkan popoverPresentationController, sehingga kode akan macet di iPad iOS 9.x. Solusinya adalah menambahkan vc.popoverPresentationController?.barButtonItem = navigationItem.rightBarButtonItemsebelum Anda menampilkan controller tampilan.
Martin Marconcini
hai Martin ... Saya sudah memiliki garis yang terlihat seperti ini, di shareTapped(): vc.popoverPresentationController?.barButtonItem = sender as? UIBarButtonItemkeduanya masih macet. Apa yang saya lakukan salah? sementara saya di sini, bagaimana cara mengisi popover dengan berbagai layanan berbagi seperti Facebook, twitter, dll?
Dave Kliman
Dan apa kecelakaannya? Anda mungkin ingin memposting pertanyaan Anda sendiri. Periksa itu sendermemang a UIBarButtonItem. Pastikan vc tidak nil. Periksa apakah Anda dapat menyajikan ViewController ... tanpa melihat crash / kode yang sulit diketahui. Layanan akan secara otomatis terisi jika pengguna menginstal aplikasi (kecuali Anda secara eksplisit memutuskan untuk mengecualikan beberapa).
Martin Marconcini
1
@DaveKliman ya, Xcode sangat buruk ketika datang ke IBOutlets dan IBActions yang telah dihapus / diganti namanya dan akan macet saat startup. Sebenarnya tidak ada cara yang berbeda untuk melakukannya di iOS, Anda perlu menggunakan Xcode dan InterfaceBuilder. Anda dapat melakukan banyak hal "dalam kode" tetapi beberapa memerlukan "seret dan lepas". Apple ingin Anda menggunakan Storyboards dan InterfaceBuilder sebanyak mungkin ... jadi biasakanlah :)
Martin Marconcini
10

Solusi menggunakan Xamarin.iOS.

Dalam contoh saya, saya melakukan tangkapan layar, menghasilkan gambar, dan memungkinkan pengguna untuk berbagi gambar. Munculan di iPad diletakkan di tengah layar.

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);
ben
sumber
Terima kasih banyak :) bekerja dengan Formulir Xamarin menggunakan Layanan Ketergantungan
Ricardo Romo
7

Swift, iOS 9/10 (setelah UIPopoverController tidak digunakan lagi)

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)
MPaulo
sumber
3
Swift 3 tidak mendukung ini
Maksim Kniazev
5

Di Swift untuk memperbaiki ini untuk iPad, cara terbaik adalah melakukan seperti ini yang saya temukan.

    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
    }
Niklas
sumber
Gunakan jawaban @ Galen. Tidak ada pemeriksaan idiom saat menargetkan iOS8 +
doozMen
5

Jika Anda menunjukkan UIActivityViewControllerketika Anda mengklik UIBarButtonItemgunakan kode berikut:

activityViewController.popoverPresentationController?.barButtonItem = sender

Jika tidak, jika Anda menggunakan kontrol lain, misalnya a UIButton, gunakan kode berikut:

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

Dari dokumentasi ke UIPopoverPresentationController:

var barButtonItem: UIBarButtonItem? { get set }

Tetapkan nilai untuk properti ini untuk melabuhkan popover ke item tombol bar yang ditentukan. Saat disajikan, panah popover menunjuk ke item yang ditentukan. Atau, Anda dapat menentukan lokasi jangkar untuk popover menggunakan properti sourceView dan sourceRect.

DronPop
sumber
4

Perbaiki untuk 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)
    }
datayeah
sumber
2
UIPopoverController telah ditinggalkan.
LevinsonTeknologi
1
Terima kasih!! Itu banyak membantu saya.
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)
}
Daniel McLean
sumber
3

Cepat:

    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)

    }
kalpeshdeo
sumber
2

Solusi untuk Objective-C dan dengan menggunakan 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];
        }
    }
kurono267
sumber
1

Saya mencoba kode berikutnya dan berfungsi:

pertama-tama letakkan item tombol bilah di View Controller Anda lalu buat IBOutlet:

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

selanjutnya dalam file .m: yourUIActivityViewController.popoverPresentationController.barButtonItem = self.barButtonItem;

Mike
sumber
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)
ingconti
sumber
0

Saya menemukan solusi ini Pertama, view controller Anda yang menyajikan popover harus mengimplementasikan <UIPopoverPresentationControllerDelegate> protokol.

Selanjutnya, Anda harus mengatur popoverPresentationController delegasi.

Tambahkan fungsi-fungsi ini:

- (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;
}
Mongo db
sumber
0

Dalam cepat 4 kode berikut berfungsi di iphone dan ipad. Dokumentasi menurut

Merupakan tanggung jawab Anda untuk menampilkan dan mengabaikan pengontrol tampilan menggunakan cara yang sesuai untuk idiom perangkat yang diberikan. Di iPad, Anda harus menampilkan pengontrol tampilan dalam popover. Di perangkat lain, Anda harus mempresentasikannya secara modern.

 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)
Imran Khan
sumber
0

Saya menggunakan Swift 5. Saya memiliki masalah mogok yang sama ketika saya mengklik "tombol Bagikan" di aplikasi saya di iPad. Menemukan solusi ini. langkah 1: Tambahkan objek "lihat" (cari "UIView" di perpustakaan objek) ke Main.storyboard. langkah 2: Buat @IBOutlet di ViewController.swift dan tetapkan nama apa pun (misalnya: view1)

langkah 3: tambahkan nama di atas (misalnya: view1) sebagai sourceView. ini adalah tindakan "tombol Bagikan" saya.

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


}

Saya sangat baru untuk cepat dan terjebak dalam hal ini selama seminggu. Semoga ini bisa membantu seseorang. jadi bagikan solusi ini.

Dishara
sumber
Terima kasih, ini berhasil!
Jnguyen22
-1

Untuk Swift 2.0. Saya menemukan bahwa ini berfungsi jika Anda mencoba untuk melabuhkan popover ke tombol share di iPad. Ini mengasumsikan bahwa Anda telah membuat outlet untuk tombol bagikan di bilah alat Anda.

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

    }
}
iosdevlangley
sumber
-5

Hati-hati jika Anda mengembangkan untuk iPad menggunakan swift, ini akan berfungsi dengan baik dalam debug, tetapi akan macet saat dirilis. Untuk membuatnya bekerja dengan testFlight dan AppStore, nonaktifkan optimasi untuk penggunaan cepat -noneuntuk rilis.

ingconti
sumber