UIActivity activityViewController not dismissing o

2019-06-17 01:31发布

问题:

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?

回答1:

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.



回答2:

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. :)



回答3:

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


回答4:

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.



回答5:

a workaround is to ask the calling ViewController to perform segue to your destination ViewController via - (void)performActivity although Apple does not recommend to do so.

For example:

- (void)performActivity
{

    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
    {
        [self.delegate performSomething]; // (delegate is the calling VC)
        [self activityDidFinish: YES];
    }
}

- (UIViewController *)activityViewController
{
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
    {
        UIViewController* vc=XXX;

        return vc;
    }
    else
    {
        return nil;
    }
}


回答6:

Do you use storyboards? Maybe in your iPad storyboard, the UIActivityIndicatorView doesn't have a check on "Hides When Stopped"?

Hope it helps!



回答7:

So I had the same problem, I had a custom UIActivity with a custom activityViewController and when it was presented modally it would not dismiss not matter what I tried. The work around I choose to go with so that the experience remained the same to the user was to still use a custom UIActivity but give that activity a delegate. So in my UIActiviy subclass I have the following:

- (void)performActivity
{
    if ([self.delegate respondsToSelector:@selector(showViewController)]) {
        [self.delegate showViewController];    
    }

    [self activityDidFinish:YES];
}

- (UIViewController *)activityViewController
{
    return nil;
}

Then I make the view controller that shows the UIActivityViewController the delegate and it shows the view controller that you would otherwise show in activityViewController in the delegate method.



回答8:

what about releasing at the end? Using non-arc project!

[progressView release];

Many Users have the same problem as u do! Another solution is:

UIActivityIndicatorView *progress= [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(125, 50, 30, 30)];
progress.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge;
[alert addSubview:progress];
[progress startAnimating];

If you are using storyboard be sure that when u click on the activityind. "Hides When Stopped" is clicked!

Hope that helped...