Add bottom line to view in SwiftUI / Swift / Objective-C / Xamarin

前端 未结 22 2073
自闭症患者
自闭症患者 2020-11-27 09:19

I would like to keep the border at the bottom part only in UITextField. But I don\'t know how we can keep it on the bottom side.

Can you please advise m

相关标签:
22条回答
  • 2020-11-27 09:54

    I am creating custom textField to make it reusable component for SwiftUI

    SwiftUI

    struct CustomTextField: View {
        var placeHolder: String
        @Binding var value: String
        
        var lineColor: Color
        var width: CGFloat
        
        var body: some View {
            VStack {
                TextField(self.placeHolder, text: $value)
                .padding()
                .font(.title)
                
                Rectangle().frame(height: self.width)
                    .padding(.horizontal, 20).foregroundColor(self.lineColor)
            }
        }
    }
    

    Usage:

    @Binding var userName: String
    @Binding var password: String
    
    var body: some View {
        VStack(alignment: .center) {
            CustomTextField(placeHolder: "Username", value: $userName, lineColor: .white, width: 2)
            CustomTextField(placeHolder: "Password", value: $password, lineColor: .white, width: 2)
        }
    }
    


    Swift 5.0

    I am using Visual Formatting Language (VFL) here, This will allow adding a line to any UIControl.

    You can create a UIView extension class like UIView+Extention.swift

    import UIKit
    
    enum LinePosition {
        case top
        case bottom
    }
    
    extension UIView {
        func addLine(position: LinePosition, color: UIColor, width: Double) {
            let lineView = UIView()
            lineView.backgroundColor = color
            lineView.translatesAutoresizingMaskIntoConstraints = false // This is important!
            self.addSubview(lineView)
    
            let metrics = ["width" : NSNumber(value: width)]
            let views = ["lineView" : lineView]
            self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[lineView]|", options:NSLayoutConstraint.FormatOptions(rawValue: 0), metrics:metrics, views:views))
    
            switch position {
            case .top:
                self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[lineView(width)]", options:NSLayoutConstraint.FormatOptions(rawValue: 0), metrics:metrics, views:views))
                break
            case .bottom:
                self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[lineView(width)]|", options:NSLayoutConstraint.FormatOptions(rawValue: 0), metrics:metrics, views:views))
                break
            }
        }
    }
    

    Usage:

    textField.addLine(position: .LINE_POSITION_BOTTOM, color: .darkGray, width: 0.5)
    

    Objective C:

    You can add this helper method to your global helper class(I used global class method) or in the same view controller(using an instance method).

    typedef enum : NSUInteger {
        LINE_POSITION_TOP,
        LINE_POSITION_BOTTOM
    } LINE_POSITION;
    
    
    - (void) addLine:(UIView *)view atPosition:(LINE_POSITION)position withColor:(UIColor *)color lineWitdh:(CGFloat)width {
        // Add line
        UIView *lineView = [[UIView alloc] init];
        [lineView setBackgroundColor:color];
        [lineView setTranslatesAutoresizingMaskIntoConstraints:NO];
        [view addSubview:lineView];
        
        NSDictionary *metrics = @{@"width" : [NSNumber numberWithFloat:width]};
        NSDictionary *views = @{@"lineView" : lineView};
        [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[lineView]|" options: 0 metrics:metrics views:views]];
        
        switch (position) {
            case LINE_POSITION_TOP:
                [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[lineView(width)]" options: 0 metrics:metrics views:views]];
                break;
                
            case LINE_POSITION_BOTTOM:
                [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[lineView(width)]|" options: 0 metrics:metrics views:views]];
                break;
            default: break;
        }
    }
    

    Usage:

    [self addLine:self.textField atPosition:LINE_POSITION_TOP withColor:[UIColor darkGrayColor] lineWitdh:0.5];
    

    Xamarin code:

     var border = new CALayer();
     nfloat width = 2;
     border.BorderColor = UIColor.Black.CGColor;
     border.Frame = new CoreGraphics.CGRect(0, textField.Frame.Size.Height - width, textField.Frame.Size.Width, textField.Frame.Size.Height);
     border.BorderWidth = width;
     textField.Layer.AddSublayer(border);
     textField.Layer.MasksToBounds = true;
    
    0 讨论(0)
  • 2020-11-27 09:54

    None of these solutions really met my expectations. I wanted to subclass the TextField since I don't want to set the border manually all the time. I also wanted to change the border color e.g. for an error. So here's my solution with Anchors:

    class CustomTextField: UITextField {
    
        var bottomBorder = UIView()
    
        override func awakeFromNib() {
    
                // Setup Bottom-Border
    
                self.translatesAutoresizingMaskIntoConstraints = false
    
                bottomBorder = UIView.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
                bottomBorder.backgroundColor = UIColor(rgb: 0xE2DCD1) // Set Border-Color
                bottomBorder.translatesAutoresizingMaskIntoConstraints = false
    
                addSubview(bottomBorder)
    
                bottomBorder.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
                bottomBorder.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
                bottomBorder.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
                bottomBorder.heightAnchor.constraint(equalToConstant: 1).isActive = true // Set Border-Strength
    
        }
    }
    

    ---- Optional ----

    To change the color add sth like this to the CustomTextField Class:

    @IBInspectable var hasError: Bool = false {
        didSet {
    
            if (hasError) {
    
                bottomBorder.backgroundColor = UIColor.red
    
            } else {
    
                bottomBorder.backgroundColor = UIColor(rgb: 0xE2DCD1)
    
            }
    
        }
    }
    

    And to trigger the Error call this after you created an instance of CustomTextField

    textField.hasError = !textField.hasError
    

    Hope it helps someone ;)

    0 讨论(0)
  • 2020-11-27 09:54
     extension UITextField {  
      func setBottomBorder(color:String) {
        self.borderStyle = UITextBorderStyle.None
        let border = CALayer()
        let width = CGFloat(1.0)
        border.borderColor = UIColor(hexString: color)!.cgColor
        border.frame = CGRect(x: 0, y: self.frame.size.height - width,   width:  self.frame.size.width, height: self.frame.size.height)
        border.borderWidth = width
        self.layer.addSublayer(border)
        self.layer.masksToBounds = true
       }
    }
    

    and then just do this:

    yourTextField.setBottomBorder(color: "#3EFE46")
    
    0 讨论(0)
  • 2020-11-27 09:54

    You can use this ORGANIZED and can also CUSTOMIZE this extension further:

    "One Line Implementation" in viewDidAppear(so that frame size will be correct):

    // Add layer in your textfield    
    yourTextField.addLayer(.bottom).addPadding(.left)
    
    
    // Extension
        extension UITextField {
    
        enum Position {
            case up, bottom, right, left
        }
    
        //  MARK: - Add Single Line Layer
        func addLayer(_ position: Position) -> UITextField {
    
            // bottom layer
            let bottomLayer = CALayer()
            // set width
            let height = CGFloat(1.0)
            bottomLayer.borderWidth = height
            // set color
            bottomLayer.borderColor = UIColor.white.cgColor
            // set frame
            // y position changes according to the position
            let yOrigin = position == .up ? 0.0 : frame.size.height - height
            bottomLayer.frame = CGRect.init(x: 0, y: yOrigin, width: frame.size.width, height: height)
            layer.addSublayer(bottomLayer)
            layer.masksToBounds = true
    
            return self
        }
    
        // Add right/left padding view in textfield
        func addPadding(_ position: Position, withImage image: UIImage? = nil) {
            let paddingHeight = frame.size.height
            let paddingViewFrame = CGRect.init(x: 0.0, y: 0.0, width: paddingHeight * 0.6, height: paddingHeight)
            let paddingImageView = UIImageView.init(frame: paddingViewFrame)
            paddingImageView.contentMode = .scaleAspectFit
    
            if let paddingImage = image {
                paddingImageView.image = paddingImage
            }
    
            // Add Left/Right view mode
            switch position {
            case .left:
                leftView        = paddingImageView
                leftViewMode    = .always
            case .right:
                rightView       = paddingImageView
                rightViewMode    = .always
            default:
                break
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-27 09:55

    If you want to do without knowing frames beforehand, without subclassing and without Autolayout:

    Swift 5 / Swift 4.x / Swift 3.x

    extension UITextField {
      func setBottomBorder() {
        self.borderStyle = .none
        self.layer.backgroundColor = UIColor.white.cgColor
    
        self.layer.masksToBounds = false
        self.layer.shadowColor = UIColor.gray.cgColor
        self.layer.shadowOffset = CGSize(width: 0.0, height: 1.0)
        self.layer.shadowOpacity = 1.0
        self.layer.shadowRadius = 0.0
      }
    }
    

    Call as yourTextField.setBottomBorder() from anywhere without making sure of the frames to be right.

    The Result looks like this:

    Swift UI

    struct MyTextField: View {
      var myPlaceHolder: String
      @Binding var text: String
    
      var underColor: Color
      var height: CGFloat
    
      var body: some View {
        VStack {
            TextField(self.myPlaceHolder, text: $text)
            .padding()
            .font(.title)
    
            Rectangle().frame(height: self.height)
                .padding(.horizontal, 24).foregroundColor(self.underColor)
        }
      }
    }
    
    0 讨论(0)
  • 2020-11-27 09:58

    Swift 5 You can use a UIView extension so that you can add bottom border to any view:

    extension UIView {
    
        func addBottomLine(width: CGFloat, color: UIColor) {
            let lineView: UIView = {
                let view = UIView()
                view.translatesAutoresizingMaskIntoConstraints = false
                view.backgroundColor = color
                return view
            }()
            addSubview(lineView)
            NSLayoutConstraint.activate([
                lineView.heightAnchor.constraint(equalToConstant: width),
                lineView.leadingAnchor.constraint(equalTo: leadingAnchor),
                lineView.trailingAnchor.constraint(equalTo: trailingAnchor),
                lineView.bottomAnchor.constraint(equalTo: bottomAnchor)
            ])
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题