我正在制作一个 TextField,在输入时,它有一个浮动标签,该标签会切入 TextField 的边框。对于不同的占位符,修剪功能无法按预期工作,例如,如果修剪功能对占位符“名称”正常工作,则对占位符“输入名称”不起作用。此外,第一次在字段中输入时,FocusState 会更改为 false。为什么会发生这种情况?有人可以帮忙修复这些问题吗?
视图代码:
struct RoundedTextField: View {
@Binding var text: String
let placeholder: String
init(_ placeholder: String, text: Binding<String>) {
self.placeholder = placeholder
self._text = text
}
@State private var width: CGFloat = .zero
@State private var labelWidth: CGFloat = .zero
@FocusState private var isFieldFocused
var body: some View {
ZStack(alignment: .leading) {
Text(placeholder)
.foregroundStyle(text.isEmpty ? Color(.placeholderText) : .caribe)
.padding(.horizontal, 25)
.offset(y: text.isEmpty ? 0 : -37)
.scaleEffect(text.isEmpty ? 1 : 0.8, anchor: .leading)
.overlay {
GeometryReader { proxy in
Color.clear
.onChange(of: text) { _, _ in
labelWidth = proxy.size.width
}
}
}
TextField("", text: $text)
.textFieldStyle(RoundedTextFieldStyle(isFieldFocused: isFieldFocused, textIsEmpty: text.isEmpty, width: width, labelWidth: labelWidth))
.focused($isFieldFocused)
.overlay {
GeometryReader { proxy in
Color.clear
.onChange(of: text) { _, _ in
width = proxy.size.width * (text.isEmpty ? 1 : 0.8)
}
}
}
}
.animation(.default, value: text)
.padding()
}
}
struct RoundedTextFieldStyle: TextFieldStyle {
var isFieldFocused: Bool
var textIsEmpty: Bool
var width: CGFloat
var labelWidth: CGFloat
func _body(configuration: TextField<Self._Label>) -> some View {
configuration
.padding(.vertical)
.padding(.horizontal, 25)
.frame(height: 60)
.if(textIsEmpty) { view in
view.background {
RoundedRectangle(cornerRadius: 10)
.stroke(isFieldFocused ? .caribe : .gray, lineWidth: isFieldFocused ? 2 : 1)
}
}
.if(!textIsEmpty) { view in
view.background {
ZStack {
RoundedRectangle(cornerRadius: 10)
.trim(from: 0, to: 0.55)
.stroke(isFieldFocused ? .caribe : .gray, lineWidth: isFieldFocused ? 2 : 1)
RoundedRectangle(cornerRadius: 10)
.trim(from: 0.565 + 0.2 * (labelWidth / width), to: 1)
.stroke(isFieldFocused ? .caribe : .gray, lineWidth: isFieldFocused ? 2 : 1)
}
}
}
}
}
条件修饰符:
extension View {
@ViewBuilder
func `if`<Content: View>(_ condition: Bool, transform: (Self)-> Content) -> some View {
if condition {
transform(self)
} else {
self
}
}
}
如果我理解正确的话,要求是在边框中创建一个空间,以便在文本字段中输入任何文本时以较小的尺寸显示标签。
用来
.trim
实现这一点会比较困难。我建议,更简单的方法是,当占位符文本移动到边框上方的位置时,为其应用背景填充。FocusState
,因为发生的状态更新较少。为了实现此功能,占位符需要显示为 中的顶层
ZStack
。因此,为了防止它拦截手势,您需要.allowsHitTesting(false)
对其应用。您可以始终将背景应用于占位符。但是,这可能会导致闪烁的光标消失,因为占位符现在是顶层。如果在占位符移动到位时使用过渡效果显示填充的背景,动画效果也会略好一些。
其他建议:
将
.animation
修饰符改为使用value: textField.isEmpty
,以便它仅在空状态改变时适用。如果字段没有焦点,则始终以占位符颜色显示较小的标签。
以下是更新后的示例,以显示其工作原理: