443

I would like to inset the text of a UITextField.

Is this possible?

Jack
  • 13,571
  • 6
  • 76
  • 98
RunLoop
  • 20,288
  • 21
  • 96
  • 151

31 Answers31

650

Overriding -textRectForBounds: will only change the inset of the placeholder text. To change the inset of the editable text, you need to also override -editingRectForBounds:

// placeholder position
- (CGRect)textRectForBounds:(CGRect)bounds {
     return CGRectInset(bounds, 10, 10);
}

// text position
- (CGRect)editingRectForBounds:(CGRect)bounds {
     return CGRectInset(bounds, 10, 10);
}
kukushi
  • 647
  • 9
  • 22
dvs
  • 12,324
  • 6
  • 38
  • 45
  • 10
    This solution worked for me, although I used a return value of CGRectInset(bounds, 9, 0); I also needed to set this value for textRectForBounds, editingRectForBounds, and placeholderRectForBounds. – RyJ Feb 12 '12 at 19:43
  • 2
    This solution doesn't play well with clearButton. Text inside TextField overlays button. – Piotr Dec 17 '12 at 10:41
  • I think, overriding above methods will cause scroll slow, if `UITextField` is residing within a `UIScrollView`. – Bharat Dodeja Mar 19 '13 at 06:36
  • 2
    For placing the ClearButton: `- (CGRect)clearButtonRectForBounds:(CGRect)bounds { return CGRectMake(x, y, w, h); }` Found here: http://stackoverflow.com/questions/5361369/ios-programming-frame-bounds-and-center – Miros May 08 '13 at 21:40
  • 25
    I suggest to call [super textRectForBounds:bounds] and [super editingRectForBounds:bounds] before calling CGRectInset(bounds,10, 10). This will fix the clear button overlay issue. – tonymontana Sep 27 '13 at 00:25
  • Note that you can avoid subclassing by using a category on UITextField. – user May 11 '14 at 01:36
  • I want to subclass `UITextField` in a way that I can pass padding in parameters in constructor or something. I don't want to create 100 subclass if I need text boxes with 100 diff padding. – shashwat Jun 12 '14 at 12:01
  • swift 3 solution below `override func editingRect(forBounds bounds: CGRect) -> CGRect { return bounds.insetBy(dx: 10, dy: 10); }` – GyroCocoa Mar 10 '17 at 13:28
  • 1
    The correct way to fix the issue with the clear button is to implement `clearButtonRect(forBounds:)` – Ash Jun 05 '18 at 07:23
302

I was able to do it through:

myTextField.layer.sublayerTransform = CATransform3DMakeTranslation(5, 0, 0);

Of course remember to import QuartzCore and also add the Framework to your project.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
chuthan20
  • 5,389
  • 1
  • 17
  • 21
  • 43
    +1 for creativity, but this is a little bit problematic, it moves also delete button inside textfield – Nikita Feb 20 '13 at 10:05
  • 2
    you could do myTextField.layer.sublayers which is an array of all the sublayers... and if its UIImageView <- I am assuming the X is an image.. or maybe UIButton... or you could loop thorough each one and see which one belongs to which subview... but myfield.layer.sublayerTransform all the sublayers and thus the X button moving as well.. – chuthan20 Feb 21 '13 at 03:19
  • This solution is not working for me. I can only set Left and Top margin but not right and bottom. `UITextField` overlaps content at right side of it. – Bharat Dodeja Mar 19 '13 at 06:33
  • Strangely, this technique does not work for a UILabel. Anyone know why? – northernman Jan 02 '14 at 04:43
  • absolutely a great solution. works with multiple uitextfields. – birdcage Apr 25 '14 at 14:55
  • 2
    This is the best solution without subclassing and doesn't require extra, unnecessary views to be placed on screen! +1! – Rambatino Nov 10 '14 at 20:34
  • @Nikita which "delete button" are you talking about. Sorry for digging in an old thread. But, I want to know its cons to avoid future problems. – jeet.chanchawat Sep 12 '16 at 05:37
  • 1
    @jeet.chanchawat The X button on the right side in this picture.. https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/UIKitUICatalog/Art/uitextfield_callouts_2x.png – chuthan20 Sep 13 '16 at 20:36
  • For those trying to inset the textField on a searchBarController (so it's not so close to the icon, for example) `searchBar.searchTextPositionAdjustment = UIOffsetMake(10, 0)` is what you want. – Scott Fister Nov 25 '17 at 21:01
182

If you need just a left margin, you can try this:

UItextField *textField = [[UITextField alloc] initWithFrame:...];
UIView *leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, textField.frame.size.height)];
leftView.backgroundColor = textField.backgroundColor;
textField.leftView = leftView;
textField.leftViewMode = UITextFieldViewModeAlways;

It works for me. I hope this may help.

Salman Zaidi
  • 9,342
  • 12
  • 44
  • 61
roberto.buratti
  • 2,487
  • 1
  • 16
  • 10
  • 18
    This is far easier than subclassing just to get an inset, and lets you add any arbitrary view on the left (you can also use rightView to put something on the right). Better than the accepted answer IMHO. – Kenny Grant Jun 05 '13 at 20:56
  • 5
    +1 easy, no-subclassing, and designed to work with textfield properties (rather than 'hacking'). – So Over It Jul 09 '13 at 04:15
  • Third line should be `leftView.backgroundColor = textField.backgroundColor;`... Other than that great solution... Thanks (: – Aviel Gross Nov 13 '13 at 15:27
  • Not quite as elegant/thorough as azdev's answer, but a great solution for a common and simple case! – Rembrandt Q. Einstein Jun 05 '14 at 18:27
  • In case, there is a background image for the text field, how can we use this to set the inset ? – Alex Andrews Jul 17 '14 at 06:08
  • I was skeptical that this was the best solution, but honestly, short of a sub-class, this is the easiest and best solution IMHO – Scott D Nov 10 '14 at 20:23
  • 1
    Subclassing will save you a ton of time over this answer, unless you have one single text box you need this for. – Crake Jun 13 '15 at 01:41
  • Add a method in a category could be even cleaner. Something like `[myTextField pad:10.0f edge:UIRectEdgeLeft];` Saves subclassing and flexible. – Adam Waite Jun 14 '15 at 18:15
  • Perfect, thanks ! Much better than subclassing, I added a dedicated function in an extension. One comment though: it is better to use `leftView.backgroundColor = UIColor.clear`, because in the case you use *roundedRect BorderStyle* it will eat the left border. – Romano Oct 04 '19 at 10:24
  • Great solution! Works for me, I posted a version in Swift below. Thanks @roberto.buratti ! – Natalia Terlecka Jul 15 '20 at 10:16
170

In a class derived from UITextField, override at least this two methods:

- (CGRect)textRectForBounds:(CGRect)bounds;
- (CGRect)editingRectForBounds:(CGRect)bounds;

It might be as simple as this if you have no additional content:

return CGRectInset(bounds , 10, 10);

UITextField provides several positioning methods you can override.

Jacob Ras
  • 5,974
  • 1
  • 28
  • 26
drawnonward
  • 53,459
  • 16
  • 107
  • 112
  • 2
    yes, if you don't override editingRectForBounds you get the text when editing at the top left of the textfield. - (CGRect)editingRectForBounds:(CGRect)bounds { return CGRectInset(bounds, 10, 10); } – Mark W Jul 24 '13 at 15:45
  • 1
    Just edited the answer to integrate editingRectForBounds method into – ıɾuǝʞ Aug 25 '14 at 10:36
  • 5
    This seems to me a horrible hack - you would also need to override `- (CGRect)borderRectForBounds:(CGRect)bounds; - (CGRect)placeholderRectForBounds:(CGRect)bounds; - (CGRect)clearButtonRectForBounds:(CGRect)bounds; - (CGRect)leftViewRectForBounds:(CGRect)bounds; - (CGRect)rightViewRectForBounds:(CGRect)bounds;` – Zorayr Dec 22 '14 at 22:52
107

How about an @IBInspectable, @IBDesignable swift class.

@IBDesignable
class TextField: UITextField {
    @IBInspectable var insetX: CGFloat = 6 {
       didSet {
         layoutIfNeeded()
       }
    }
    @IBInspectable var insetY: CGFloat = 6 {
       didSet {
         layoutIfNeeded()
       }
    }

    // placeholder position
    override func textRectForBounds(bounds: CGRect) -> CGRect {
        return CGRectInset(bounds , insetX , insetY)
    }

    // text position
    override func editingRectForBounds(bounds: CGRect) -> CGRect {
        return CGRectInset(bounds , insetX , insetY)
    }
}

You'll see this in your storyboard.

enter image description here

Update - Swift 3

@IBDesignable
class TextField: UITextField {
    @IBInspectable var insetX: CGFloat = 0
    @IBInspectable var insetY: CGFloat = 0

    // placeholder position
    override func textRect(forBounds bounds: CGRect) -> CGRect {
        return bounds.insetBy(dx: insetX, dy: insetY)
    }

    // text position
    override func editingRect(forBounds bounds: CGRect) -> CGRect {
        return bounds.insetBy(dx: insetX, dy: insetY)
    }
}
SPatel
  • 4,768
  • 4
  • 32
  • 51
mash
  • 4,204
  • 4
  • 32
  • 34
  • 2
    I found the effect on Y undesired, I don't want to shrink the rect for the text, rather nudge it down towards the baseline of the field. I adjusted the implementations to `let rect = CGRect(x: bounds.minX, y: bounds.minY + insetY, width: bounds.width, height: bounds.height) return CGRectInset(rect , insetX , 0)` – Chris Wagner Jul 28 '15 at 23:09
  • 1
    And add this too if yo are using placeholder ` override func placeholderRectForBounds(bounds: CGRect) -> CGRect { return CGRectInset(bounds , insetX , insetY) }` – RameshVel Apr 01 '16 at 11:42
  • Weirdly this (setting the insets in `textRect` / `editingRect`) affects the scrolling performance (on iOS 12 at least), when the text overflows the visible rect. With an inset of 15 it even stops scrolling. – User Oct 18 '19 at 06:56
  • @Ixx For you (and anyone else who comes across this): I discovered the issue on iOS 12+ has to do with an inset that leaves bounds too small for the font size... The text layout engine tries to fit the text into the bounds, fails, and still lays out (appearing correct but taking a long time to chew on it before giving up). The solution was to reduce the inset (or the font size) to make enough room for the text. – Itai Ferber Sep 16 '20 at 21:01
30

If you have a clear button, the accepted answer won't work for you. We should also guard against Apple changing things in the future by calling super.

So, to make sure the text doesn't overlap the clear button, let's get the 'default' value from super first, then adjust as necessary.

This code will add a 10px insets on the top, left and bottom of the text field:

@interface InsetTextField : UITextField

@end


@implementation InsetTextField

// Placeholder position
- (CGRect)textRectForBounds:(CGRect)bounds {
    CGRect rect = [super textRectForBounds:bounds];
    UIEdgeInsets insets = UIEdgeInsetsMake(10, 10, 10, 0);

    return UIEdgeInsetsInsetRect(rect, insets);
}

// Text position
- (CGRect)editingRectForBounds:(CGRect)bounds {
    CGRect rect = [super editingRectForBounds:bounds];
    UIEdgeInsets insets = UIEdgeInsetsMake(10, 10, 10, 0);

    return UIEdgeInsetsInsetRect(rect, insets);
}

// Clear button position
- (CGRect)clearButtonRectForBounds:(CGRect)bounds {
    CGRect rect = [super clearButtonRectForBounds:bounds];

    return CGRectOffset(rect, -5, 0);
}

@end

Note: UIEdgeInsetsMake takes parameters in the order: top, left, bottom, right.

Chris Nolet
  • 8,714
  • 7
  • 67
  • 92
  • Using `textRectForBounds:` and `editingRectForBounds:` methods *without* `clearButtonRectForBounds:` on iOS 7+ worked for me. – Stunner Jun 13 '15 at 01:49
  • `clearButtonRectForBounds:` just helps nudge the clear button to the left a bit. You may like to leave it out. My text field was on a dark background, and the clear button needed a bit of extra padding on the right. – Chris Nolet Jun 15 '15 at 01:14
  • Weirdly this affects the scrolling performance (on iOS 12 at least), when the text overflows the visible rect. With an inset of 15 it even stops scrolling. – User Oct 18 '19 at 06:55
  • @Ixx For you (and anyone else who comes across this): I discovered the issue on iOS 12+ has to do with an inset that leaves bounds too small for the font size... The text layout engine tries to fit the text into the bounds, fails, and still lays out (appearing correct but taking a long time to chew on it before giving up). The solution was to reduce the inset (or the font size) to make enough room for the text. – Itai Ferber Sep 16 '20 at 21:01
25

Thought I would supply a Swift Solution

import UIKit

class TextField: UITextField {
    let inset: CGFloat = 10

    // placeholder position
    override func textRectForBounds(bounds: CGRect) -> CGRect {
        return CGRectInset(bounds , inset , inset)
    }

    // text position
    override func editingRectForBounds(bounds: CGRect) -> CGRect {
        return CGRectInset(bounds , inset , inset)
    }

    override func placeholderRectForBounds(bounds: CGRect) -> CGRect {
        return CGRectInset(bounds, inset, inset) 
    }
}

Swift 3+

import UIKit

class TextField: UITextField {
    let inset: CGFloat = 10

    // placeholder position
    override func textRect(forBounds: CGRect) -> CGRect {
        return forBounds.insetBy(dx: self.inset , dy: self.inset)
    }

    // text position
    override func editingRect(forBounds: CGRect) -> CGRect {
        return forBounds.insetBy(dx: self.inset , dy: self.inset)
    }

    override func placeholderRect(forBounds: CGRect) -> CGRect {
        return forBounds.insetBy(dx: self.inset, dy: self.inset)
    }
}
Joshua Wolff
  • 2,687
  • 1
  • 25
  • 42
smitt04
  • 3,018
  • 2
  • 28
  • 30
  • 2
    Don't forget `override func placeholderRectForBounds(bounds: CGRect) -> CGRect { return CGRectInset(bounds, inset, inset) }` – Eugene Braginets Mar 31 '15 at 07:14
  • In Swift 3 you must use 'CGRect.insetBy()' method – Den Sep 06 '17 at 08:43
  • 1
    In iOS 11 at least, if you override `textRectForBounds`, the placeholder is also affected - so adding the placeholder override insets the placeholder another 10pt further. If that's what you're looking for, , but if not, it's good to be aware of. – DesignatedNerd Mar 12 '18 at 15:40
  • Weirdly this affects the scrolling performance (on iOS 12 at least), when the text overflows the visible rect. With an inset of 15 it even stops scrolling. – User Oct 18 '19 at 06:55
17

Swift

 class TextField: UITextField {

    let inset: CGFloat = 8

    // placeholder position
    override func textRect(forBounds bounds: CGRect) -> CGRect {
        return bounds.insetBy(dx: inset, dy: inset)
    }

    // text position
    override func editingRect(forBounds bounds: CGRect) -> CGRect {
        return bounds.insetBy(dx: inset, dy: inset)
    }
}
lcl
  • 1,045
  • 11
  • 13
  • Weirdly this affects the scrolling performance (on iOS 12 at least), when the text overflows the visible rect. With an inset of 15 it even stops scrolling. – User Oct 18 '19 at 06:55
14

Using textRectForBounds: is the correct approach. I have wrapped this up in my subclass so you can simply use textEdgeInsets. See SSTextField.

Sam Soffes
  • 14,831
  • 9
  • 76
  • 80
14

You can set text inset for UITextField by setting the leftView.

Like this:

UITextField *yourTextField = [[UITextField alloc] init];
UIView *leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 5, 5)];
leftView.backgroundColor = [UIColor clearColor];
yourTextField.leftViewMode = UITextFieldViewModeAlways;
yourTextField.leftView = leftView;
ƒernando Valle
  • 3,634
  • 6
  • 36
  • 58
Golden
  • 153
  • 1
  • 5
  • 1
    When you also need to use left view for an icon, this cant work – Reaper Jan 20 '15 at 11:08
  • @Reaper this method will also work for an image. add the amount of padding you want to the width of the imageview frame and set contentmode to center. ```imageView.contentMode = UIViewContentMode.Center imageView.frame = CGRectMake(0.0, 0.0, imageView.image!.size.width + 16.0, imageView.image!.size.height)``` – Andrew Aug 14 '15 at 16:03
  • this is way too hacky. there is already a textRectForBounds method for setting the inset – Gerald Jul 07 '16 at 14:48
14

Swift

    // adjust place holder text
    let paddingView = UIView(frame: CGRectMake(0, 0, 10, usernameOrEmailField.frame.height))
    usernameOrEmailField.leftView = paddingView
    usernameOrEmailField.leftViewMode = UITextFieldViewMode.Always
LondonGuy
  • 10,778
  • 11
  • 79
  • 151
13

A good approach to add padding to UITextField is to subclass UITextField and add an edgeInsets property. You then set the edgeInsets and the UITextField will be drawn accordingly. This will also function correctly with a custom leftView or rightView set.

OSTextField.h

#import <UIKit/UIKit.h>

@interface OSTextField : UITextField

@property (nonatomic, assign) UIEdgeInsets edgeInsets;

@end

OSTextField.m

#import "OSTextField.h"

@implementation OSTextField

- (id)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        self.edgeInsets = UIEdgeInsetsMake(0, 0, 0, 0);
    }
    return self;
}

-(id)initWithCoder:(NSCoder *)aDecoder{
    self = [super initWithCoder:aDecoder];
    if(self){
        self.edgeInsets = UIEdgeInsetsMake(0, 0, 0, 0);
    }
    return self;
}

- (CGRect)textRectForBounds:(CGRect)bounds {
    return [super textRectForBounds:UIEdgeInsetsInsetRect(bounds, self.edgeInsets)];
}

- (CGRect)editingRectForBounds:(CGRect)bounds {
    return [super editingRectForBounds:UIEdgeInsetsInsetRect(bounds, self.edgeInsets)];
}

@end
Brody Robertson
  • 8,506
  • 2
  • 47
  • 42
13

For people, who are searching for a easier solution.

Add the UITextField inside a UIView. To simulate an inset around text field I keep 10 px left and width is 20px less than the view. For a rounded corner border around the textfield, use the view's border

viewBG.layer.cornerRadius = 8.0;
viewBG.layer.borderColor = [UIColor darkGrayColor].CGColor;
viewBG.layer.borderWidth = 1.0;
karim
  • 15,408
  • 7
  • 58
  • 96
  • 2
    Honestly just putting a UIView behind the UITextField is the best and simplest solution. Make the UITextField transparent and done. I aligned it with a UITextView - turns out to be about 6 pixels inset. Much easier and also more flexible than creating a subclass... – n13 Mar 06 '12 at 05:46
  • A problem with this approach is the location of where the scroll bar will appear. – Doug Amos May 12 '16 at 10:51
  • @DougAmos What scrollbar? Are you referring to `UITextView` perhaps? – meaning-matters Apr 11 '19 at 21:39
9

Swift 5 version of Christopher's answer with extra usage sample

import UIKit

private class InsetTextField: UITextField {
    var insets: UIEdgeInsets

    init(insets: UIEdgeInsets) {
        self.insets = insets
        super.init(frame: .zero)
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("not intended for use from a NIB")
    }

    // placeholder position
    override func textRect(forBounds bounds: CGRect) -> CGRect {
         return super.textRect(forBounds: bounds.inset(by: insets))
    }
 
    // text position
    override func editingRect(forBounds bounds: CGRect) -> CGRect {
         return super.editingRect(forBounds: bounds.inset(by: insets))
    }
}

extension UITextField {

    class func textFieldWithInsets(insets: UIEdgeInsets) -> UITextField {
        return InsetTextField(insets: insets)
    }

}

Usage: -

class ViewController: UIViewController {

  private let passwordTextField: UITextField = {

        let textField = UITextField.textFieldWithInsets(insets: UIEdgeInsets(top: 10, left: 15, bottom: 10, right: 15))
     // ---
   
        return textField
    }()

}
Mussa Charles
  • 4,014
  • 2
  • 29
  • 24
  • Besides not requiring` get{}` for getter-only calculated properties, in Swift 5, you don't need the return statement for 1-liners, so you can tighten up the use case a bit with: `let passwordTextField = { UITextField.textFieldWithInsets(insets: UIEdgeInsets(top:0, left:5, bottom:0, right:15)) }()` – clearlight May 07 '22 at 22:15
  • Works excellently, BTW, and it's an interesting approach. Mixed feelings about the obfuscation by adding the extension though. Not sure why not just let the class be public and make it clear you're instantiating a subclass of UITextField where you use it. – clearlight May 07 '22 at 22:20
7

I did this in IB where I created a UIView Behind the textView that was a little bit longer. With the textField background color set to clear. enter image description here

jeremy wilson
  • 136
  • 1
  • 5
7

Swift 3 / Designable in Interface builder / Separate horizontal & vertical insects / usable out of the box

@IBDesignable
class TextFieldWithPadding: UITextField {

@IBInspectable var horizontalInset: CGFloat = 0
@IBInspectable var verticalInset: CGFloat = 0

override func textRect(forBounds bounds: CGRect) -> CGRect {
    return bounds.insetBy(dx: horizontalInset, dy: verticalInset)
}

override func editingRect(forBounds bounds: CGRect) -> CGRect {
    return bounds.insetBy(dx: horizontalInset , dy: verticalInset)
}

override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
    return bounds.insetBy(dx: horizontalInset, dy: verticalInset)
}
}

usage:

usage

&

enter image description here

zgorawski
  • 2,597
  • 4
  • 30
  • 43
7

Swift 4.2 version:

import UIKit

class InsetTextField: UITextField {

  let inset: CGFloat = 10

  override func textRect(forBounds bounds: CGRect) -> CGRect {
    return bounds.insetBy(dx: inset, dy: inset)
  }


  override func editingRect(forBounds bounds: CGRect) -> CGRect {
    return bounds.insetBy(dx: inset, dy: inset)
  }

  override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
    return bounds.insetBy(dx: inset, dy: inset)
  }

}
Bruno Paulino
  • 5,611
  • 1
  • 41
  • 40
  • Weirdly this affects the scrolling performance (on iOS 12 at least), when the text overflows the visible rect. With an inset of 15 it even stops scrolling. – User Oct 18 '19 at 06:55
6

It's the quickest way I've found without doing any subclasses:

UIView *spacerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10., 10.)];
[textField setLeftViewMode:UITextFieldViewModeAlways];
[textField setLeftView:spacerView];

In Swift:

let spacerView = UIView(frame:CGRect(x:0, y:0, width:10, height:10))
textField.leftViewMode = UITextFieldViewMode.Always
textField.leftView = spacerView
Max
  • 636
  • 3
  • 13
  • 28
5

It's absurd you have to subclass, since UITextField already implements the methods, as @Adam Waite points out. Here's a swift extension that exposes a factory method, also available in our categories repo:

private class InsetTextField: UITextField {
    var insets: UIEdgeInsets

    init(insets: UIEdgeInsets) {
        self.insets = insets
        super.init(frame: CGRectZero)
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("not intended for use from a NIB")
    }

    // placeholder position
    override func textRectForBounds(bounds: CGRect) -> CGRect {
        return super.textRectForBounds(UIEdgeInsetsInsetRect(bounds, insets))
    }

    // text position
    override func editingRectForBounds(bounds: CGRect) -> CGRect {
        return super.editingRectForBounds(UIEdgeInsetsInsetRect(bounds, insets))
    }
}

extension UITextField {

    class func textFieldWithInsets(insets: UIEdgeInsets) -> UITextField {
        return InsetTextField(insets: insets)
    }

}
Christopher Pickslay
  • 17,523
  • 6
  • 79
  • 92
5

Here is the same subclassed UITextField written in Swift 3. It is quite different from prior versions of Swift, as you'll see:

import UIKit

class MyTextField: UITextField
    {
    let inset: CGFloat = 10

    // placeholder position
    override func textRect(forBounds bounds: CGRect) -> CGRect
        {
        return bounds.insetBy(dx: inset, dy: inset)
        }

    // text position
    override func editingRect(forBounds bounds: CGRect) -> CGRect
        {
        return bounds.insetBy(dx: inset, dy: inset)
        }

    override func placeholderRect(forBounds bounds: CGRect) -> CGRect
        {
        return bounds.insetBy(dx: inset, dy: inset)
        }
    }

Incidentally, you can also do something like the following, if you want to control the inset of just one side. This particular example of adjusting only the left inset comes in handy if you place an image on top of the UITextField but you want it to appear to the user to be within the text field:

    override func editingRect(forBounds bounds: CGRect) -> CGRect
        {
        return CGRect.init(x: bounds.origin.x + inset, y: bounds.origin.y, width: bounds.width - inset, height: bounds.height)
        }
Gene Loparco
  • 2,157
  • 23
  • 23
3

You can adjust the positioning of the text within a text field by making it a subclass of UITextField and overriding the -textRectForBounds: method.

Noah Witherspoon
  • 57,021
  • 16
  • 130
  • 131
2

I subclased UITextField to handle this that supports left, top, right and bottom inset, and clear button positioning as well.

MRDInsetTextField.h

#import <UIKit/UIKit.h>

@interface MRDInsetTextField : UITextField

@property (nonatomic, assign) CGRect inset;

@end

MRDInsetTextField.m

#import "MRDInsetTextField.h"

@implementation MRDInsetTextField

- (id)init
{
    self = [super init];
    if (self) {
        _inset = CGRectZero;
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        _inset = CGRectZero;
    }
    return self;
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        _inset = CGRectZero;
    }
    return self;
}

- (void)setInset:(CGRect)inset {
    _inset = inset;

    [self setNeedsLayout];
}

- (CGRect)getRectForBounds:(CGRect)bounds withInset:(CGRect)inset {

    CGRect newRect = CGRectMake(
                         bounds.origin.x + inset.origin.x,
                         bounds.origin.y + inset.origin.y,
                         bounds.origin.x + bounds.size.width - inset.origin.x - inset.size.width,
                         bounds.origin.y + bounds.size.height - inset.origin.y - inset.size.height
                         );

    return newRect;
}

- (CGRect)textRectForBounds:(CGRect)bounds {
    return [self getRectForBounds:[super textRectForBounds:bounds] withInset:_inset];
}

- (CGRect)placeholderRectForBounds:(CGRect)bounds {
    return [self getRectForBounds:bounds withInset:_inset];
}

- (CGRect)editingRectForBounds:(CGRect)bounds {
    return [self getRectForBounds:[super editingRectForBounds:bounds] withInset:_inset];
}

- (CGRect)clearButtonRectForBounds:(CGRect)bounds {
    return CGRectOffset([super clearButtonRectForBounds:bounds], -_inset.size.width, _inset.origin.y/2 - _inset.size.height/2);
}

@end

Example of usage where *_someTextField* comes from nib/storyboard view with MRDInsetTextField custom class

[(MRDInsetTextField*)_someTextField setInset:CGRectMake(5, 0, 5, 0)]; // left, top, right, bottom inset
Firula
  • 1,251
  • 10
  • 29
  • Thank you. One suggestion to your code though - why did you use CGRect for inset and not UIEdgeInsets? – sha Apr 08 '14 at 16:41
2

This is not as short as the other examples, but takes an entirely different approach to solving this problem. Note, the caret will still begin flush to the left edge but the text will be properly indented when typed/displayed. This works without subclassing if your looking for just a left margin and you are already using UITextFieldDelegate for your text fields. You need to set both the default text attributes and the typing attributes. You set the default text attributes when you create the text field. The typing attributes you need to set in the delegate. If you are also using a placeholder you will want to set that to the same margin as well. Putting it altogether you get something like this.

First create a category on the UITextField class.

//  UITextField+TextAttributes.h

#import <UIKit/UIKit.h>

@interface UITextField (TextAttributes)

- (void)setIndent:(CGFloat)indent;

@end


//  UITextField+TextAttributes.m
#import "UITextField+TextAttributes.h"

@implementation UITextField (TextAttributes)

- (void)setTextAttributes:(NSDictionary*)textAttributes indent:(CGFloat)indent
{
    if (!textAttributes) return;

    NSMutableParagraphStyle *paragraphStyle = [textAttributes objectForKey:NSParagraphStyleAttributeName];
    paragraphStyle.firstLineHeadIndent = indent;
    paragraphStyle.headIndent = indent;
}

- (void)setIndent:(CGFloat)indent
{
   [self setTextAttributes:self.defaultTextAttributes indent:indent];
   [self setTextAttributes:self.typingAttributes indent:indent];
}

@end

Then, if you are using placed holders make sure to use an attributed placeholder setting the same indent. Create a default attributed dictionary with the proper attributes, something like this:

NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.firstLineHeadIndent = 7;
paragraphStyle.headIndent = 7;
NSDictionary *placeholderAttributes = [NSDictionary dictionaryWithObjectsAndKeys: paragraphStyle, NSParagraphStyleAttributeName, nil];

Then, import the above category and whenever you create a text field set the default indent, the delegate and use the default placeholder attributes defined above. For example:

UITextField *textField = [[UITextField alloc] init];
textField.indent = 7;
textField.delegate = self;
textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"Placeholder Text" attributes:placeholderAttributes];

Lastly, in the delegate, implement the textFieldDidBeginEditing method, something like this:

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    textField.indent = 7;
}
KlimczakM
  • 12,576
  • 11
  • 64
  • 83
  • The assumption that `defaultTextAttributes` contains `NSMutableParagraphStyle` is quite dangerous.. I'd rather mutableCopy all of this. – pronebird Mar 07 '15 at 13:01
2

A solution that actually works and covers all cases:

  • Should use offsetBy not insetBy.
  • Should also call the super function to get the original Rect.
  • Bounds is faulty. you need to offset the original X, Y. Bounds have X, Y as zeros.
  • Original x, y can be non-zero for instance when setting the leftView of the UITextField.

Sample:

override func textRect(forBounds bounds: CGRect) -> CGRect {
    return super.textRect(forBounds: bounds).offsetBy(dx: 0.0, dy: 4)
}


override func editingRect(forBounds bounds: CGRect) -> CGRect {
    return super.editingRect(forBounds: bounds).offsetBy(dx: 0.0, dy: 4)
}
hasan
  • 23,815
  • 10
  • 63
  • 101
2

I found the option posted by roberto.buratti the fastest solution, here it is in Swift:

let leftView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: textField.frame.size.height))
leftView.backgroundColor = textField.backgroundColor
textField.leftView = leftView
textField.leftViewMode = UITextField.ViewMode.always
1

I normally try avoid subclassing but this works if you have already:

// add a property 
@property (nonatomic) UIEdgeInsets edgeInsets;

// and override:

- (CGRect)textRectForBounds:(CGRect)bounds
{
    return [super textRectForBounds:UIEdgeInsetsInsetRect(bounds, self.edgeInsets)];
}

- (CGRect)editingRectForBounds:(CGRect)bounds
{
    return [super editingRectForBounds:UIEdgeInsetsInsetRect(bounds, self.edgeInsets)];
}
Adam Waite
  • 19,175
  • 22
  • 126
  • 148
1

To throw in another solution that has no need for subclassing:

UITextField *txtField = [UITextField new];
txtField.borderStyle = UITextBorderStyleRoundedRect;

// grab BG layer
CALayer *bgLayer = txtField.layer.sublayers.lastObject;
bgLayer.opacity = 0.f;

// add new bg view
UIView *bgView = [UIView new];
bgView.backgroundColor = [UIColor whiteColor];
bgView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
bgView.userInteractionEnabled = NO;

[txtField addSubview: bgView];
[txtField sendSubviewToBack: bgView];

Original UITextField Fixed UITextField

Tested with iOS 7 and iOS 8. Both working. Still there might be the chance of Apple modifying the UITextField's layer hierarchy screwing up things badly.

Quxflux
  • 3,133
  • 2
  • 26
  • 46
1

Here's a comprehensive Swift answer that includes a leftView (custom icon) and a custom clear button, both set in Interface Builder with customizable insets.

import UIKit

@IBDesignable
class InsetTextField: UITextField {
@IBInspectable var leftInset:CGFloat = 0
@IBInspectable var rightInset:CGFloat = 0
@IBInspectable var icon:UIImage? { didSet {
    let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 16, height: 16))
    imageView.image = icon
    self.leftView = imageView
    self.leftViewMode = .Always
} }

@IBInspectable var clearButton:UIImage? { didSet {
    let button = UIButton(type: .Custom)
    button.setImage(clearButton, forState: .Normal)
    button.addTarget(self, action: "clear", forControlEvents: UIControlEvents.TouchUpInside)
    button.frame = CGRect(x: 0, y: 0, width: 18, height: 18)
    self.rightView = button
    self.rightViewMode = .WhileEditing
} }

func clear() {
    self.text = ""
}

override func leftViewRectForBounds(bounds: CGRect) -> CGRect {
    var height:CGFloat = 0
    var width:CGFloat = 0
    if let leftView = self.leftView {
        height = leftView.bounds.height
        width = leftView.bounds.width
    }

    return CGRect(x: leftInset, y: bounds.height/2 - height/2, width: width, height: height)
}

override func rightViewRectForBounds(bounds: CGRect) -> CGRect {
    var height:CGFloat = 0
    var width:CGFloat = 0
    if let rightView = self.rightView {
        height = rightView.bounds.height
        width = rightView.bounds.width
    }

    return CGRect(x: bounds.width - width - rightInset, y: bounds.height/2 - height/2, width: width, height: height)
}

}
rmooney
  • 6,123
  • 3
  • 29
  • 29
1

You might need this solution which also supports leftView and rightView.

class InsettedTextField: UITextField {

    private let textInset: UIEdgeInsets

    var rightViewInset: CGRect {
        rightView.flatMap { $0.frame } ?? .zero
    }

    var leftViewInset: CGRect {
        leftView.flatMap { $0.frame } ?? .zero
    }

    /// Init the text field with insets.
    init(textInset: UIEdgeInsets) {
        self.textInset = textInset
        super.init(frame: .zero)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func textRect(forBounds bounds: CGRect) -> CGRect {
        bounds
            .inset(by: textInset)
            .inset(by: UIEdgeInsets(top: 0, left: leftViewInset.width, bottom: 0, right: rightViewInset.width))
    }

    override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
        bounds
            .inset(by: textInset)
            .inset(by: UIEdgeInsets(top: 0, left: leftViewInset.width, bottom: 0, right: rightViewInset.width))
    }

    override func editingRect(forBounds bounds: CGRect) -> CGRect {
        bounds
            .inset(by: textInset)
            .inset(by: UIEdgeInsets(top: 0, left: leftViewInset.width, bottom: 0, right: rightViewInset.width))
    }
}

0

If you want to change TOP and LEFT indent only then

// placeholder position

- (CGRect)textRectForBounds:(CGRect)bounds {

CGRect frame = bounds;
frame.origin.y = 3;
 frame.origin.x = 5;
bounds = frame;
return CGRectInset( bounds , 0 , 0 );
}

// text position

- (CGRect)editingRectForBounds:(CGRect)bounds {

CGRect frame = bounds;
frame.origin.y = 3;
 frame.origin.x = 5;
bounds = frame;
return CGRectInset( bounds , 0 , 0 );
}
Mann
  • 5,477
  • 6
  • 45
  • 57
-1

Quick solution without subclass & also inspectable

extension UITextField {
    @IBInspectable var textInsets: CGPoint {
            get {
                return CGPoint.zero
            }
            set {
                layer.sublayerTransform = CATransform3DMakeTranslation(newValue.x, newValue.y, 0);
            }
        }
}
Bhavesh Tiwari
  • 155
  • 1
  • 3