2

I have a view controller that contains a table view and displays custom cells. I'm trying to react to a content size category change using the traitCollectionDidChange method (within the cell subclass), but this method is never called for the cells when this trait changes. On the other hand, it does get called when the size class changes.

traitCollectionDidChange is called for the view controller (when the content size category changes) and I can manually propagate the call to the visible cells, but it feels like extra work. Why is it not getting called for the cell subclasses specifically for the content size category change?

Edit for reasoning why I want the call within the cell:

I want to change the cell's label to multi-line when the content size category is associated with accessibility, but keep it single-line otherwise. I've noticed this behavior in certain iOS apps, e.g. Apple Music.

Brian Spilner
  • 581
  • 1
  • 5
  • 12
  • What do you mean by "content size"? Do you mean "content size _category_"? If so, you don't need to propagate anything. What aspect of the cell is supposed to be affected here? Typically it's the font of a label, and labels are _automatically_ registered to receive the notification for this (so there is nothing for you to do). Can you explain the issue a bit better? – matt Jan 04 '22 at 14:28
  • @matt Correct, I mean content size category. Thanks for the correction. – Brian Spilner Jan 04 '22 at 14:30
  • Cool, see the rest of my comment now that I've edited it. :) – matt Jan 04 '22 at 14:31
  • @matt See the edit at the end of the question, thanks. – Brian Spilner Jan 04 '22 at 14:35
  • Yes, I know what you mean now! Apple has a WWDC video where they talk about rearranging the interface quite dramatically when the content size category gets up into the accessibility levels. – matt Jan 04 '22 at 14:40

2 Answers2

3

Any place in your app that needs to hear about content size category changes can register for the UIContentSizeCategory.didChangeNotification.

https://developer.apple.com/documentation/uikit/uicontentsizecategory/1622948-didchangenotification

So instead of you propagating the information down the hierarchy, just have an appropriate object register for that notification. For example, perhaps you have (or could have) a custom table view cell subclass where this would be appropriate.


As for the question you actually asked, all I can do is provide a theory. Apple configures things like a UILabel to receive UIContentSizeCategory.didChangeNotification when it is in a table view cell. This involves some hanky-panky behind the scenes. I suspect that as part of this hanky-panky, they deliberately prevent the cell itself from receiving traitCollectionDidChange for content size category changes, since the same signal is arriving already in a different way and they don't want to bombard the cell contents with too many events.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Indeed I have thought about doing this. Still it's not clear why traitCollectionDidChange is not getting called. In any case, I appreciate your input. – Brian Spilner Jan 04 '22 at 14:47
  • I'm totally with you on that. Let me see whether I can add any useful info about it. Give me a few minutes to try it on my machine and see what's going on. – matt Jan 04 '22 at 14:49
  • 1
    Okay! I have no strong statement to make about this, but I can certainly confirm that the `didChange` override doesn't automatically propagate to the cell when the content size category changes. My guess is that this is deliberate, perhaps because there could be a lot of cells, perhaps because of the way cells are reused, or perhaps because things like labels automatically _do_ get it routed to them instead. There is definitely hanky panky here; I've noticed it before. However, as I've said, you can always register for the notification instead, so there isn't really any problem. – matt Jan 04 '22 at 15:03
  • True, it does seem to be a "feature" rather than a bug, possibly due to one of the reasons you mentioned. I'll use the notification then, thanks for your time! – Brian Spilner Jan 04 '22 at 15:18
  • No problem, you made a really interesting observation here. Like I say, I can't give Apple's definitive reasoning, but I did notice way back when they first introduced automatic response to size category changes for UILabels specifically in table view cells that something odd was going on, and I suspect this is part of it. – matt Jan 04 '22 at 15:21
  • No worries, I think your theory makes sense. I just needed some thoughts from someone more experienced before using another approach, such as the notification. It's certainly one of those questions I wish I could get an answer on from Apple system devs. :) – Brian Spilner Jan 04 '22 at 15:49
2

A surprising detail about a UITableViewCell lifecycle:

  • changes in size class cause a call to traitCollectionDidChange(_:)

  • changes in size category recycle the cell, and traitCollectionDidChange(_:) is not called

Therefore, changing the layout in response to size class and category needs code in two places:

  • traitCollectionDidChange(_:) for size class changes
  • initializer of the cell for size category changes

You can confirm that the cell is recycled with a breakpoint in the delegate method tableView(_:willDisplay:forRowAt:), and also by noting that previous constraints conditionally set for the category disappear (because the cell is removed from its superview).

Jano
  • 62,815
  • 21
  • 164
  • 192