ioslocalizationuikituiedgeinsetsinsets

Support NSDirectionalEdgeInsets for UIButton's contentEdgeInsets?


I would like to use NSDirectionalEdgeInsets to UIButton's contentEdgeInsets and titleEdgeInsets. Is that possible?

Background

For localization purposes, an iOS application may be required to adapt to both Left-To-Right and Right-To-Left languages when setting up a component's insets.

Apple introduced NSDirectionalEdgeInsets with iOS11, unfortunately this was applied to few properties only like directionalLayoutMargins, deprecating UIEdgeInsets which is used for layoutMargins.

NSDirectionalEdgeInsets honors interface layout direction using leading and trailing modifiers instead of left and right which is used by its counterpart UIEdgeInsets.

Current Improper Solution

Using the following code for every button/view with every modified UIEdgeInsets property is quite cumbersome and prone to errors when making changes:

let isRTL: UIUserInterfaceLayoutDirection = // logic to determine language direction 

if isRTL == .leftToRight {
    nextButton.contentEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0)
} else {
    nextButton.contentEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 10)
}

Solution

  • Solution

    import UIKit
    
    extension UIView {
        // Returns `UIEdgeInsets` set using `leading` and `trailing` modifiers adaptive to the language direction
        func getDirectionalUIEdgeInsets(top: CGFloat, leading: CGFloat, bottom: CGFloat, trailing: CGFloat) -> UIEdgeInsets {
            // NOTE: this wil be deprecated when Apple use `NSDirectioanlEdgeInsets` (https://developer.apple.com/documentation/uikit/nsdirectionaledgeinsets) for your insets property instead of `UIEdgeInsets`
    
            if self.userInterfaceLayoutDirection == .leftToRight {
                return UIEdgeInsets(top: top, left: leading, bottom: bottom, right: trailing)
            } else {
                return UIEdgeInsets(top: top, left: trailing, bottom: bottom, right: leading)
            }
        }
    
        /// Returns text and UI direction based on current view settings
        var userInterfaceLayoutDirection: UIUserInterfaceLayoutDirection
        {
            if #available(iOS 9.0, *) {
                return UIView.userInterfaceLayoutDirection(for: self.semanticContentAttribute)
            } else {
                return UIApplication.shared.userInterfaceLayoutDirection
            }
        }
    }
    

    Usage

    nextButton.contentEdgeInsets = nextButton.getDirectionalUIEdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 0)
    

    I looked for an answer on Stackoverflow and found nothing. Thus I am sharing my answer.

    Credits

    Thanks to this Stackoverflow answer by David Rysanek for userInterfaceLayoutDirection extension.