Ошибка 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
для выпуска.