如何在 TextField (SwiftUI) 上添加底线
How can I add a bottom line on TextField (SwiftUI)
我使用 Rectangle()
在 TextField (SwiftUI) 上添加底部边框
但我想使用 protocol TextFieldStyle
作为 TextField 样式的底线,如 RoundedBorderTextFieldStyle
如何在不使用 Rectangle 的情况下为 TextField 自定义样式?
https://developer.apple.com/documentation/swiftui/staticmember
struct ContentView : View {
@State private var username = "Text Hellow"
var body: some View {
VStack() {
TextField($username)
.foregroundColor(Color.yellow)
Rectangle()
.frame(height: 1.0, alignment: .bottom)
.relativeWidth(1)
.foregroundColor(Color.red)
}
.padding()
}
func editChanged() {
}
}
#if DEBUG
struct ContentView_Previews : PreviewProvider {
static var previews: some View {
ContentView()
}
}
#endif
要定义自定义样式,您可以使用下面的代码。 Rectangle 最终被使用,但在自定义样式中,而不是在您的视图代码中。这是你想要的吗?
注意:已在 Beta 3 上测试
要使用您的自定义初始化程序:
struct ContentView : View {
@State private var username = ""
var body: some View {
VStack {
TextField("Enter text", text: $username)
.textFieldStyle(.myOwnStyle)
}.frame(width:300)
}
}
您的自定义初始化程序实现:
public struct MyOwnTextFieldStyle: TextFieldStyle {
public func _body(configuration: TextField<Self.Label>) -> some View {
VStack() {
configuration
.border(Color.blue, width: 2)
.foregroundColor(Color.yellow)
Rectangle()
.frame(height: 1.0, alignment: .bottom)
.relativeWidth(1)
.foregroundColor(Color.red)
}
}
}
extension StaticMember where Base : TextFieldStyle {
public static var myOwnStyle: MyOwnTextFieldStyle.Member {
StaticMember<MyOwnTextFieldStyle>(MyOwnTextFieldStyle())
}
}
不过,kontiki 的解决方案不适用于 Beta 6。
所以我创建了一个解决方案,我在新视图中嵌入了一个 TextField 和绘制的底线。您可以只复制代码并通过编写 TextFieldWithBottomLine(placeholder: "My placeholder")
使用它,在视图中使用它看起来像:
我首先创建的是一条水平线:
import SwiftUI
struct HorizontalLineShape: Shape {
func path(in rect: CGRect) -> Path {
let fill = CGRect(x: 0, y: 0, width: rect.size.width, height: rect.size.height)
var path = Path()
path.addRoundedRect(in: fill, cornerSize: CGSize(width: 2, height: 2))
return path
}
}
struct HorizontalLine: View {
private var color: Color? = nil
private var height: CGFloat = 1.0
init(color: Color, height: CGFloat = 1.0) {
self.color = color
self.height = height
}
var body: some View {
HorizontalLineShape().fill(self.color!).frame(minWidth: 0, maxWidth: .infinity, minHeight: height, maxHeight: height)
}
}
struct HorizontalLine_Previews: PreviewProvider {
static var previews: some View {
HorizontalLine(color: .black)
}
}
接下来我创建一个视图,其中包含一个 TextField 和下面的 HorizontalLine:
import SwiftUI
struct TextFieldWithBottomLine: View {
@State var text: String = ""
private var placeholder = ""
private let lineThickness = CGFloat(2.0)
init(placeholder: String) {
self.placeholder = placeholder
}
var body: some View {
VStack {
TextField(placeholder, text: $text)
HorizontalLine(color: .black)
}.padding(.bottom, lineThickness)
}
}
struct TextFieldWithBottomLine_Previews: PreviewProvider {
static var previews: some View {
TextFieldWithBottomLine(placeholder: "My placeholder")
}
}
为了查看实际效果,我创建了一个示例视图:
import SwiftUI
struct SampleView: View {
@State var text: String = ""
@ObservedObject var model: LoginModel
init() {
self.model = LoginModel()
}
init(model: LoginModel) {
self.model = model
}
var body: some View {
TextFieldWithBottomLine(placeholder: "My placeholder").padding(24)
}
func initialActions() {
}
func reactOnButtonClick() {
}
}
struct SampleView_Previews: PreviewProvider {
static var previews: some View {
SampleView()
}
}
符合 TextFieldStyle 协议
struct BottomLineTextFieldStyle: TextFieldStyle {
func _body(configuration: TextField<Self._Label>) -> some View {
VStack() {
configuration
Rectangle()
.frame(height: 0.5, alignment: .bottom)
.foregroundColor(Color.secondary)
}
}
}
用法:
TextField("Name", text: $presenter.name).textFieldStyle(BottomLineTextFieldStyle())
换个方式试试
struct ContentView: View {
@State var name: String = ""
var body: some View {
VStack {
// change frame as per requirement
TextField("Please enter text", text: $name)
.frame(width: 300, height: 30, alignment: .center)
.background(Color.gray.opacity(0.2))
LineView().frame(width: 300, height: 1.0, alignment: .center)
}
}
}
LineView :为 UIView
创建自定义包装器
struct LineView: UIViewRepresentable {
typealias UIViewType = UIView
func makeUIView(context: UIViewRepresentableContext<LineView>) -> UIView {
let view = UIView()
view.backgroundColor = UIColor.lightGray
return view
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LineView>) {
}
}
我的解决方案是在文本框下添加分隔线:
private let textFieldLength:CGFloat = 200
var body: some View {
VStack {
TextField("placeHolder", text: .constant("")).frame(width: textFieldLength, height: 30, alignment: .center)
Divider().frame(width: textFieldLength, height: 2, alignment: .center)
}
}
或使用覆盖
TextField("", text: .constant(""))
.frame(width: textFieldLength, height: 30, alignment: .center)
.multilineTextAlignment(.leading)
.overlay(Divider().frame(width: textFieldLength, height: 2, alignment: .center), alignment: .bottom)
分频器
可用于分隔其他内容的视觉元素。
您可以设置color
和height
Divider()
.frame(height: 1)
.padding(.horizontal, 30)
.background(Color.red)
struct LoginField: View {
@State private var email: String = ""
@State private var password: String = ""
var body: some View {
VStack {
TextField("Email", text: $email)
.padding(.horizontal, 30).padding(.top, 20)
Divider()
.padding(.horizontal, 30)
TextField("Password", text: $password)
.padding(.horizontal, 30).padding(.top, 20)
Divider()
.padding(.horizontal, 30)
Spacer()
}
}
}
使用.overlay
TextField("mail@example.com", text: $email)
.overlay(VStack{Divider().offset(x: 0, y: 15)})
使用VStack
是为了确保Divider
始终是水平的。
我使用 Rectangle()
在 TextField (SwiftUI) 上添加底部边框
但我想使用 protocol TextFieldStyle
作为 TextField 样式的底线,如 RoundedBorderTextFieldStyle
如何在不使用 Rectangle 的情况下为 TextField 自定义样式?
https://developer.apple.com/documentation/swiftui/staticmember
struct ContentView : View {
@State private var username = "Text Hellow"
var body: some View {
VStack() {
TextField($username)
.foregroundColor(Color.yellow)
Rectangle()
.frame(height: 1.0, alignment: .bottom)
.relativeWidth(1)
.foregroundColor(Color.red)
}
.padding()
}
func editChanged() {
}
}
#if DEBUG
struct ContentView_Previews : PreviewProvider {
static var previews: some View {
ContentView()
}
}
#endif
要定义自定义样式,您可以使用下面的代码。 Rectangle 最终被使用,但在自定义样式中,而不是在您的视图代码中。这是你想要的吗?
注意:已在 Beta 3 上测试
要使用您的自定义初始化程序:
struct ContentView : View {
@State private var username = ""
var body: some View {
VStack {
TextField("Enter text", text: $username)
.textFieldStyle(.myOwnStyle)
}.frame(width:300)
}
}
您的自定义初始化程序实现:
public struct MyOwnTextFieldStyle: TextFieldStyle {
public func _body(configuration: TextField<Self.Label>) -> some View {
VStack() {
configuration
.border(Color.blue, width: 2)
.foregroundColor(Color.yellow)
Rectangle()
.frame(height: 1.0, alignment: .bottom)
.relativeWidth(1)
.foregroundColor(Color.red)
}
}
}
extension StaticMember where Base : TextFieldStyle {
public static var myOwnStyle: MyOwnTextFieldStyle.Member {
StaticMember<MyOwnTextFieldStyle>(MyOwnTextFieldStyle())
}
}
不过,kontiki 的解决方案不适用于 Beta 6。
所以我创建了一个解决方案,我在新视图中嵌入了一个 TextField 和绘制的底线。您可以只复制代码并通过编写 TextFieldWithBottomLine(placeholder: "My placeholder")
使用它,在视图中使用它看起来像:
我首先创建的是一条水平线:
import SwiftUI
struct HorizontalLineShape: Shape {
func path(in rect: CGRect) -> Path {
let fill = CGRect(x: 0, y: 0, width: rect.size.width, height: rect.size.height)
var path = Path()
path.addRoundedRect(in: fill, cornerSize: CGSize(width: 2, height: 2))
return path
}
}
struct HorizontalLine: View {
private var color: Color? = nil
private var height: CGFloat = 1.0
init(color: Color, height: CGFloat = 1.0) {
self.color = color
self.height = height
}
var body: some View {
HorizontalLineShape().fill(self.color!).frame(minWidth: 0, maxWidth: .infinity, minHeight: height, maxHeight: height)
}
}
struct HorizontalLine_Previews: PreviewProvider {
static var previews: some View {
HorizontalLine(color: .black)
}
}
接下来我创建一个视图,其中包含一个 TextField 和下面的 HorizontalLine:
import SwiftUI
struct TextFieldWithBottomLine: View {
@State var text: String = ""
private var placeholder = ""
private let lineThickness = CGFloat(2.0)
init(placeholder: String) {
self.placeholder = placeholder
}
var body: some View {
VStack {
TextField(placeholder, text: $text)
HorizontalLine(color: .black)
}.padding(.bottom, lineThickness)
}
}
struct TextFieldWithBottomLine_Previews: PreviewProvider {
static var previews: some View {
TextFieldWithBottomLine(placeholder: "My placeholder")
}
}
为了查看实际效果,我创建了一个示例视图:
import SwiftUI
struct SampleView: View {
@State var text: String = ""
@ObservedObject var model: LoginModel
init() {
self.model = LoginModel()
}
init(model: LoginModel) {
self.model = model
}
var body: some View {
TextFieldWithBottomLine(placeholder: "My placeholder").padding(24)
}
func initialActions() {
}
func reactOnButtonClick() {
}
}
struct SampleView_Previews: PreviewProvider {
static var previews: some View {
SampleView()
}
}
符合 TextFieldStyle 协议
struct BottomLineTextFieldStyle: TextFieldStyle {
func _body(configuration: TextField<Self._Label>) -> some View {
VStack() {
configuration
Rectangle()
.frame(height: 0.5, alignment: .bottom)
.foregroundColor(Color.secondary)
}
}
}
用法:
TextField("Name", text: $presenter.name).textFieldStyle(BottomLineTextFieldStyle())
换个方式试试
struct ContentView: View {
@State var name: String = ""
var body: some View {
VStack {
// change frame as per requirement
TextField("Please enter text", text: $name)
.frame(width: 300, height: 30, alignment: .center)
.background(Color.gray.opacity(0.2))
LineView().frame(width: 300, height: 1.0, alignment: .center)
}
}
}
LineView :为 UIView
struct LineView: UIViewRepresentable {
typealias UIViewType = UIView
func makeUIView(context: UIViewRepresentableContext<LineView>) -> UIView {
let view = UIView()
view.backgroundColor = UIColor.lightGray
return view
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LineView>) {
}
}
我的解决方案是在文本框下添加分隔线:
private let textFieldLength:CGFloat = 200
var body: some View {
VStack {
TextField("placeHolder", text: .constant("")).frame(width: textFieldLength, height: 30, alignment: .center)
Divider().frame(width: textFieldLength, height: 2, alignment: .center)
}
}
或使用覆盖
TextField("", text: .constant(""))
.frame(width: textFieldLength, height: 30, alignment: .center)
.multilineTextAlignment(.leading)
.overlay(Divider().frame(width: textFieldLength, height: 2, alignment: .center), alignment: .bottom)
分频器
可用于分隔其他内容的视觉元素。
您可以设置color
和height
Divider()
.frame(height: 1)
.padding(.horizontal, 30)
.background(Color.red)
struct LoginField: View {
@State private var email: String = ""
@State private var password: String = ""
var body: some View {
VStack {
TextField("Email", text: $email)
.padding(.horizontal, 30).padding(.top, 20)
Divider()
.padding(.horizontal, 30)
TextField("Password", text: $password)
.padding(.horizontal, 30).padding(.top, 20)
Divider()
.padding(.horizontal, 30)
Spacer()
}
}
}
使用.overlay
TextField("mail@example.com", text: $email)
.overlay(VStack{Divider().offset(x: 0, y: 15)})
使用VStack
是为了确保Divider
始终是水平的。