6

I'm updating my app to support Dynamic Type in iOS 7. It was relatively easy to make the text adjust its size depending on the system setting, but as I'm using it in the context of a UITableView and cells with multiple UILabels in them, text size isn't the only thing I have to worry about. If the text grows, the cell's height should as well, if the text shrinks, so should the cell height.

Also, if it gets smaller, it should obviously have less spacing between items when compared to at its largest size type (as at a small size the spaces between would be giant).

How do I change more advanced layout issues such as these when the user changes their Dynamic Type size?


Right now, I'm doing something really ugly that barely works. I look at the height of one of my labels and scale my constants with its size. But it's very imprecise, as, say 110% of a UILabel height at the current text size being used as the padding between elements will not necessarily be universally working.

So here's what I'm doing in that example:

    CGRect articleTitleRect = [article.title boundingRectWithSize:CGSizeMake(CGRectGetWidth(self.contentView.bounds) - 29, MAXFLOAT)
                                                          options:NSStringDrawingUsesFontLeading
                                                       attributes:@{ NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline] }
                                                          context:nil];
    self.urlConstant.constant = articleTitleRect.size.height / 5;
    self.previewConstant.constant = articleTitleRect.size.height / 5;

(Basically finding out what the height of a label is, then using percentages of that to infer the spacing. Again, very imprecise and doesn't work well universally.)

The other thing I considered doing was checking what the current preferredFontForTextStyle: is equal to at a point size, and for specific values hardcode the interface adjustments/spacing. This works a little better, but it still doesn't seem optimal to what Apple had in mind, as it's not terribly dynamic (it breaks if they add another type size, for example) and you're almost sniffing for values they don't give you off the bat (which makes it seem hacky).


So what do apps such as Tweetbot 3 (that now use Dynamic Type to set their UITableViewCell elements) do to make their UI look so well done over different Dynamic Type sizes? What's the best way to go about doing this? There honestly seems like no tutorials on the topic.

1
  • Can you show some screenshots of how Tweetbot 3 changes spacing between text elements depending on the Dynamic Type? Commented Nov 7, 2013 at 11:16

2 Answers 2

1

This is something that you will have to do yourself, but iOS has given you the tools you need with TextKit. There is actually a lot of documentation in the Test Programming Guide.

For example, in the Working with Font Objects section, the UIContentSizeDidChangeNotification, which informs your app that the Dynamic Type value has changed, with a userInfo dictionary with the new value. This is the entry point to what changes you want to make. For example, if the new value is UIContentSizeCategoryAccessibilityMedium, the distance between two labels is 10 points, but if the new value is UIContentSizeCategoryAccessibilityLarge, you can set it to 15. Of course I'm just making up values, figuring out what works best is something you'll have to do through trial and error. However, once you figure out the right distances, making sure everything works shouldn't take more than a dozen lines of code.

Also take a look at UIFontDescriptor, especially the constants at the bottom of that reference. They let you access pretty much every font property and trait imaginable. You can use that to "build" your own font with custom properties. If you want to go that way, it's going to require a little bit more code, but TextKit provides you with a lot of different APIs when it comes to showing text on screen.

Sign up to request clarification or add additional context in comments.

Comments

0

I haven't used dynamic type yet. But I think one approach you could use would be to use auto-layout to lay out your cell content, and let the auto-layouting engine determine your required cell heights. Then, when the dynamic type size updates you'd simply have to ask the tableview to either reload or recalculate (via beginUpdates/endUpdates).

I answered a question with an example of how to use auto layout to calculate the tableview cell height for any given cell, here:

How to resize superview to fit all subviews with autolayout?

EDIT

Per your comment:

Height's not really the issue here, I can calculate that rather easily. My question is moreso how to deal with the harder things, like space between labels for instance, where as the labels get bigger their spacing should grow slightly as well. Also just basically learning how best to adjusts layouts affected by Dynamic Type

Anytime you need to adjust a constraint at runtime once the constraint has been created and registered, you do so by adjusting the constant property of the constraint. So if you want to tweak the spacing between two items based on some other property (e.g. text size) you have to do that manually by adjusting the constraint constant that manages spacing for those two items. If the constraint was created in Interface Builder you need to bind it to an IBOutlet in somewhere so you can refer to it in code.

Constraints also have a multiplier property, which you can use to dynamically adjust one constraint based on the calculated attribute value of some other view. I don't think you can manipulate this in Interface Builder, but if you create your constraints in code you can. Using the multiplier you should be able to set up a spacing constraint that adjusts larger or smaller based on the height of some other element.

In a more complex scenario, you might desire to dramatically change the layout given some property change (e.g. text size), beyond tweaking simple spacing constraints. In this case I'd recommend either of the following:

1) Create and manage your constraints entirely in code. Tear-down and build up the correct set of constraints when you determine a layout changeover is required.

2) Create multiple nibs to manage multiple UI layouts via Interface Builder-defined constraints. Dynamically load/reload the correct nib when you determine a layout changeover is required. The possibly undesired side effect of this is all of your views/controls will be recreated.

2 Comments

Height's not really the issue here, I can calculate that rather easily. My question is moreso how to deal with the harder things, like space between labels for instance, where as the labels get bigger their spacing should grow slightly as well. Also just basically learning how best to adjusts layouts affected by Dynamic Type.
I understand how to change constants, but changing them to what values is the confusing part here. How do I know what multiplier to use? Just making guesses by looking at the growths of other UI elements is a rather inaccurate way.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.