Your App's Layout and Localization APIs
Let’s discuss five techniques for dealing with right-to-left layout across Apple platforms, using 11 different APIs. Be sure to check out the handy dandy chart at the end, and Apple’s Building Apps for the World webpage. Let’s get started, in order of oldest API to newest.
Inference By Writing Direction using NSLocale
The first technique is inferring the device’s direction, using two class methods on NSLocale
. characterDirection(forLanguage:)
and lineDirection(forLanguage:)
take an ISO language code, such as en-US
or he-IL
and return a direction. You can get a valid value to pass into these methods from NSLocale
’s languageCode
property.
These class methods are supported as far back as iOS 4.0 and are safe to use in app extensions. They also exist on other Apple platforms: macOS 10.6, tvOS 9.0, and watchOS 2.0. I wouldn’t base the majority of my app’s layout on it, but it’s certainly useful to have them in your toolkit.
It’s also notable that, unlike the other APIs mentioned here, these methods can return top-to-bottom and bottom-to-top, besides for the expected left-to-right and right-to-left values. English, for example, has a character direction of left-to-right and line direction of top-to-bottom.
In my experiments with Xcode Playgrounds, I did not find any macOS languages that assigned a line direction that wasn’t top-to-bottom.
Explicit Layout Direction with UIApplication & NSApplication
The second technique is to reference UIApplication
’s userInterfaceLayoutDirection
property. This is probably the earliest API explicitly for view layout. Available since iOS 5.0, this property determines the general direction of the app. NSApplication
has this property, too, as of macOS 10.6.
On both platforms, userInterfaceLayoutDirection
is either .rightToLeft
or .leftToRight
, based on the user’s systemwide preferences. The catch here is that since this property is accessible on a sharedApplication
instance, it won’t work in app extensions.
Another Writing Direction Trick, with NSParagraphStyle
NSParagraphStyle
gives us another way to check for the default text layout direction. defaultWritingDirectionForLanguage(_:)
takes an ISO language code, and returns one of three NSWritingDirection
values: .natural
, .leftToRight
, or .rightToLeft
. This was added to iOS 6 with a slew of typesetting improvements, and was around on macOS since 10.2. You can override the writing direction of an NSMutableParagraphStyle
instance by setting its baseWritingDirection
yourself.
UIView & NSView Semantic Content Attributes watchOS
Next up is UIView
’s userInterfaceLayoutDirection(for attribute:)
method. Added in iOS 9, you can use this to check the layout direction on a specific view or control. You can set the semanticContentAttribute
manually, to force specific views to defy the rest of your app’s layout. You can use values such as .forceLeftToRight
and .forceRightToLeft
. The documentation says you should use this API for media playback controls. Another use case might be providing a manual language override for your app. (Really, though, just localize properly.)
There is a similar property on NSView
called userInterfaceLayoutDirection
. You can set it to an NSUserInterfaceLayoutDirection
value: .leftToRight
or .rightToLeft
. WKInterfaceControl
has a setSemanticContentAttribute(_:)
method for watchOS 2.1 to set (but not get) the semantic content attributes.
The Modern Approach: UITraitCollection
Finally, there’s UITraitCollection
, which was added in iOS 8 to help deal with the wide variety of screen resolutions stemming from all of the iOS device screen resolutions and orientations. Trait collections added a property in iOS 10 called layoutDirection
. Since every UIView
conforms to UITraitEnvironment
, you can access this property on any UIView
on iOS 10 or higher.
So there you have it. Five techniques to get and set layout direction on iOS, macOS, tvOS, and watchOS. Oh, here’s the chart:
Class | Method | Version Added |
NSLocale |
characterDirection(forLanguage:) |
iOS 4, macOS 10.6, tvOS 9, watchOS 2 |
NSLocale |
lineDirection(forLanguage:) |
iOS 4, macOS 10.6, tvOS 9.0, watchOS 2 |
UIApplication |
userInterfaceLayoutDirection |
iOS 5, tvOS 9 |
NSApplication |
userInterfaceLayoutDirection |
macOS 10.6 |
NSParagraphStyle |
defaultWritingDirectionForLanguage(_:) |
iOS 6, macOS 10.2, tvOS 9, watchOS 2 |
NSParagraphStyle |
baseWritingDirection |
iOS 6, macOS 10.2, tvOS 9, watchOS 2 |
UIView |
userInterfaceLayoutDirection(for attribute:) |
iOS 9, tvOS 9 |
UIView |
semanticContentAttribute |
iOS 9, tvOS 9 |
NSView |
userInterfaceLayoutDirection |
macOS 10.6 |
WKInterfaceControl |
setSemanticContentAttribute(_:) |
iOS 8.2, watchOS 2.1 |
UITraitCollection |
layoutDirection |
iOS 10, tvOS 10 |
In case you missed the link above, here’s a link to download the bonus playground.