Ошибка UIActivityViewController на iOS 8 iPads

В настоящее время я тестирую свое приложение с помощью 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.

Я использую следующий код для iPhone и iPad как для iOS 7, так и для 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];

Я получаю подобный сбой в одном другом приложении. Можете ли вы направить меня? что-то изменилось с помощью UIActivityViewController в iOS 8? Я проверил, но я ничего не нашел на этом

Ответ 1

На iPad контроллер активности будет отображаться как popover, используя новый UIPopoverPresentationController, для этого требуется указать опорную точку для представления popover используя одно из трех следующих свойств:

Чтобы указать опорную точку, вам нужно будет получить ссылку на UIActivityController UIPopoverPresentationController и установить одно из следующих свойств:

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

Ответ 2

Та же проблема пришла в мой проект, тогда я нашел решение, чтобы открыть UIActivityViewController в iPad, мы должны использовать UIPopoverController

Вот код для использования в iPhone и iPad

//to attach the image and text with sharing 
UIImage *image=[UIImage imageNamed:@"giraffe.png"];
NSString *[email protected]"Image form My app";
NSArray *[email protected][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)
}

Ответ 3

Недавно я столкнулся с этой точной проблемой (исходный вопрос) в Swift 2.0, где UIActivityViewController отлично работал для iPhone, но вызвал сбои при моделировании iPads.

Я просто хочу добавить к этой теме ответов здесь, что, по крайней мере, в Swift 2.0 вам не нужен оператор if. Вы можете просто сделать popoverPresentationController опциональным.

Как бы то ни было, принятый ответ, похоже, говорит, что у вас может быть только sourceView, просто sourceRect или просто barButtonItem, но согласно документации Apple для UIPopoverPresentationController вам нужно одно из следующего:

  • barButtonItem
  • sourceView и sourceRect

Ниже приведен конкретный пример, в котором я создаю функцию, которая принимает UIView (для sourceView и sourceRect) и String (единственный объект активности 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)
}

Этот код работает на iPhone и iPad (и даже я думаю, что tvOS) - если устройство не поддерживает popoverPresentationController, две строки кода, которые упоминают его, по существу игнорируются.

Приятно, что все, что вам нужно сделать, чтобы заставить его работать для iPads, - это просто добавить две строки кода или только одну, если вы используете barButtonItem!

Ответ 4

Я вижу много людей hardcoding iPhone/iPad и т.д., используя код Swift.

Это не нужно, вам нужно использовать функции языка. Следующий код предполагает, что вы будете использовать 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.

Ответ 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);

Ответ 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)

Ответ 7

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

Ответ 8

Исправить для 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)
    }

Ответ 9

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

Ответ 10

Swift:

    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)

    }

Ответ 11

Если вы показываете UIActivityViewController при нажатии на UIBarButtonItem используйте следующий код:

activityViewController.popoverPresentationController?.barButtonItem = sender

В противном случае, если вы используете другой элемент управления, например UIButton, используйте следующий код:

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

Из документации к UIPopoverPresentationController:

var barButtonItem: UIBarButtonItem? { get set }

Присвойте значение этому свойству, чтобы привязать всплывающее окно к указанному элементу панели кнопок. Когда представлена, стрелка всплывающих окон указывает на указанный элемент. Кроме того, вы можете указать место привязки для всплывающего окна, используя свойства sourceView и sourceRect.

Ответ 12

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)

Ответ 13

Решение для 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];
        }
    }

Ответ 14

Я нашел это решение Во-первых, ваш контроллер представления, представляющий popover, должен реализовать протокол <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;
}

Ответ 15

Я попробовал следующий код, и он работает:

сначала поместите элемент кнопки панели в свой контроллер просмотра затем создайте IBOutlet:

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

Далее в файле .m: yourUIActivityViewController.popoverPresentationController.barButtonItem = self.barButtonItem;

Ответ 16

В быстрый 4 следующий код работает в iphone и ipad. Согласно документации

Вы несете ответственность за представление и отклонение контроллера вида, используя соответствующие средства для данного идиома устройства. На iPad вы должны представить контроллер представления в popover. На других устройствах вы должны представить его в текстовом формате.

 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)

Ответ 17

Для Swift 2.0. Я обнаружил, что это работает, если вы пытаетесь привязать popover к кнопке share на 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)
        }

    }
}

Ответ 18

Будьте осторожны, если вы разрабатываете для iPad с помощью swift, он отлично работает при отладке, но будет аварийно завершен. Чтобы заставить его работать с testFlight и AppStore, отключите оптимизацию для быстрого использования -none для выпуска.