October 19, 2020

RTL Considerations

Here are some things to keep in mind so that you app can handle right-to-left languages like Arabic and Hebrew.

Guidelines

While Apple provides information about supporting RTL (right to left), I generally refer to Mozilla's RTL Guidelines and Google's for information about what should flip for RTL and what should not.

RTL Images

Mirroring images which are packaged with the app are handled automatically by the system. All we need to do is mark the images in the asset catalog as left-to-right versions and the system will take care of flipping them for RTL.

The default value for Direction is Fixed which is perfect for images that do not need to be flipped such as smiley-faces and images of clocks. Select Left to Right for images such as navigation arrows which should flip in RTL.

Trailing Constraints

Views generally need trailing constraints specified in Autolayout and not only leading constraints. By specifying trailing constraints even when they’re seemingly not necessary, you can make sure that in RTL mode, elements will layout in a nice way.

textContentType

Text fields should have their textContentType set according to the content you expect in the field. This will help iOS to flip the proper fields as well as offer better auto-complete suggestions. Phone numbers, for example, should not be flipped in RTL.

UICollectionView Layouts

Horizontally-scrolling collection view layouts usually will need to be able to scroll in the opposite direction. This can be handled automatically by adding one override to your layout subclass:

override var flipsHorizontallyInOppositeLayoutDirection: Bool {
   return true
}

If you do not allow the layout to flip, the page indicator below likely will. If you mean for the collection view not to flip, you'll need to prevent the page indicator from flipping to match to avoid confusion.

Directional Insets

Where possible, use NSDirectionalEdgeInsets instead of UIEdgeInsets. If you are supporting a version of iOS so old you cannot use this (directional insets were added in iOS 11), you'll need to write your own convenience function to flip your right and left insets based on the component's effectiveUserInterfaceLayoutDirection.

Un-mirrored UI Elements

Some elements, such as phone numbers, should not be mirrored. When creating UI elements like this, remember to use right and left anchors explicitly in Autolayout. This includes in views where the element is used. Consider the margins and constraints used to position the element on the screen, if they are based on neighboring leading or trailing anchors, the element will not position correctly in its parent-view.

Text Alignment

For labels and such which should flip for RTL languages, using textAlignment = .left will not display like you'd expect. You can instead chose .natural which will align text to the left for LTR languages and to the right for RTL languages. This is the default for UILabels, so in most case you won't need to write code to change it at all.

Manual Frame Math

Sometimes, for performance reasons for example, you may want to avoid using Autolayout altogether and instead use frames to position elements manually. In this case, it's important to factor in the current value of the UIView property effectiveUserInterfaceLayoutDirection. A function like this could be used to adjust their frame math based on direction:

func directionAdjustedFrame(leading: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat) -> CGRect {
    if effectiveUserInterfaceLayoutDirection == .leftToRight {
        return CGRect(
            x: leading,
            y: y,
            width: width,
            height: height
        )
    } else {
        return CGRect(
            x: bounds.width - width - leading,
            y: y,
            width: width,
            height: height
        )
    }
}

Language-Based Detection

For some content, such as chat conversations, you may want the text alignment to match the language of the content instead of the alignment the system would chose to match the system language. In this case, you can use functions like CFStringTokenizerCopyBestStringLanguage to attempt to detect the language of the content to decide alignment. This is not 100% accurate, but they can offer a good-enough solution without dramatically impacting performance.