简体   繁体   中英

TextField stroke border text with SwiftUI

Here is what I've done, but the problem is with Text background. It can be implemented on white background by setting the Text's background to white as well, but in case of image background it stays "strikedthrough". You can find a source code below where I tried to make it as close to the result as possible. How it could be resolved?

输入

struct CustomTextField: View {
    let placeholder: String
    @Binding var text: String

    var body: some View {
        TextField("", text: $text)
            .placeholder(when: $text.wrappedValue.isEmpty,
                         alignment: .leading,
                         placeholder: {
                Text(placeholder)
                    .foregroundColor(.gray)
                    .font(.system(size: 20))
                    .padding(.leading, 15)
            })
            .foregroundColor(.gray)
            .font(.system(size: 20))
            .padding(EdgeInsets(top: 15, leading: 10, bottom: 15, trailing: 10))
            .background {
                ZStack {
                    RoundedRectangle(cornerRadius: 5)
                        .stroke(.gray, lineWidth: 1)
                    Text(placeholder)
                        .foregroundColor(.gray)
                        .padding(2)
                        .font(.caption)
                        .frame(maxWidth: .infinity,
                               maxHeight: .infinity,
                               alignment: .topLeading)
                        .offset(x: 20, y: -10)
                }
            }
    }
}

Here is a solution using .trim on two RoundedRectangles based on the length of the label text, which should give you the result you want:

struct CustomTextField: View {
    let placeholder: String
    @Binding var text: String

    @State private var width = CGFloat.zero
    @State private var labelWidth = CGFloat.zero

    var body: some View {
        TextField(placeholder, text: $text)
            .foregroundColor(.gray)
            .font(.system(size: 20))
            .padding(EdgeInsets(top: 15, leading: 10, bottom: 15, trailing: 10))
            .background {
                ZStack {
                    RoundedRectangle(cornerRadius: 5)
                        .trim(from: 0, to: 0.55)
                        .stroke(.gray, lineWidth: 1)
                    RoundedRectangle(cornerRadius: 5)
                        .trim(from: 0.565 + (0.44 * (labelWidth / width)), to: 1)
                        .stroke(.gray, lineWidth: 1)
                    Text(placeholder)
                        .foregroundColor(.gray)
                        .overlay( GeometryReader { geo in Color.clear.onAppear { labelWidth = geo.size.width }})
                        .padding(2)
                        .font(.caption)
                        .frame(maxWidth: .infinity,
                               maxHeight: .infinity,
                               alignment: .topLeading)
                        .offset(x: 20, y: -10)

                }
            }
            .overlay( GeometryReader { geo in Color.clear.onAppear { width = geo.size.width }})
            .onChange(of: width) { _ in
                print("Width: ", width)
            }
            .onChange(of: labelWidth) { _ in
                print("labelWidth: ", labelWidth)
            }

        
   }
}

在此处输入图像描述

Here is my version of the TextField.

在此处输入图像描述

在此处输入图像描述

struct TextInputField: View {
    let placeHolder: String
    @Binding var textValue: String
    
    var body: some View {
        ZStack(alignment: .leading) {
            Text(placeHolder)
                .foregroundColor(Color(.placeholderText))
                .offset(y: textValue.isEmpty ? 0 : -25)
                .scaleEffect(textValue.isEmpty ? 1: 0.8, anchor: .leading)
            TextField("", text: $textValue)
        }
        .padding(.top, textValue.isEmpty ? 0 : 15)
        .frame(height: 52)
        .padding(.horizontal, 16)
        .overlay(RoundedRectangle(cornerRadius: 12).stroke(lineWidth: 1).foregroundColor(.gray))
        .animation(.default)
    }
}

The above code is to create a CustomTextField named TextInputField. If you want to use the about component

struct ContentView: View {
    
    @State var itemName: String = ""
    
    var body: some View {
         TextInputField(placeHolder: "Item Name": textValue: $itemName)
    }
}

I'm using @ChrisR's answer as a base for my answer, so instead of doing all that calculation with two RoundedRectangles and label's width; you can position the Text on top and give it a background matching the app's background color

    struct FloatingTitleTextField: View {
    
    let placeholder: String
    @Binding var text: String
    
    var body: some View {
        TextField("Placeholder", text: $text)
            .foregroundColor(.gray)
            .font(.system(size: 20))
            .padding(EdgeInsets(top: 15, leading: 10, bottom: 15, trailing: 10))
            .background {
                ZStack {
                    RoundedRectangle(cornerRadius: 5)
                        .stroke(.black, lineWidth: 1)
                    Text(placeholder)
                        .foregroundColor(.gray)
                        .padding(2)
                        .background()
                        .frame(maxWidth: .infinity,
                               maxHeight: .infinity,
                               alignment: .topLeading)
                    
                        .offset(x: 20, y: -10)
                }
            }
    }
}

When calling the textfield you do it like this

FloatingTitleTextField(placeholder: "Placeholder", text: $text)

I also found this article very helpful

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM