UIActivityViewController se bloquea en iPads con iOS 8
objective-c ios8 (19)
Actualmente estoy probando mi aplicación con Xcode 6 (Beta 6). UIActivityViewController funciona bien con dispositivos y simuladores de iPhone, pero se bloquea con simuladores y dispositivos de iPad (iOS 8) con los siguientes registros
Terminating app due to uncaught exception ''NSGenericException'',
reason: ''UIPopoverPresentationController
(<_UIAlertControllerActionSheetRegularPresentationController: 0x7fc7a874bd90>)
should have a non-nil sourceView or barButtonItem set before the presentation occurs.
Estoy usando el siguiente código para iPhone y iPad tanto para iOS 7 como para 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];
Estoy teniendo un bloqueo similar en una de mis otras aplicaciones también. ¿Puedes por favor guiarme? ¿Ha cambiado algo con UIActivityViewController en iOS 8? Lo comprobé pero no encontré nada sobre esto
Arreglo para 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)
}
El mismo problema vino a mi proyecto, entonces encontré la solución de que para abrir
UIActivityViewController
en iPad tenemos que usar
UIPopoverController
Aquí hay un código para usarlo tanto en iPhone como en 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];
}
Para 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)
}
En Swift para arreglar esto para iPad, la mejor manera es hacer lo que encontré.
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
}
En iPad, el controlador de vista de actividad se mostrará como un popover usando el nuevo UIPopoverPresentationController , requiere que especifique un punto de anclaje para la presentación del popover usando una de las tres propiedades siguientes:
Para especificar el punto de anclaje, deberá obtener una referencia al UIPopoverPresentationController de UIActivityController y establecer una de las propiedades de la siguiente manera:
if ( [activityViewController respondsToSelector:@selector(popoverPresentationController)] ) {
// iOS8
activityViewController.popoverPresentationController.sourceView =
parentView;
}
En swift 4 el siguiente código funciona en iphone y ipad. De acuerdo con la documentación
Es su responsabilidad presentar y descartar el controlador de vista utilizando los medios apropiados para el idioma del dispositivo dado. En iPad, debe presentar el controlador de vista en un popover. En otros dispositivos, debe presentarlo modalmente.
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)
Encontré esta solución En primer lugar, su controlador de vista que presenta el popover debe implementar el protocolo
<UIPopoverPresentationControllerDelegate>
.
A continuación, deberá configurar el delegado de
popoverPresentationController
.
Agregue estas funciones:
- (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;
}
Intenté el siguiente código y funciona:
primero coloque un elemento de botón de barra en su controlador de vista y luego cree un IBOutlet:
@property(weak,nonatomic)IBOutlet UIBarButtonItem *barButtonItem;
siguiente en el archivo .m:
yourUIActivityViewController.popoverPresentationController.barButtonItem = self.barButtonItem;
Me encontré con este problema exacto recientemente (la pregunta original) en Swift 2.0, donde
UIActivityViewController
funcionó bien para iPhones, pero causó fallas al simular iPads.
Solo quiero agregar a este hilo de respuestas aquí que, al menos en Swift 2.0, no necesita una declaración if.
Simplemente puede hacer que
popoverPresentationController
opcional.
Como comentario rápido, la respuesta aceptada parece estar diciendo que podría tener solo sourceView, solo sourceRect o simplemente barButtonItem, pero de acuerdo con la documentación de Apple para UIPopoverPresentationController , necesita uno de los siguientes:
- barButtonItem
- sourceView y sourceRect
El ejemplo particular en el que estaba trabajando es el siguiente, donde estoy creando una función que toma una
UIView
(para sourceView y sourceRect) y
String
(el único ActivityItem de 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)
}
Este código funciona en iPhone y iPad (e incluso en tvOS, creo): si el dispositivo no admite
popoverPresentationController
, las dos líneas de código que lo mencionan se ignoran esencialmente.
¡Qué bueno que todo lo que necesita hacer para que funcione para iPads es simplemente agregar dos líneas de código, o solo una si está usando un barButtonItem!
Para Swift 2.0. Descubrí que esto funciona si estás tratando de anclar el popover a un botón de compartir en iPad. Esto supone que ha creado una salida para el botón compartir en su barra de herramientas.
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)
}
}
}
Rápido:
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)
}
Respuesta de Hardik Thakkar en Swift 5 , XCode 11 .
let quoteText = "Life is 10% what happens to you and 90% how you react to it."
let activityViewController = UIActivityViewController(activityItems: [quoteText], applicationActivities: nil)
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)
}
UIApplication.topViewController?.present(activityViewController, animated: true, completion: nil)
Si muestra
UIActivityViewController
cuando hace clic en un
UIBarButtonItem
use el siguiente código:
activityViewController.popoverPresentationController?.barButtonItem = sender
De lo contrario, si usa otro control, por ejemplo, un
UIButton
, use el siguiente código:
activityViewController.popoverPresentationController?.sourceView = sender
activityViewController.popoverPresentationController?.sourceRect = sender.bounds
De la documentación al
UIPopoverPresentationController
:
var barButtonItem: UIBarButtonItem? { get set }
Asigne un valor a esta propiedad para anclar el popover al elemento de botón de barra especificado. Cuando se presenta, la flecha del popover apunta al elemento especificado. Alternativamente, puede especificar la ubicación del ancla para el popover usando las propiedades sourceView y sourceRect.
Solución para Objective-C y con uso 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];
}
}
Solución usando Xamarin.iOS.
En mi ejemplo, estoy haciendo una captura de pantalla, produciendo una imagen y permitiendo que el usuario comparta la imagen. La ventana emergente en el iPad se coloca en el medio de la pantalla.
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);
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)
}
Swift, iOS 9/10 (después de que UIPopoverController en desuso)
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)
Tenga cuidado si está desarrollando para iPad usando swift, funcionará bien en la depuración, pero se bloqueará en el lanzamiento.
Para que funcione con testFlight y AppStore, desactive la optimización para
-none
usando
-none
para su lanzamiento.
Veo a muchas personas codificando iPhone / iPad, etc., mientras usan el código Swift.
Esto no es necesario, debe utilizar las funciones del idioma. El siguiente código supone que usará un UIBarButtonItem y funcionará tanto en iPhone como en 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)
}
Observe cómo no hay declaraciones If o cualquier otra cosa loca.
El desempaquetado opcional será nulo en el iPhone, por lo que la línea
vc.popoverPresentationController?
no hará nada en iPhones.
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)