iosswiftswiftuiios17

Adjust padding on wrapped lines in TextField (Swift)


I am using a multiline text field that wraps the text vertically to new lines. Is there a way to adjust the padding for the wrapped lines, not including the first line? I am trying to have subsequent lines use a negative leading padding, or more generally, have them appear right under the Text("Word").

HStack(alignment: .top) {
    Text("Word")
        .foregroundColor(.black)
        .padding(.top, 8)

    TextField("", text: $PresentationManager.entryText, prompt: Text("required"), axis: .vertical)
        .foregroundColor(.black)
        .focused($isTextFieldFocused)
        .background(Color.clear)
        .padding(.top, 8)
        .multilineTextAlignment(.leading)
}

In the image below, I am trying to get the second line of the wrapped text to have less padding while maintaining the positioning of the first line of the entry.

enter image description here


Solution

  • One way to achieve this would be to use a ZStack to superimpose the TextField over the Text, then add leading spaces to the text being entered.

    struct ContentView: View {
        private let leadingSpaces = "             "
        @State private var entryText = ""
        @FocusState private var isTextFieldFocused: Bool
    
        var body: some View {
            ZStack(alignment: .topLeading) {
                Text("Word")
                    .foregroundStyle(.black)
                    .padding(.top, 8)
    
                TextField("", text: $entryText, axis: .vertical)
                    .foregroundStyle(.black)
                    .focused($isTextFieldFocused)
                    .background(alignment: .topLeading) {
                        (Text(leadingSpaces) + Text("required"))
                            .foregroundStyle(.placeholder)
                            .opacity(entryText.trimmingCharacters(in: .whitespaces).isEmpty ? 1 : 0)
                    }
                    .padding(.top, 8)
                    .multilineTextAlignment(.leading)
                    .onChange(of: entryText, initial: true) { oldVal, newVal in
                        let trimmedText = newVal.drop { $0 == " " }
                        if !newVal.hasPrefix(leadingSpaces) {
                            entryText = leadingSpaces + trimmedText
                        }
                        // PresentationManager.entryText = String(trimmedText)
                    }
            }
            .border(.red)
            .padding(32)
        }
    }
    

    Screenshot