Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIActivity activityViewController not dismissing on iPad

I have a UIActivity subclass that creates its own activityViewController:

- (UIViewController *)activityViewController {
    WSLInProgressViewController* progressView = [[[WSLInProgressViewController alloc] init] autorelease];
    progressView.message = [NSString stringWithFormat:NSLocalizedString(@"Posting to %@...",@"Posting to..."),
                        self.activityType];

    return progressView;
}

I've add a full repro on GitHub.


According to the documentation, you aren't supposed to dismiss this manually. Instead, the OS does that when you call activityDidFinish:. This works fine when ran on an iPhone.

When I say "works," this is the sequence of events that I'm expecting (and see on the iPhone):

  1. Display the UIActivityViewController
  2. User presses my custom activity
  3. My view controller appears
  4. I call activityDidFinish:
  5. My custom view controller is dismissed
  6. The UIActivityViewController is also dismissed

However, when I run this same code on the iPad Simulator -- the only difference being that I put the UIActivityViewController in a popup, as the documentation says you should -- the activityViewController never dismisses.

As I say, this is code wo/the popUP works on the iPhone and I have stepped through the code so I know that activityDidFinish: is getting called.

I found this Radar talking about the same problem in iOS6 beta 3, but it seems such fundamental functionality that I suspect a bug in my code rather than OS (also note that it works correctly with the Twitter and Facebook functionality!).


Am I missing something? Do I need to do something special in the activityViewController when it's run in a UIPopoverViewController? Is the "flow" supposed to be different on the iPad?

like image 762
Stephen Darlington Avatar asked Sep 22 '12 12:09

Stephen Darlington


4 Answers

The automatic dismissal only appears to happen when your 'activity' controller is directly presented, not wrapped in anything. So just before showing the popup it's wrapped in, add a completion handler

activity.completionHandler = ^(NSString *activityType, BOOL completed){
   [self.popup dismissPopoverAnimated:YES];
};

and you'll be good.

like image 69
Alex Curylo Avatar answered Dec 07 '22 15:12

Alex Curylo


I see the question is quite old, but we've been debugging the same view-controller-not-dismissing issue here and I hope my answer will provide some additional details and a better solution than calling up -dismissPopoverAnimated: manually.

The documentation on the UIActivity is quite sparse and while it hints on the way an implementation should be structured, the question shows it's not so obvious as it could be.

The first thing you should notice is the documentation states you should not be dismissing the view controller manually in anyway. This actually holds true.

What the documentation doesn't say, and what comes as an observable thing when you come across debugging the non-dissmissing-view-controller issue, is iOS will call your -activityViewController method when it needs a reference to the subject view controller. As it turns out, probably only on iPad, iOS doesn't actually store the returned view controller instance anywhere in it's structures and then, when it wants to dismiss the view controller, it merely asks your -activityViewController for the object and then dismisses it. The view controller instantiated in the first call to the method (when it was shown) is thus never dismissed. Ouch. This is the cause of the issue.

How do we properly fix this?

Skimming the UIActivity docs further one may stumble accross the -prepareWithActivityItems: method. The particular hint lies along the following text:

If the implementation of your service requires displaying additional UI to the user, you can use this method to prepare your view controller object and make it available from the activityViewController method.

So, the idea is to instantiate your view controller in the -prepareWithActivityItems: method and tackle it into an instance variable. Then merely return the same instance from your -activityViewController method.

Given this, the view controller will be properly hidden after you call the -activityDidFinish: method w/o any further manual intervention.

Bingo.

NB! Digging this a bit further, the -prepareWithActivityItems: should not instantiate a new view controller each time it's called. If you have previously created one, you should merely re-use it. In our case it happily crashed if we didn't.

I hope this helps someone. :)

like image 27
eploko Avatar answered Dec 07 '22 16:12

eploko


I had the same problem. It solved for me saving activityViewController as member and return stored controller. Activity return new object and dismiss invoked on new one.

    - (UIViewController *)activityViewController {
        if (!self.detaisController) {
            // create detailsController
        }
        return self.detailsController;
    }
like image 25
aironik Avatar answered Dec 07 '22 17:12

aironik


I pass through the UIActivity to another view then call the following...

[myActivity activityDidFinish:YES];

This works on my device as well as in the simulator. Make sure you're not overriding the activityDidFinish method in your UIActivity .m file as I was doing previously. You can see the code i'm using here.

like image 31
AndyDev Avatar answered Dec 07 '22 15:12

AndyDev