List item
Using Swift for an app that runs in iOS 8, I need to write a completion handler for the UIActivityViewController to capture the results of which "share" method a user selected.
This is a snippet of the code I have so far. My question is how to I set the avc.completionWithItemsHandler? I'm sure it's simple, but I don't see it.
var activityItems = NSMutableArray()
activityItems.addObject("Email or text for 'share' goes here")
var avc = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
avc.setValue("Subject for Email", forKey: "Subject")
avc.completionWithItemsHandler = //Here is where I dont know what to do.
self.navigationController?.presentViewController(avc, animated: true, completion: nil)
The completionWithItemsHandler typealias:
typealias UIActivityViewControllerCompletionWithItemsHandler = (String?, Bool, [AnyObject]?, NSError?) -> Void
Note: the previous code block is not to be used in your project, it just shows the type of closure needed (docs).
So those are the parameters that are passed into the completion handler for you to do with as you will, so the completion handler would look like this:
avc.completionWithItemsHandler = { activity, success, items, error in
}
NOTE: Because I didn't read the "SWIFT" part of the question, I answered the question in Obj-C. My bad, To the OP: I apologize
Here is a more complete answer that actually can be compiled. I used: dispatch_async in order to do an alert so you can see what "activityType" ended up being.
avc.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertViewQuick(#"Activity Status", activityType, #"OK");
});
if (completed)
{
NSLog(#"The Activity: %# was completed", activityType);
}
else
{
NSLog(#"The Activity: %# was NOT completed", activityType);
}
};
As this answer says, for Swift 3 and 4 and iOS 10 and 11 use it like this:
activityVC.completionWithItemsHandler = {(activityType: UIActivityType?, completed: Bool, returnedItems: [Any]?, error: Error?) in
}
present(activityVC, animated: true, completion: nil)
This was answered quite a while ago, but has a mix of missing and non-swift info so here's my version in the hope that it will help someone needing a more complete example of the completion handler:
avc.completionWithItemsHandler = {[weak self](activityTypeChosen, completed:Bool, returnedItems:[AnyObject]?, error:NSError?) -> Void in
// ReturnedItems is an array of modified NSExtensionItem, or nil of nothing modified
// if (activityType == nil) User dismissed the view controller without making a selection.
// Dismiss the view controller we presented
// (assume a reference to it was stored in self.activityVC)
self?.activityVC?.dismissViewControllerAnimated(true, completion: {
if activityTypeChosen == nil {
NSLog("User canceled without choosing anything")
}
else if completed {
NSLog(")User chose an activity and iOS sent it to that other app/service/whatever OK")
}
else {
NSLog("There was an error: \(error)")
}
})
}
Note the line where it dismisses the view controller. The docs for UIActivityViewController say very explicitly that your app is responsible for both presenting the VC and dismissing it.
Related
I have a bug I've been stuck on for a bit and was wondering if someone can help.
My UIActivityViewController is called within a navigationController.
The issue is the completionblock is only called SOMETIMES.
Specifically for Messages and Mail
The only instance when it actually calls the completionBlock is when user sends a successful message/mail.
If user cancels, it does not call the completionBlock which causes it to hang: Though for Mail you can dismiss the modal, for messages you cannot.
Thank you!
let shareContent = ["Write your share content"]
let activityController = UIActivityViewController(activityItems: shareContent,
applicationActivities: nil)
self.present(activityController, animated: true, completion: nil)
//Completion handler
activityController.completionWithItemsHandler = { (activityType: UIActivity.ActivityType?, completed:
Bool, arrayReturnedItems: [Any]?, error: Error?) in
if completed {
print("share completed")
return
} else {
print("cancel")
}
if let shareError = error {
print("error while sharing: \(shareError.localizedDescription)")
}
}
So after a bit, I've determined that the issue is highly likely a conflicting cocoapod or another library.
To combat this issue, we resolved this by creating a Custom Activity that mimics the "look" of messaging but just calls the MFMessageViewController regularly. With the delegate attached to the custom activity itself.
I would like to know if iOS provides us any way to know when the Share Modal will be presented or dismissed.
var completionWithItemsHandler: UIActivityViewController.CompletionWithItemsHandler? { get set }
The completion handler to execute after the activity view controller is dismissed.
let activity = UIActivityViewController(activityItems: [activityItems], applicationActivities: nil)
activity.completionWithItemsHandler = {(activityType: UIActivity.ActivityType?, completed: Bool, returnedItems:[Any]?, error: Error?) in
// dismiss activity
}
self.present(activity, animated: true, completion: nil)
Presents a view controller modally.
completion
self.present(activity, animated: true) {
}
The block to execute after the presentation finishes. This block has no return value and takes no parameters. You may specify nil for this parameter.
to share an image, i swipe it off screen.
What's happening now is:
i swipe the image
it presents the UIActivityViewController
i choose an activity
the activity shows up modally
if i cancel the activity:
the image is on screen (the view did load)
the image move back in
what i need:
i cancel the activity
the image is not on screen
the image move back in
How can i do this ?
here is my code:
let activityViewController = UIActivityViewController(activityItems: [imageToShare], applicationActivities: nil)
self.present(activityViewController, animated: true, completion: {
print("presented")
//self.grids.center = self.offScreenPosition
print("position share is \(self.grids.center)")
})
switch deviceOrientation {
case "portrait":
activityViewController.completionWithItemsHandler = {(UIActivityType: UIActivityType?, completed: Bool, returnedItems: [Any]?, error: Error?) in
if !completed {
print("cancelled")
self.moveViewVertically(.backIn, range: self.verticalRange)
}
if completed {
print("completed")
self.moveViewVertically(.backIn, range: self.verticalRange)
}
}
case "landscape":
activityViewController.completionWithItemsHandler = {(UIActivityType: UIActivityType?, completed: Bool, returnedItems: [Any]?, error: Error?) in
if !completed {
print("cancelled")
self.moveViewHorizontally(.backIn, range: self.horizontalRange)
}
if completed {
self.moveViewHorizontally(.backIn, range: self.horizontalRange)
}
}
default:
break
}
prints are there to see what's happening.
it' my first app.
thank you for your help.
Do you want the view move back before activityViewController dismiss? you can try this: custom a subclass of UIActivity,and then
- (void)performActivity {
// move your view back in
[self activityDidFinish:YES];
}
problem is solved.
in the moveView method, i wrote center.y -= 1 to move the view. ie an iteration over a desired range.
now i do:
UIView.animate(withDuration: 0.5) {
self.grids.transform = CGAffineTransform(translationX: 0, y: -self.view.frame.height)}
it works as expected.
I want to share QR image on tap of button using ActivityViewController.
Below is code that I’ve used :
#IBAction func btnShareQRCode_Clicked(sender: UIButton) {
self.shareQRCodeUsingActivityViewController(self.imageviewQRCode.image!)
}
func shareQRCodeUsingActivityViewController(imageParamater: UIImage) {
let activityItem: [UIImage] = [imageParamater as UIImage]
let objActivityViewController = UIActivityViewController(activityItems: activityItem as [UIImage], applicationActivities: nil)
objActivityViewController.excludedActivityTypes = [UIActivityTypeAirDrop, UIActivityTypeAddToReadingList]
// objActivityViewController.popoverPresentationController?.sourceView = sender
self.presentViewController(objActivityViewController, animated: true, completion: {
objActivityViewController.completionWithItemsHandler = { activity, success, items, error in
if !success { print("cancelled")
return
}
if activity == UIActivityTypeMail {
print("mail")
}
else if activity == UIActivityTypeMessage {
print("message")
}
else if activity == UIActivityTypeSaveToCameraRoll {
print("camera")
}
}
})
}
func completionHandler() {
}
The issue with this is that it is getting crashed on mail stating an error regarding MailComposer.
I want to know how and where these MailComposer function should be handled?
If you are running this on iOS Simulator, Mail component is likely to fail.
Apart from that, I don't think you need to cast your activity items list as UIImage. Simply put an array of objects as a activityItems array.
I'm using a UIActivityViewController to display a share sheet so users can share my app. I'm currently testing tweets and i'm getting some unexpected results. On tweeting for the first time, all goes well. On the second time, i'm getting a duplicate tweet error message, which is expected. The problem is that the completionWithItemsHandler is returning success: Bool as true!
I want to be able to display my own personalised message to the user rather than the massive one that is returned currently.
Here is my code:
#IBAction func ShareButtonTapped(sender: AnyObject) {
let textToShare = "I'm using Buzz! The new way to send emoji's, with sound, it's annoying, funny and amazing"
var url = NSURL(string: "-Image url masked out-")
var data = NSData(contentsOfURL: url!)
let image = UIImage(data: data!)
if let myWebsite = NSURL(string: "-redirect masked out-")
{
let objectsToShare = [textToShare, myWebsite]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
activityVC.completionWithItemsHandler = {
(activity, success, items, error) in
println("Activity: \(activity) Success: \(success) Items: \(items) Error: \(error)")
}
self.presentViewController(activityVC, animated: true, completion: { () -> Void in
})
}
}
Here is my log:
2015-01-27 11:10:58.021 Buzz[3239:813859] LaunchServices:
invalidationHandler called
2015-01-27 11:10:58.052 Buzz[3239:813860]
LaunchServices: invalidationHandler called Activity:
com.apple.UIKit.activity.PostToTwitter Success: true Items: nil Error: nil
2015-01-27 11:11:04.134 Buzz[3239:813859] LaunchServices:
invalidationHandler called
2015-01-27 11:11:09.182 Buzz[3239:813859] plugin com.apple.share.Twitter.post invalidated
Use completion handler like this For SWIFT 3 AND 4, iOS 10 AND 11 :
activityVC.completionWithItemsHandler = {(activityType: UIActivityType?, completed: Bool, returnedItems: [Any]?, error: Error?) in
if !completed {
// User canceled
return
}
// User completed activity
}
self.present(activityVC, animated: true, completion: nil)
SWIFT 2.0 iOS 8.0 >, you should use completion handler like this:
self.presentViewController(activityVC, animated: true, completion: nil)
activityVC.completionWithItemsHandler = {(activityType, completed:Bool, returnedItems:[AnyObject]?, error: NSError?) in
// Return if cancelled
if (!completed) {
return
}
//activity complete
//some code here
}
I don't think you can affect the feedback flow of the UIActivityViewController as it is high-level, easy-to-use component that is not tailored for fine-grained customization.
What you can do, though, is to save the state that user has tweeted this exact message after the first tweet and then disable Twitter from UIActivityController using excludedActivityTypes and UIActivityTypePostToTwitter. So, instead of showing an error for a duplicate tweet, you prevent the action sequence even from happening.
I suggest replacing
self.presentViewController(activityVC, animated: true, completion: { () -> Void in })
with
self.presentViewController(activityVC, animated: true, completion: nil)
This worked for me. Hope it helps!