Search code examples
ioslayoutuikitswiftui

What is UIView readableContentGuide equivalent thing in the SwiftUI


The UIView has the readableContentGuide so layout could be related to screen size and make sure content always readable. I can not find any pieces of information about those things in the SwiftUI.

What's the equivalent thing in the SwiftUI could be using? And if not, could we use the SwiftUI component to build the same effect in UIKit?

Thanks.


Solution

  • The other solutions didn't work for me because they relied on a hardcoded amount of padding or dropping down to UIKit, which isn't great for multi-platform SwiftUI apps that need to look nice on all the differently-sized iPhones, iPads, and Macs. So as there still isn't a native SwiftUI equivalent (as of iOS 15/macOS 12), I thought I'd make an alternative.

    My alternative is to calculate the amount of padding based on:

    • the current width of the view
    • and some ideal, readable width
    struct ReadabilityPadding: ViewModifier {
        let isEnabled: Bool
        @ScaledMetric private var unit: CGFloat = 20
    
        func body(content: Content) -> some View {
            // Use a GeometryReader here to get view width.
            GeometryReader { geometryProxy in
                content
                    .padding(.horizontal, padding(for: geometryProxy.size.width))
            }
        }
    
        private func padding(for width: CGFloat) -> CGFloat {
            guard isEnabled else { return 0 }
    
            // The internet seems to think the optimal readable width is 50-75
            // characters wide; I chose 70 here. The `unit` variable is the 
            // approximate size of the system font and is wrapped in
            // @ScaledMetric to better support dynamic type. I assume that 
            // the average character width is half of the size of the font. 
            let idealWidth = 70 * unit / 2
    
            // If the width is already readable then don't apply any padding.
            guard width >= idealWidth else {
                return 0
            }
    
            // If the width is too large then calculate the padding required
            // on either side until the view's width is readable.
            let padding = round((width - idealWidth) / 2)
            return padding
        }
    }
    

    Here is how it looks on different devices (top is normal, bottom is with the ReadabilityPadding view modifier applied):

    screenshot comparison