问题
I'd like to easily test my app with different selections of system text size, including accessibility sizes. These can be set in the Settings app (Display & Brightness => Text Size or General => Accessibility => Larger Text).
The only way I can find to currently do this is to go into Settings and change the value with the UI (edit: partial solution described below). This is slow and cumbersome. I suspect there's a way to dynamically alter it using private APIs, but I can't figure out how. Since my goal is to only use this for debugging, private API use and swizzling is fine (this code won't be making its way to production).
To try to find a private API to do this, I looked at some reverse engineering resources. I'm new to disassemblers, symbol tables, class dumps, and finding private APIs I can use, but this is what I've tried so far:
I successfully swizzled
-[UIApplication preferredContentSizeCategory]
(other posts said this worked in the past), but this doesn't affect the result returned from+[UIFont preferredFontForTextStyle:]
.Using the disassembler IDA I discovered that
+[UIFont preferredFontForTextStyle:]
is in the private frameworkUIFoundation.framework
. (-[UIApplication preferredContentSizeCategory]
is inUIKit.framework
but the disassembly didn't look at all useful).Just as I started writing this question (it always happens that way), I discovered a partial solution. One can set a launch argument in the scheme to set the value on launch. This is useful, but not what I'm after.
Through the above answer, I found the value for the user's preference is apparently stored in "the
com.apple.UIKit.plist
file located in the Simulator'sdata/Library/Preferences
directory". The value we're after can be set from the command line utilityplutil
. This is also an improvement! But I'd like to dynamically alter it at runtime.
More on my results from IDA:
I don't really know how to read disassembly, but +[UIFont preferredFontForTextStyle:]
seems to point to a symbol called ___UIFontForTextStyle
which seems to point to some interesting-sounding symbols called _getUIContentSizeCategoryUnspecified
and _getUIContentSizeCategoryPreferenceClass
I also found these symbols using the command-line utility nm on UIFoundation.framework
. They were marked with the lowercase letter "s" meaning, apparently, "The symbol is in an uninitialized data section for small objects.". I have no idea what this means (all I've gathered is that they're not a class or method).
Searching the web for the _getUIContentSizeCategory...
symbols yields nothing, but nearby there's another symbol _getUIApplicationClass
. I searched for that one since it sounded a bit more general, and found something similar in some WebKit source. Could be nothing, but maybe it's an internal Apple convention. Regardless, the example doesn't really help me solve the problem.
Anyway thanks for reading so far. If you're still here, my question is:
I'd like to be able to dynamically mock the value for the dynamic type size preference. These disassembly symbols might help, but maybe I'm on the wrong track. It feels like the solution is close but I just can't put all the pieces together.
Setting this value as a launch argument is nice, but doesn't fully solve my problem. Similarly, modifying the Simulator's value in the plist
is also nice for automation, but doesn't solve my problem.
Is there a way to dynamically alter this value at runtime?
回答1:
How embarrassing! I was looking at some out-of-date iOS 9.x documentation and missed that UITraitCollection
gained init(preferredContentSizeCategory: UIContentSizeCategory)
and var preferredContentSizeCategory: UIContentSizeCategory
in iOS 10, which was helpfully pointed out to me by Brandon Williams. This addresses my needs perfectly.
回答2:
One easy way is to write a wrapper around UIFontMetrics
and route all UIContentSizeCategoryDidChange
notifications through that. It allows for unit/UI testing various Dynamic Type settings. I wrote about it here.
来源:https://stackoverflow.com/questions/43879298/dynamically-mocking-ios-dynamic-type-system-text-size-uicontentsizecategory