2

I have a view controller that is setup with a UITableView in UIStoryboard. The UITableView is constrained to the SafeArea of the controller on all four sides.

It may seem that this question has been asked before, but each time I have found the question, it is because there are changes taking place in viewDidLayoutSubviews that cause the loop to occur. I have a blank implementation of viewDidLayoutSubviews and basic UITableViewCells (no nib). Yet in iOS 11, Xcode 9.2, flicking the tableview up causes viewDidLayoutSubviews to be called on an endless loop. This is my implementation:

- (void)viewDidLayoutSubviews {

    NSLog(@"viewDidLayoutSubviews");
    [super viewDidLayoutSubviews];
}

I cannot figure out for the life of me how this is happening.

Edit: The controller has a NavigationController and is the Master controller of a UISplitViewController, which is embedded in a BannerViewController (from iAdSuite example).

Edit: This happens on iOS 11 (up through 11.2) on ALL devices on the simulator. Does NOT happen iOS 10.3. viewDidLayoutSubviews isn't even called in iOS 10.3. I get the same results when I simply use the Nav Controller with the UIViewController/TableView in place of the UISplitViewController/BannerViewController. So seems that the issue is not introduced there, but from something that is happening in iOS 11.

EDIT: So this is odd - seems it is happening only when large titles are selected for the navigation controller!

if (@available(iOS 11.0, *)) {
    self.navigationController.navigationBar.prefersLargeTitles = YES;
    self.navigationItem.largeTitleDisplayMode = UINavigationItemLargeTitleDisplayModeAlways;
}

EDIT: This problem can be easily reproduced. I downloaded Apple's iAd Suite sample code, and used the SplitNavigationController project. I updated the project for iOS 11, including updating the storyboard to use Safe Area Layout Guides. I changed the MasterViewController from a UITableViewController to a UIViewController with a UITableView, which I constrained to the Safe Area layout guides in the storyboard. I added in prefersLargeTitles and largeTitleDisplayMode to the MasterViewController. I added in the viewDidLayoutSubviews method, with a NSLog statement, so this behavior can be easily observed. I think this is a real problem with iOS 11, and I will try to get to the bottom of it, and I would also appreciate if anyone else can offer any thoughts or suggestions.

EDIT: matt was able to reproduce this issue and post it in a GitHub project, to be found at the following link (thanks matt!): InfiniteLayoutSubviewsBug

SAHM
  • 4,078
  • 7
  • 41
  • 77
  • 2
    "how to make it stop" One simple idea: Don't do that. — Also what on earth does "I have a view controller that is not subclassed" mean? – matt Dec 20 '17 at 18:41
  • Could you please elaborate on what you mean by endless loop here? Does your app hang executing this method? – The Dreams Wind Dec 20 '17 at 18:49
  • 1
    Could you post the example on github? I can't reproduce based on your description. It seems like maybe you're doing something illegal, like maybe you've got an illegal parent view controller arrangement or something. I would not expect "flicking the tableview up" to call `viewDidLayoutSubviews` at all. – matt Dec 20 '17 at 18:53
  • Matt - the first thing I checked was if it was a subclass of another controller that might have been doing something funky. I edited the question to remove that part of it since, you are right - it was confusing. I can't post the project on github because it is huge, and confidential as well. I should have mentioned that the controller has a NavigationController and is the MasterController of a UISplitViewController, which is embedded in a BannerViewController (from old iAd example). I will add that. – SAHM Dec 20 '17 at 19:16
  • Aleksandr - by endless loop, I mean that when I flick the tableview up, viewDidLayoutSubviews is called, then called again, then called again, and keeps getting called. I can tell because I have the NSLog statement. The app is still responsive at this point but really slows down. – SAHM Dec 20 '17 at 19:16
  • @matt - I got the sample project to do it. I am not sure how to post on GitHub but I will try. – SAHM Dec 21 '17 at 02:18
  • Ok, I know it's not ideal, but I don't know how to use Github. Here is a public Dropbox link I created: The zip file is just the project file itself, and all of the other project files are in the folder. This is essentially Apple's iAdSuite SplitNavigationBanner sample project - all I did was update to iOS 11 and make the MasterViewController a UIViewController w/a UITableView instead of a UITableViewController. And saw the same issues I have been writing about. Sorry - I haven't learned how to use Github yet. https://www.dropbox.com/sh/hvrqn4wzhpx8oh6/AACxppgjp3-IvtP9NofeGLW8a?dl=0 – SAHM Dec 21 '17 at 02:35
  • I also added in the viewDidLayoutSubviews method in the MasterViewController with a NSLog statement, so anyone running this can see that it gets into an endless loop when the UITableView is flicked upward. – SAHM Dec 21 '17 at 02:43
  • And I updated the storyboard so that it uses safe area layout guides, to which I constrained the MasterViewController tableview. I also added prefersLargeTitles and largeTitleDisplayMode to MasterViewController. – SAHM Dec 21 '17 at 02:49
  • Sorry, because of the weird way you zipped the pieces I can't build. – matt Dec 21 '17 at 03:40
  • Let me try again. Dropbox did it when I uploaded – SAHM Dec 21 '17 at 03:41
  • @matt OK, I zipped the whole folder into one file and now that is the only file in the Dropbox folder. Thanks for taking a look at this. It really is remarkable when you see what it is doing. Remarkably bad. https://www.dropbox.com/sh/hvrqn4wzhpx8oh6/AACxppgjp3-IvtP9NofeGLW8a?dl=0 – SAHM Dec 21 '17 at 03:45
  • 1
    Okay, I see, let me see if I can reproduce in a clean project – matt Dec 21 '17 at 03:52
  • Nailed it! Hang on a second and I'll post to github – matt Dec 21 '17 at 04:05
  • Great! So glad you could reproduce the issue. Were you also able to solve it, or is this an iOS 11 issue where a radar needs to be filed? – SAHM Dec 21 '17 at 04:11
  • 1
    Oh, I'd say it's a bug all right. We are getting infinite calls to layout subviews, once per display link (1/60 second), if you scroll _up_ with large titles turned on. Turning off large titles solves the problem. – matt Dec 21 '17 at 04:26
  • 1
    Here's the tiny demo on github, feel free to use it as you wish. https://github.com/mattneub/InfiniteLayoutSubviewsBug – matt Dec 21 '17 at 04:27
  • @matt, I think I asked the question rather clumsily. If you want to post your own question about this issue please feel free - I can delete mine - I think you would ask the question in a much more clear way and it would probably get the attention it deserves. I'm not very good at StackOverflow. Thanks again for taking a serious look at it and for the sample project – SAHM Dec 21 '17 at 04:42
  • not at all, it's your discovery; I've given you an absolute rock-bottom example project, feel free to reference it in any way you like – matt Dec 21 '17 at 04:43
  • but I'll write an answer if you like – matt Dec 21 '17 at 04:44
  • Yes, that would be great. Thank you so much. – SAHM Dec 21 '17 at 04:45
  • 1
    This bug no longer exists (in iOS 12.1), so clearly they fixed it at some point along the way. – matt Dec 29 '18 at 01:33

1 Answers1

3

EDIT This was definitely a bug in iOS 11 thru at least iOS 11.2, but in iOS 12 the bug no longer exists.

It looks like you've found a bug. I was able to reproduce the problem in a minimal example project, which I've posted at GitHub here:

https://github.com/mattneub/InfiniteLayoutSubviewsBug

Download the project and run it. You'll see a simple table with three rows. Scroll the table view up and let go. Watch the console. We are getting repeated calls to viewDidLayoutSubviews, once every 1/60 of a second, forever.

Note that this is not caused by a recursive layout loop in our code. My example doesn't even call super. All it does is log. This is the runtime itself calling viewDidLayoutSubviews repeatedly forever. No code of ours (except print) runs while this is happening.

Other observations:

  • As you rightly observed, if you change the example so that the navigation bar doesn't use large titles, the problem goes away.

  • If you change the example so that the table view is not positioned using autolayout, the problem goes away.

  • If you change the example (rather more elaborately) so that the view controller is a UITableViewController subclass, the problem goes away; we get some repeated calls to viewDidLayoutSubviews but not forever, just while scrolling.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • I like the second or third solution. Preferable to taking out large titles altogether, until the fix comes from Apple, at least. Thank you so much Matt! – SAHM Dec 21 '17 at 04:57
  • I will do it tomorrow. I've never done it before, so hopefully I will do a good job. I will try my best. – SAHM Dec 21 '17 at 05:01
  • 1
    I filed the bug with Apple. – SAHM Dec 21 '17 at 05:25
  • Bravo! I might pile on as well, tomorrow. But the glory goes to you. – matt Dec 21 '17 at 05:27
  • Pile it on! No glory haha - just want to have it fixed! But really, so thankful you came up with that super simple project to demonstrate, and that you gave this some serious attention. I thought I was losing my mind. – SAHM Dec 21 '17 at 05:30
  • It doesn't look like Apple has looked at the bug report. I guess these things take time but this seems like kind of a big deal, no? – SAHM Dec 22 '17 at 04:51
  • You've never done this before, have you? You'll never hear from them again. It just goes into a black hole. We've done our duty, that's that. We both submitted reports. On with life. – matt Dec 22 '17 at 06:07
  • guys, this same infinite loop just happened to me. I guess they did not do anything about it. Is there a solution to make it return after changing collectionView contentOffset – Mert Köksal Jan 12 '22 at 13:04