【问题标题】:How can I add a bottom line on TextField (SwiftUI)如何在 TextField (SwiftUI) 上添加底线
【发布时间】:2019-11-22 22:01:24
【问题描述】:

我使用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

【问题讨论】:

    标签: swift textfield swiftui


    【解决方案1】:

    分频器

    可用于分隔其他内容的视觉元素。

    您可以设置colorheight

    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()
            }
        }
    }
    

    【讨论】:

    • 请注意,您可以使用.padding(.horizontal) 获得平台建议的间距(不指定明确的长度)
    【解决方案2】:

    使用.overlay

    TextField("mail@example.com", text: $email)
            .overlay(VStack{Divider().offset(x: 0, y: 15)})
    

    VStack的使用是为了保证Divider总是水平的。

    【讨论】:

    • 简直太棒了。最佳答案。
    【解决方案3】:

    但是,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 和下面的 Horizo​​ntalLine 的视图:

    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()
        }
    }
    

    【讨论】:

    • 如果不支持动画,我们可以将colorheight设为private let
    【解决方案4】:

    要定义自定义样式,您可以使用以下代码。 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())
        }
    }
    

    【讨论】:

    • 是的,这就是我要找的。谢谢。
    • beta 6 中不再有 StaticMember。有替代品吗?
    【解决方案5】:

    符合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())
    

    【讨论】:

      【解决方案6】:

      我的解决方案是在文本字段下添加一个分隔符:

      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)
      

      【讨论】:

      • 我认为overlay的解决方案比其他的更干净
      • 我喜欢第一个 sn-p 的简单性。很棒。
      【解决方案7】:

      换一种方式试试

      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>) {
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2022-08-02
        • 2015-01-04
        • 2020-06-11
        • 2022-07-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-11-12
        • 2020-06-05
        相关资源
        最近更新 更多