2

I'm working on a simple app to display items in a table view. If I return an ordinary UITableViewCell object from tableView:cellForRowAtIndexPath:

static NSString *cellIdentifier = @"EmailCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
    cellIdentifier];

if (!cell) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault 
        reuseIdentifier:@"EmailCell"];
}

cell.textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];

... then the interaction with Dynamic Text works as expected; if the user goes to Settings | General | Text Size, changes the slider value and then returns to my app, all of the visible cells immediately update themselves to use the new font size.

If, however, I return a custom subclass of UITableViewCell, where the XIB contains a UILabel that is set to use a Text Style instead of a System Font, then the Dynamic Text does not work properly. Here is the code I'm using to assign the XIB to the table in viewDidLoad:

[self.table registerNib:[UINib nibWithNibName:@"EmailCell"
                                           bundle:[NSBundle mainBundle]]
     forCellReuseIdentifier:@"EmailCell"];

and then this code in tableView:cellForRowAtIndexPath:

static NSString *cellIdentifier = @"EmailCell";
EmailCell *cell = (EmailCell *)[tableView 
    dequeueReusableCellWithIdentifier:cellIdentifier];

When I first run the app, the visible cells appear with a text size that matches the user's selected preferred text size. However, if I then go to settings and change that size and then go back to my app, all of the visible cells remain with the previous text size. If I then scroll up, I will see two cells that show the new (correct) text size but the rest are still the old size. If I stop and restart the app, all cells now appear with the new (correct) text size.

It seems apparent to me that the tableview is keeping the previously-sized cells in the queue and not automatically updating their font size in response to the user's change of preferred text size. But I'm not understanding why the tableview does make this change automatically when the queue contains ordinary non-subclassed UITableViewCell instances. Is there any way I can get this to work without restarting the app (or without recreating the UITableView instance, thereby emptying the queue)? Is there any way to programmatically (and legally) clear this queue?

Edit: in case anyone's interested in this problem, my drop-in fix for this was to write a general utility method that makes a new tableview, copies over all the relevant properties from the original tableview (included registered cell classes) and then swaps the new one for the old one. Since this is a new table, it generates all-new instances of the queued cells which incorporate the new text size.

MusiGenesis
  • 74,184
  • 40
  • 190
  • 334
  • Have you looked at this - https://stackoverflow.com/questions/20115053/how-to-make-uitableview-row-height-respond-to-users-preferred-text-size-dynami? Seems like a similar issue. Just have your custom cells listen for `UIContentSizeCategoryDidChangeNotification` and update the fonts when it's called. – ansible Jan 16 '14 at 22:14
  • @ansible: Yeah, I may have to end up doing something like that, but it seems like the subclassed cell should do this automatically just like the un-subclassed cell does. This problem may just be a side effect of the fact that iOS doesn't support XIB-based cells very well. – MusiGenesis Jan 16 '14 at 23:02
  • Hmm, if you are adding new UILabels to your custom cell, I wouldn't expect the parent class to know about those labels, unless it walks through all of the subviews or something. But that would seem fairly inefficient to me. What happens if you show the textLabel from UITableViewCell? Does that get auto-updated? – ansible Jan 16 '14 at 23:18
  • The "built-in" textLabel in UITableViewCell automatically picks up the new font size. I wonder if maybe iOS doesn't even bother caching cells with nothing added to them - that would explain why [table reload] refreshed them all with the new font size. Tomorrow I'm going to try adding views programmatically to see if maybe this problem is a side effect of the XIB loading. – MusiGenesis Jan 17 '14 at 04:40

3 Answers3

1

This is now handled for you in iOS 10.

http://useyourloaf.com/blog/auto-adjusting-fonts-for-dynamic-type/

Set adjustsFontForContentSizeCategory to YES / true on your label and it'll resize itself automatically when the text size preference changes.

Tom Hamming
  • 10,577
  • 11
  • 71
  • 145
0

Based on what you described, it would seem that you simply want to reload the table anytime the view comes back on screen after the user has backgrounded it. To achieve this the way I think you want, you need to add the following in your init method for your tableView - it will tell your tableView to reload the cells properly whenever the app is about to enter the foreground:

[[NSNotificationCenter defaultCenter] addObserver:self.tableView selector:@selector(reloadData) name:UIApplicationWillEnterForegroundNotification object:nil];

This way, if the user comes back to the view by opening the app after going to the phone's settings, your tableView should reload and the changes (if any were made) should properly be reflected.

You can see a quick video of the result I tested here:

https://www.dropbox.com/s/pvjuiyofydnxnvd/textsize.mov

EDIT:

Like you said in a previous comment, it would seem like it's something wrong with your nib implementation. The only difference is where/how you update the label property. In the custom cell, I created a label property and a font property, and added the label to the cell in init, and in layoutSubviews I overrode the font. Here's the code:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    LabelCell *cell = [tableView dequeueReusableCellWithIdentifier:@"LabelCell"];

    if (cell == nil) {
        cell = [[LabelCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"LabelCell"];
    }

    cell.myLabel.text = _items[indexPath.row];
    cell.myFont = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];

    return cell;
}

And in the cell itself:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];

    self.myLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, self.contentView.frame.size.width - 20, 34)];
    self.myLabel.backgroundColor = [UIColor greenColor];
    [self.contentView addSubview:self.myLabel];

    return self;
}

- (void)layoutSubviews {
    [super layoutSubviews];

    self.myLabel.font = self.myFont;
}

And here is the same result using custom cells with labels:

https://www.dropbox.com/s/ow2zkb6j9yq2c3m/labelcells.mov

Regarding "clearing the queue", the cells don't get queued up until they are juuuust about to be shown on screen, you can see this by logging a counter value right after you dequeue cell with identifier. If there are 10 cells on screen right now, it only dequeues 10 cells. This is the reason why you use the code if (!cell) {do stuff here that's common for all cells} and then you do things that are specific to each cell afterwards, and why this code works even if you were to assume that reloadData didn't "clear the queue", which I'm not convinced it wouldn't anyway after reading through the UITableView docs.

Mike
  • 9,765
  • 5
  • 34
  • 59
  • I think you should read my whole question. Your example only works because you're assigning a value to the `textLabel` of a `UITableViewCell`; as I mentioned at the beginning of my question, that control picks up the new preferred text size with no problem. Custom subclasses of UITableViewCell are the problem. Also, `reloadTable` does *not* clear the queue of reusable cells - it only instructs the table to re-render all the visible cells. – MusiGenesis Jan 19 '14 at 13:04
  • Added edit with custom cell - doesn't make a difference. – Mike Jan 19 '14 at 16:12
  • Your code example works because you explicitly set the font in `tableView:cellForRowAtIndexPath:`. I'm trying to set this up so that I can design the cell in IB and then not have to also set the fonts etc. in code anywhere. – MusiGenesis Jan 21 '14 at 14:38
  • Also, I apologize for being a douche in my original comment. Thanks for your efforts. – MusiGenesis Jan 21 '14 at 14:39
  • No worries, I'm not too familiar with IB and the proper implementation, so I unfortunately can't help too much more, but best of luck. – Mike Jan 21 '14 at 14:42
0

My drop-in fix for this was to write a general utility method that makes a new tableview, copies over all the relevant properties from the original tableview (included registered cell classes) and then swaps the new one for the old one. Since this is a new table, it generates all-new instances of the queued cells which incorporate the new text size.

MusiGenesis
  • 74,184
  • 40
  • 190
  • 334