26

I currently have a regular border. I would like to only have a top and bottom border.

How do I accomplish this?

Using the UITextField's layer property, I have the following code:

    self.layer.borderColor = [[UIColor colorWithRed:160/255.0f green:160/255.0f blue:160/255.0f alpha:1.0f] CGColor];
    self.layer.borderWidth = 4.0f;

I have kind of got it to work by making my UITextField extra long, so that the user does not see the left and right borders, but I was just wondering if there was a better, less hackish way of doing this?

I have checked the docs, and changing a UITextField's borderStyle does not have this option.

From,

An iOS First Timer

Girish
  • 4,692
  • 4
  • 35
  • 55
GangstaGraham
  • 8,865
  • 12
  • 42
  • 60

11 Answers11

77

One approach I have found works good is using layers. Here's a snippet:

CALayer *bottomBorder = [CALayer layer];
bottomBorder.frame = CGRectMake(0.0f, self.frame.size.height - 1, self.frame.size.width, 1.0f);
bottomBorder.backgroundColor = [UIColor blackColor].CGColor;
[myTextField.layer addSublayer:bottomBorder];

Hope this helps someone.

user3075378
  • 786
  • 6
  • 2
42

@user3075378's great & simple example in Swift

var bottomBorder = CALayer()
bottomBorder.frame = CGRectMake(0.0, textField.frame.size.height - 1, textField.frame.size.width, 1.0);
bottomBorder.backgroundColor = UIColor.blackColor().CGColor
textField.layer.addSublayer(bottomBorder)
Sebyddd
  • 4,305
  • 2
  • 39
  • 43
  • 11
    @kareem why? The question is tagged objective-c and it's also used in the question. – bobbaluba Jun 25 '15 at 12:42
  • 21
    Because there may be more people with the same problem that are using Swift instead of Objective-C. I am for example happy that he posted it in Swift – Emptyless Jun 27 '16 at 12:37
23

@Sebyddd why stop there? (;

EDIT: There is an issue with lines being drawn before auto layout sets the right frame for the view, I edited my answer with a fix: it basically involves calling drawLines() in layoutSubviews():

class FramedTextField: UITextField {

    @IBInspectable var linesWidth: CGFloat = 1.0 { didSet{ drawLines() } }

    @IBInspectable var linesColor: UIColor = UIColor.blackColor() { didSet{ drawLines() } }

    @IBInspectable var leftLine: Bool = false { didSet{ drawLines() } }
    @IBInspectable var rightLine: Bool = false { didSet{ drawLines() } }
    @IBInspectable var bottomLine: Bool = false { didSet{ drawLines() } }
    @IBInspectable var topLine: Bool = false { didSet{ drawLines() } }



    func drawLines() {

        if bottomLine {
            add(CGRectMake(0.0, frame.size.height - linesWidth, frame.size.width, linesWidth))
        }

        if topLine {
            add(CGRectMake(0.0, 0.0, frame.size.width, linesWidth))
        }

        if rightLine {
            add(CGRectMake(frame.size.width - linesWidth, 0.0, linesWidth, frame.size.height))
        }

        if leftLine {
            add(CGRectMake(0.0, 0.0, linesWidth, frame.size.height))
        }

    }

    typealias Line = CGRect
    private func add(line: Line) {
        let border = CALayer()
        border.frame = line
        border.backgroundColor = linesColor.CGColor
        layer.addSublayer(border)
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        drawLines()
    }

}
Aviel Gross
  • 9,770
  • 3
  • 52
  • 62
  • I'm new to objective c and I'm having difficulties creating a class, can you try to explain how I can do it? Thanks in advance – Ricardo Alves Dec 21 '15 at 10:53
  • @RicardoAlves in Xcode, on the left in `Project Navigator` (where all the files are) right click > `New File` > under iOS choose `Source` > `Cocoa Touch Class`> hit `Next` to enter the class name, parent etc. – Aviel Gross Dec 21 '15 at 11:49
  • 1
    That should be the accepted answer. Without auto layout support, it's a prototype. – Pedro Góes Mar 31 '16 at 21:19
9

you can create one image that with top and bottom border and set it to the background of your UITextField :

yourTextField.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"yourBorderedImageName"]];
KDeogharkar
  • 10,939
  • 7
  • 51
  • 95
  • Hmm... this seems pretty hackish too, I am surprised there is no way via border style or something simpler to do this, I will see what other people have to say, maybe they might know a better way. – GangstaGraham Mar 22 '13 at 04:04
  • yes I had the same issue and finally I come out with this option. – KDeogharkar Mar 22 '13 at 04:05
  • Does this work if your UITextField's height or width changes? – netwire Aug 16 '14 at 11:33
  • @Dean You can slice the image in the asset catalog to create repeating portions so the image won't stretch. There's a button in the lower right corner of the image view that will show you the slices or you can add them in the attributes inspector. – melissa.mcmurtrie Apr 08 '15 at 17:42
5

You can also add views to the top and bottom to use as borders.

// Top border
UIView *topBorder = [[UIView alloc]
    initWithFrame:CGRectMake(0,
                             0,
                             textfield.frame.size.width,
                             4.0f)];
topBorder.backgroundColor = [UIColor colorWithRed:160/255.0f
                                            green:160/255.0f
                                             blue:160/255.0f
                                            alpha:1.0f];
[textfield addSubview:topBorder];

// Bottom border
UIView *bottomBorder = [[UIView alloc]
    initWithFrame:CGRectMake(0,
                             textfield.frame.origin.y +
                                 textfield.frame.size.height - 4.0f,
                             textfield.frame.size.width,
                             4.0f)];
bottomBorder.backgroundColor = [UIColor colorWithRed:160/255.0f
                                               green:160/255.0f
                                                blue:160/255.0f
                                               alpha:1.0f];
[textfield addSubview:bottomBorder];
Zorayr
  • 23,770
  • 8
  • 136
  • 129
Sarah Nguyen
  • 151
  • 2
  • 10
  • 2
    Excellent, thanks for the idea. Just a note: since you're adding the border as a subview of the UITextField itself you probably don't want to position the border based on the textfield's origin since that refers to the coordinate space of its parent. You'd want the top border to start at (0,0) and the bottom to start at: (textfield.frame.size.height - 4.0f) – TylerJames Jun 25 '14 at 21:03
5

You can use layers to add lines / shapes to any UIView subclass. This code draws two lines at the top and bottom of a text field. You can add it to a subclass of a control, or call this directly in a parent view / view controller.

CGRect layerFrame = CGRectMake(0, 0, _usernameField.frame.size.width, _usernameField.frame.size.height);
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 0, 0);
CGPathAddLineToPoint(path, NULL, layerFrame.size.width, 0); // top line
CGPathMoveToPoint(path, NULL, 0, layerFrame.size.height);
CGPathAddLineToPoint(path, NULL, layerFrame.size.width, layerFrame.size.height); // bottom line
CAShapeLayer * line = [CAShapeLayer layer];
line.path = path;
line.lineWidth = 2;
line.frame = layerFrame;
line.strokeColor = [UIColor blackColor].CGColor;
[_usernameField.layer addSublayer:line];
Eli Burke
  • 2,729
  • 27
  • 25
  • What's the performance impact for drawing the line as opposed to adding a subview? – Zorayr Sep 02 '14 at 00:59
  • @Zorayr A layer seems like a pretty simple object compared to a UIView so, at a educated guess, I reckon layers would be much more performant. – Gerard Sep 23 '14 at 18:25
  • 1
    Just a useful remark. If this code is added to parent view controller, the lines won't change their lengths when device changes orientation. To handle this (and avoid subclassing), I rendered CAShapeLayer to image, and used that image as a background for textfield. Code to render image: UIGraphicsBeginImageContextWithOptions(textField.bounds.size, textField.opaque, 0.0); [line renderInContext:UIGraphicsGetCurrentContext()]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); [textField setBackground:image]; – Yarlik Jan 14 '15 at 15:25
2

Swift 3: Clean with AutoLayout (I'm using here PureLayout but you can also do it with common NSLayoutConstraint:

func makeUnderline(for textField: UITextField) {
    let borderLine = UIView()
    borderLine.backgroundColor = UIColor.black
    borderLine.autoSetDimension(.height, toSize: 1)
    textField.addSubview(borderLine)
    borderLine.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets.zero, excludingEdge: .top)
}
Ravi Vyas
  • 12,212
  • 6
  • 31
  • 47
kurtanamo
  • 1,808
  • 22
  • 27
1

I will suggest you put one view on the left side of the textfield and one view on the right side of the textfield to cover the left/right border.

UIView *v1 = [[UIView all] initWithFrame:CGRectMake(textfield.frame.origin.x - 5, textfield.frame.origin.y, 10, textifield.frame.size.height)];
UIView *v2 = [[UIView all] initWithFrame:CGRectMake(textfield.frame.origin.x + textfield.frame.size.with - 5, textfield.frame.origin.y, 10, textifield.frame.size.height)];
Doon
  • 97
  • 3
1

Hope this helps, put this inside a textfield override class

UIView *view = [[UIView alloc] init];
        view.translatesAutoresizingMaskIntoConstraints = NO;
        view.layer.borderWidth = 1;
        view.backgroundColor = [UIColor blackColor];
        [self addSubview:view];

        [self addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0]];
        [self addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0]];
        [self addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeHeight multiplier:1.0 constant:1.0]];
        [self addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:1.0]];
Travis Delly
  • 1,194
  • 2
  • 11
  • 20
0

Thanks to user3075378. my answer is based on his. adding right and left small lines.

- (void)styleSearchTextField {
    CALayer *bottomBorder = [CALayer layer], *leftBorder = [CALayer layer], *rightBorder = [CALayer layer];

    CGFloat thickness = 1.0f;
    CGFloat side_height = 6.0f;

    bottomBorder.frame = CGRectMake(0.0f, searchTextField.frame.size.height - 1, searchTextField.frame.size.width, thickness);

    leftBorder.frame = CGRectMake(0.0f, searchTextField.frame.size.height - side_height, thickness, searchTextField.frame.size.height - 1);

    rightBorder.frame = CGRectMake(searchTextField.frame.size.width - 1, searchTextField.frame.size.height - side_height, thickness, searchTextField.frame.size.height - 1);

    bottomBorder.backgroundColor = [self.stylingDetails themeGrayColor].CGColor;
    leftBorder.backgroundColor = [self.stylingDetails themeGrayColor].CGColor;
    rightBorder.backgroundColor = [self.stylingDetails themeGrayColor].CGColor;

    [searchTextField.layer addSublayer:bottomBorder];
    [searchTextField.layer addSublayer:leftBorder];
    [searchTextField.layer addSublayer:rightBorder];
}
hasan
  • 23,815
  • 10
  • 63
  • 101
0

Updated for Swift 5

var bottomBorder = CALayer()
bottomBorder.frame = CGRect(x: xVal, y: yVal, width: w, height: h)
bottomBorder.backgroundColor = UIColor.black.cgColor
searchBar.layer.addSublayer(bottomBorder)
CSmizzle
  • 57
  • 10