0

I have a root view controller, which presents the modal view controller with standard animation (modal view controller appears from bottom to top).

Let's name this view controllers MyRootViewController and MyModalTableViewController.

The problem is animation stops if MyModalTableViewController reloads data when it appears.

For example:

- (void)openModalViewController {
  MyModalTableViewController * vc = [self.storyboard instantiateViewControllerWithIdentifier:@"myModalScreen"];
  [self presentViewController:vc animated:YES];
}

And in MyModalTableViewController I have the next code:

- (void)viewDidLoad {
  self.itemList = [[MyData sharedInstance] itemList]; // self.itemList is NSArray
}

// ...

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  MyTableViewCell * cell = [tableView dequeReusableCellWithIdentifier:@"myCell"];
  cell.item = self.itemList[indexPath.row];
  return cell;
}

So when MyModalTableViewController is loading from storyboard, it loads itemList and shows it on UITableView. And presentation animation starts only when UITableView complete to load data. I guess it because the animation and data reloading works in the same thread. So if I have 10000 items to show, it takes few seconds and only then presentation animation starts.

It is too slow. So my question is what is the best way to solve this problem?

Alexander Perechnev
  • 2,797
  • 3
  • 21
  • 35
  • 2
    Why not doing the opposite ? Animation first (with maybe an activity indicator) then the reloadData. – KIDdAe Feb 13 '15 at 10:34

4 Answers4

2

Well, you can load your item list on background thread

- (void)viewDidLoad {

    //background thread
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        //load data
        self.itemList = [[MyData sharedInstance] itemList]; // self.itemList is NSArray

        //main thread
        dispatch_async(dispatch_get_main_queue(), ^{
            //reload table
            [self.tableView reloadData];
        });

    });

}
Thilina Chamath Hewagama
  • 9,039
  • 3
  • 32
  • 45
1

In MyModalTableViewController, add a method loadData

- (void)loadData {
     self.itemList = [[MyData sharedInstance] itemList]; // self.itemList is NSArray
     [self.tableView reloadData];
 }

Use then the completion block of `presentViewController

 [self presentViewController:vc animated:YES completion:^{
    [vc loadData];
}
Michaël Azevedo
  • 3,874
  • 7
  • 31
  • 45
  • Thank you for response. I've thought about this, but I guess there is more elegant solution. – Alexander Perechnev Feb 13 '15 at 10:36
  • Also, the code will be more complicated and not reusable if I push `MyModalTableViewController` to `UINavigationController` first. So, I will have to access to necessary screen through the navigation controller. – Alexander Perechnev Feb 13 '15 at 10:42
  • 1
    What about `viewDidAppear` then ? This method is called when the animation is completed, so you will have the same results and will be compatible with a navigation stack. To ensure your code won't be called multiple times (`viewDidAppear` is also called when a child view controller is dismissed), use an intermediate property to know if it is the first init or not. – Michaël Azevedo Feb 13 '15 at 12:43
  • Thank you again that you've tried to help me. The problem is not where i expected, it is a bit deeper. I've found the answer and I will publish it later. – Alexander Perechnev Feb 13 '15 at 12:56
  • 1
    I'm looking forward to see it then ;) – Michaël Azevedo Feb 13 '15 at 12:58
  • Okay I've posted the answer. – Alexander Perechnev Feb 13 '15 at 14:33
1

The problem caused because I'm trying to present view controller from the - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath; delegate method.

This delegate method is calling not in the main thread, that's why animations works slow. It looks too strange, because in iOS 7 I didn't have such problem. It happenes only on iOS 8 and later.

I've found the same problem in this SO topic: Slow presentViewController performance

So the solution is to implement delegate like below:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  __block UIViewController * vc = [self.storyboard instantiateViewControllerWithIdentifier:@"myVC"];
  dispatch_async(dispatch_get_main_queue(), ^{
    [self presentViewController:vc animated:YES completion:nil];
  });
}

I've looked into Apple documentation, but didn't find the notice that this delegate methods calls not in main thread: https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UITableViewDelegate_Protocol/index.html#//apple_ref/occ/intfm/UITableViewDelegate/tableView:didSelectRowAtIndexPath:

So it would be great if someone explains why this problem caused only on iOS 8 and later.

Community
  • 1
  • 1
Alexander Perechnev
  • 2,797
  • 3
  • 21
  • 35
  • 1
    Hi, I just read your answer right now, and guess what : I got the same issue a few months ago ! I'm actually re-coding an objC app in Swift with iOS 8 SDK, and when I tried to push a viewController from `- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath` , the animation was really slow. I thought it was a Swift issue, but thanks to you i know this is an iOS 8 issue now. Pretty weird it isn't mentioned anywhere. – Michaël Azevedo Feb 16 '15 at 08:36
0

You can charge the itemlist in the previous view controller and set it to the new one:

- (void)openModalViewController {
  MyModalTableViewController * vc = [self.storyboard instantiateViewControllerWithIdentifier:@"myModalScreen"];
  vc.itemlist = self.itemlist;
  [self presentViewController:vc animated:YES];
}
peig
  • 418
  • 3
  • 11