23

I made an app for iPhone. Now, I'm recreating it for iPad.

When the user selects the action button in the toolbar, a popover should show with a UIActivityViewController, but for some reason, it's taking about 10 seconds for it to show the first time. On iPhone, it takes about a second. It's the same code except for the popover.

I tried disabling the popover, but it still takes around 10 seconds to show.

Here is the code:

-(IBAction)Actions:(UIBarButtonItem*)sender 
{
    if ([activityPopover isPopoverVisible] == YES) 
    {
        [activityPopover dismissPopoverAnimated:YES];
        return;
    }
    UIWebView *currentWebView = ((TabView *)self.tabs[self.currentTabIndex]).webViewObject;

    NSString *currentURL = (NSString*)[currentWebView request].mainDocumentURL;
    if (currentURL == NULL) return;

    BookmarkActivity *bookmarkActivity = [[BookmarkActivity alloc] init];

    UIActivityViewController *sharing = [[UIActivityViewController alloc] initWithActivityItems:[NSArray arrayWithObject:currentURL] applicationActivities:@[bookmarkActivity]];

    activityPopover = [[UIPopoverController alloc] initWithContentViewController:sharing];
    [activityPopover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];    
}

I have tested on my iPad 3 and my iPad mini, both take awhile to present this.

How can I solve the problem?

Pang
  • 9,564
  • 146
  • 81
  • 122
Maximilian Litteral
  • 3,059
  • 2
  • 31
  • 41
  • Have you tested it with time profiler? You may discover what are the instructions that take most of the time. – Ramy Al Zuhouri Dec 17 '12 at 01:21
  • Press cmd+I then select time profiler.Wait for 10-20 seconds.Then check the option "hide system libraries".This will allow you to see better your method in the call stack.Find your method in the call stack and click two times on it, you will see the time consumption. – Ramy Al Zuhouri Dec 17 '12 at 01:35
  • You should see something like this: http://imageshack.us/f/803/screenshot20121217at235.png/ tell us the time consumption or post the image. – Ramy Al Zuhouri Dec 17 '12 at 01:37
  • Says 85.7% on `activityPopover = [[UIPopoverController alloc] initWithContentViewController:sharing];`, 13.1% on `[activityPopover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];` and 1.2% on `BookmarkActivity *bookmarkActivity = [[BookmarkActivity alloc] init];` – Maximilian Litteral Dec 17 '12 at 01:55
  • Same problem, did you find a solution ? – Camille R Apr 10 '13 at 14:56
  • 1
    I have not, Its still very slow for some reason on the first time, then fast every time after. – Maximilian Litteral Apr 11 '13 at 21:45

4 Answers4

22

Good question, I just had the same problem. It is not really solvable. However, you may improve the user experience by creating an activity indicator and then sending the initialization of the UIActivityViewController to the background:

-(void)openIn:(id)sender
{
    // start activity indicator
    [self.activityIndicator startAnimating];

    // create new dispatch queue in background
    dispatch_queue_t queue = dispatch_queue_create("openActivityIndicatorQueue", NULL);

    // send initialization of UIActivityViewController in background
    dispatch_async(queue, ^{
        NSArray *dataToShare = @[@"MyData"];
        UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:dataToShare applicationActivities:nil];

        // when UIActivityViewController is finally initialized, 
        // hide indicator and present it on main thread
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.activityIndicator stopAnimating];
            [self presentViewController:activityViewController animated:YES completion:nil];
        });
    });
}

It works like a charm. When the user touches the button, the activity indicator starts animating, thus indicating that the process will take a while.

Pang
  • 9,564
  • 146
  • 81
  • 122
Matej Balantič
  • 1,627
  • 18
  • 21
  • 3
    I suggest the same, but on a more elegant way. Set the `UIBarButtonItem` property `customView` to a `UIActivityIndicator` created on the fly. This replaces the button icon to the activity indicator. Then, on the completition block of the presentation animation call `[button setCustomView:nil]` and it will revert to the original style. =) – Bruno Philipe Dec 12 '13 at 21:47
  • @BrunoPhilipe On iOS 7.0.3 Calling `setCustomView:nil` on a UIBarButtonItem created with a system icon appears to remove the icon entirely. – zekel Jan 14 '14 at 16:25
  • @zeckel I am doing in on an App with iOS 7.1 beta and it works properly. Maybe it is something else? If your button used an custom view before doing the replacement you need instead to replace it back instead of setting it to `nil`. Try checking the previous value of `customView`. – Bruno Philipe Jan 14 '14 at 20:23
  • Works like a charm! – tounaobun Sep 22 '17 at 06:17
15

I was having the same issue on iOS 7. When I removed UIActivityTypeAirDrop from the allowed activity types, however, the controller appears almost instantly.

bmueller
  • 2,681
  • 1
  • 27
  • 45
5

Although these calls are already from the main thread, since iOS 7, wrapping some of those presentation calls in a dispatch block seems to greatly reduce the delay

dispatch_async(dispatch_get_main_queue(), ^{
    [self presentViewController:activityViewController animated:YES completion:nil];
});
JP Hribovsek
  • 6,707
  • 2
  • 21
  • 26
  • Did you measure that? I can hardly understand how this should work, as it just runs the presentation in the next iteration of the mainloop, i.e. actually _later_ compared to when you directly call it. *puzzled* – DrMickeyLauer Nov 16 '17 at 14:35
4

Had this issue myself recently. Would sometimes take nearly 4 or 5 seconds to pop up, which is a lifetime! Only the first time though. Subsequent calls were quick.

Also had a similar issue a couple of years back with the keyboard appearing slowly and someone produced a few lines of code added to the appdelegate that preloads the keyboard to get around that.

I used a similar approach here to preload the UIActivityViewController by placing this in the AppDelegate on startup. It's absolutely a hack which shouldn't be necessary but I couldn't find any other options.

let lagfreeAVC:UIActivityViewController = UIActivityViewController(activityItems:  ["start"], applicationActivities: nil)
lagfreeAVC.becomeFirstResponder()
lagfreeAVC.resignFirstResponder()
m0thman
  • 51
  • 5
  • Definitely improves the load time of the ActivityViewController, I'm not sure if this will hurt the load time of the app though. – kmell96 Apr 21 '18 at 17:47