swiftswiftuilayout

Text and TextField margins differences


Does anyone know why there is a difference in the margins of the Text and TextField vertex and is there any way to make them equal without hardcoding the height.

Example code:

import SwiftUI

@main
struct TestAppApp: App {
    var body: some Scene {
        WindowGroup {
            HStack {
                Text("Hello")
                    .font(.callout)
                    .padding(16)
                    .border(.red)
                
                TextField("", text: .constant("Hello"))
                    .font(.callout)
                    .padding(16)
                    .border(.red)
            }
            .border(.green)
        }
    }
}

enter image description here

UPDATE: I mean that at the top and bottom, the red border is visible for the Text, but not for the TextField

enter image description here


Solution

  • By default, a SwiftUI TextField is programmed to take up all the available space. It has to do with something called 'intrinsic content size'. There is a UIKit document describing that behaviour. SwiftUI is also affected by this for some views.

    SwiftUI has a view modifier called fixedSize that enables you to set this behaviour for specified axis. So to 'fix' your issue:

    TextField("", text: .constant("Hello"))
        .font(.callout)
        .padding(16)
        .border(.red)
        .fixedSize(horizontal: true, vertical: false)
    

    UPDATE For whatever reason, the Text has a different height than the TextField by default. You can check that by adding a background with a GeometryReader in it:

    .background {
        GeometryReader { reader in
            let size = String(describing: reader.size.height)
            Text(size)
        }
    }
    

    This will show you that the Text height is ~19.3 and the TextField height is 21.0. Now, if you want to 'fix' the height of the TextField you can apply negative padding:

    TextField("", text: $text)
        .font(.callout)
        .foregroundStyle(.clear)
        .padding(-5/6)
        .padding(16)
        .border(.red)
    

    The -5/6 padding is exactly enough to fix the difference between the Text and the TextField.