Add lefthand margin to UITextField - ios

I want to put the left margin of a UITextField's text at 10 px. What is the best way to do that?

You can do it by extending UITextField class and overriding two methods:
- (CGRect)textRectForBounds:(CGRect)bounds;
- (CGRect)editingRectForBounds:(CGRect)bounds;
Here is the code:
The interface in MYTextField.h
#interface MYTextField : UITextField
#end
Its implementation in MYTextField.m
#implementation MYTextField
static CGFloat leftMargin = 28;
- (CGRect)textRectForBounds:(CGRect)bounds
{
bounds.origin.x += leftMargin;
return bounds;
}
- (CGRect)editingRectForBounds:(CGRect)bounds
{
bounds.origin.x += leftMargin;
return bounds;
}
#end

As I have explained in a previous comment, the best solution in this case is to extend the UITextField class instead of using a category, so you can use it explicitly on the desired text fields.
#import <UIKit/UIKit.h>
#interface MYTextField : UITextField
#end
#implementation MYTextField
- (CGRect)textRectForBounds:(CGRect)bounds {
int margin = 10;
CGRect inset = CGRectMake(bounds.origin.x + margin, bounds.origin.y, bounds.size.width - margin, bounds.size.height);
return inset;
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
int margin = 10;
CGRect inset = CGRectMake(bounds.origin.x + margin, bounds.origin.y, bounds.size.width - margin, bounds.size.height);
return inset;
}
#end
A category is intended to add new functions to an existing class, not to override an existing method.

UITextField * textField = [[UITextField alloc]init];
[textField setDelegate:self];
[textField setFrame:CGRectMake(170,112,140,25)];
[textField setBorderStyle:UITextBorderStyleNone];
[textField setBackgroundColor:[UIColor clearColor]];
[self.View addSubview:noofChildTField];
UIView *paddingView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 5, 20)] autorelease];
textField.leftView = paddingView;
textField.leftViewMode = UITextFieldViewModeAlways;
Try this code

For Swift 3 :
Make an outlet of the UITextField, say usernameTextField. Then write the following code in viewDidLoad()
let paddingView : UIView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: 20))
usernameTextField.leftView = paddingView
usernameTextField.leftViewMode = .always
Change the width: 5 to a greater value if more space is required.

I was hoping to see a setting in IB in the property inspector to adjust this value. Turns out I had set alignment and border style inadvertently to values that messed with the padding on the left.
You wouldn't think it would be a big deal to choose left justify and no border but it makes the words basically overlap or come right to the edge of the box and it doesn't look right.
Bad:
Good:
Fixed it right up for me. Hope this helps someone else as well.

Subclass the UITextField and add an extension:
extension UITextField {
func paddingLeft(inset: CGFloat){
self.leftView = UIView(frame: CGRect(x: 0, y: 0, width: inset, height: self.frame.height))
self.leftViewMode = UITextField.ViewMode.always
}
}
usage
class MyUITextFieldClass: UITextField {
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.paddingLeft(inset: 10)
}
}

i have reached almost by overriding - (CGRect)textRectForBounds:(CGRect)bounds. now issue is when TextField goes in to edit mode their left margin reset to Zero .......
#implementation UITextField(UITextFieldCatagory)
- (CGRect)textRectForBounds:(CGRect)bounds {
CGRect theRect=CGRectMake(bounds.origin.x+10, bounds.origin.y, bounds.size.width-10, bounds.size.height);
return theRect;
}

For Swift 4:
I prefer to use IBDesignable class and IBInspectable properties to allow me to set the padding via Xcode storyboards and keep it reusable. I've also updated the code to work in Swift 4.
import Foundation
import UIKit
#IBDesignable
class PaddableTextField: UITextField {
var padding = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0)
#IBInspectable var left: CGFloat = 0 {
didSet {
adjustPadding()
}
}
#IBInspectable var right: CGFloat = 0 {
didSet {
adjustPadding()
}
}
#IBInspectable var top: CGFloat = 0 {
didSet {
adjustPadding()
}
}
#IBInspectable var bottom: CGFloat = 0 {
didSet {
adjustPadding()
}
}
func adjustPadding() {
padding = UIEdgeInsets(top: top, left: left, bottom: bottom, right: right)
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
}
override func textRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: UIEdgeInsets(top: top, left: left, bottom: bottom, right: right))
}
override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: UIEdgeInsets(top: top, left: left, bottom: bottom, right: right))
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: UIEdgeInsets(top: top, left: left, bottom: bottom, right: right))
}
}

You can try this
TextField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
TextField.textAlignment = UITextAlignmentCenter;

Related

Align image at the right corner of UIButton irrespective of text length

Need to align the image on the right corner of UIButton, irrespective of text length. I have managed to set an image on the right of the text but it is placing an image, where the text ends. Below is the code and output that I am getting.
btnSelect.titleEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0)
btnSelect.imageEdgeInsets = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 0)
Use override method of UIButton and manage your title and image frame.
Here is UIButton subclass.
class ButtonIconRight: UIButton {
override func imageRect(forContentRect contentRect:CGRect) -> CGRect {
var imageFrame = super.imageRect(forContentRect: contentRect)
imageFrame.origin.x = self.bounds.width - imageFrame.width
return imageFrame
}
override func titleRect(forContentRect contentRect:CGRect) -> CGRect {
var titleFrame = super.titleRect(forContentRect: contentRect)
if (self.currentImage != nil) {
titleFrame.origin.x = 0
}
return titleFrame
}
}

cannot set UILabel padding in tableViewcell

I have set my UILabel padding using StackOverflow's popular thread for resolving auto-layout issue.
This thread is basically a UILabel extension.
Part of the answer is:
class NRLabel : UILabel {
var textInsets = UIEdgeInsets.zero {
didSet { invalidateIntrinsicContentSize() }
}
override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
let insetRect = UIEdgeInsetsInsetRect(bounds, textInsets)
let textRect = super.textRect(forBounds: insetRect, limitedToNumberOfLines: numberOfLines)
let invertedInsets = UIEdgeInsets(top: -textInsets.top,
left: -textInsets.left,
bottom: -textInsets.bottom,
right: -textInsets.right)
return UIEdgeInsetsInsetRect(textRect, invertedInsets)
}
override func drawText(in rect: CGRect) {
super.drawText(in: UIEdgeInsetsInsetRect(rect, textInsets))
}
}
Everything is working fine, padding has been added but i need to reload the tableViewcell to see the effect.
I have overridden my customCell's viewWillLayoutSubviews() function like this for padding
self.chatLabel.textInsets = UIEdgeInsets.init(top: 10, left: 10, bottom: 10, right: 10)
And the effect is like this.
You see, first label is getting it's padding after reloading the cell.
Pls suggest how to achieve UILabel padding by using the extension mentioned above so that this issue is resolved.
Add
this two function into your extention and remove all other:
override func drawText(in rect: CGRect) {
let insets: UIEdgeInsets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
}
override var intrinsicContentSize : CGSize {
var intrinsicSuperViewContentSize = super.intrinsicContentSize
intrinsicSuperViewContentSize.height += topInset + bottomInset
intrinsicSuperViewContentSize.width += leftInset + rightInset
return intrinsicSuperViewContentSize
}
Using your NRLabel class, this works fine for me. The theLabel is added in Storyboard TableViewCell Prototype, with constraints set to allow auto-sizing rows.
class PaddedLabelCell : UITableViewCell {
#IBOutlet weak var theLabel: NRLabel!
override func awakeFromNib() {
super.awakeFromNib()
self.setupLabel()
}
func setupLabel() -> Void {
theLabel.layer.cornerRadius = 8.0
theLabel.layer.masksToBounds = true
theLabel.textInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
// any other setup stuff can go here....
}
}
You have nothing in NRLabel which tells it to redraw when the textInsets property is changed. You need to do something like this:
var textInsets = UIEdgeInsets.zero {
didSet {
invalidateIntrinsicContentSize()
setNeedsDisplay()
}
}
So now when the textInsets property is changed the NRLabel will be re-drawn with the new text insets.
After some long tiring hours i just found the solution which i never tried and i don't know why. :(
In cellForRowAtIndexPath i just wrote the line which was in customCell's file.
Just call this line before returning cell.
cell.chatLabel.textInsets = UIEdgeInsets.init(top: 10, left: 10, bottom: 10, right: 10)
The reason behind it may be, Cell is drawn with all the functionality but it didn't get much to refresh it's UI.
So calling the line just before showing the cell resolve the problem.

How do you add inset to UILabel (iOS Swift)?

I am an Android developer learning iOS. How do you add padding (inset) to UILabel in iOS Swift? I have not been successful at finding an easy tutorial on this. Thank you.
Also, how can I add margins to UI elements in Swift? When I try the below, nothing gets updated (upon hitting "add constraint").
This is the code you need to add in Swift 3:
class InsetLabel: UILabel {
let topInset = CGFloat(0)
let bottomInset = CGFloat(0)
let leftInset = CGFloat(20)
let rightInset = CGFloat(20)
override func drawText(in rect: CGRect) {
let insets: UIEdgeInsets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
}
override public var intrinsicContentSize: CGSize {
var intrinsicSuperViewContentSize = super.intrinsicContentSize
intrinsicSuperViewContentSize.height += topInset + bottomInset
intrinsicSuperViewContentSize.width += leftInset + rightInset
return intrinsicSuperViewContentSize
}
}
Probably the best idea is to subclass UILabel and override drawTextInRect: like this:
class InsetLabel: UILabel {
override func drawTextInRect(rect: CGRect) {
super.drawTextInRect(UIEdgeInsetsInsetRect(rect, UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)))
}
}
SWIFT 4+:
class InsetLabel: UILabel {
override func drawText(in rect: CGRect) {
super.drawText(in: rect.inset(by: UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)))
}
}
need add this:
class InsetLabel: UILabel {
let topInset = CGFloat(12.0), bottomInset = CGFloat(12.0), leftInset = CGFloat(12.0), rightInset = CGFloat(12.0)
override func drawTextInRect(rect: CGRect) {
let insets: UIEdgeInsets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
super.drawTextInRect(UIEdgeInsetsInsetRect(rect, insets))
}
override func intrinsicContentSize() -> CGSize {
var intrinsicSuperViewContentSize = super.intrinsicContentSize()
intrinsicSuperViewContentSize.height += topInset + bottomInset
intrinsicSuperViewContentSize.width += leftInset + rightInset
return intrinsicSuperViewContentSize
}
}
There is a solution simply using the storyboard. In the storyboard, create a UIView object whose purpose will be to surround your label in order to give it padding. Then, also from the storyboard, put your UILabel inside of that view. Now select the UILabel object and bring up the Add New Constraints popup by clicking the "Pin" icon. If you want a padding of 5 all the way around the UILabel, click each of the 4 red I-beams in the Add New Constraints popup, and THEN type 5 in each of the 4 boxes adjacent to the I-beams. If you type 5 first and then click an I-Beam, XCode reverts to whatever value was in the box before you typed 5. Finally click the Add Constraints button.
In the Add Constraints popup, XCode ignores top, left, right, or bottom constraint values for which you have not clicked the corresponding red I-beam. This probably answers the second part of your question.
Remember also to setup the constraints of the UIView you added. If you don't define where it goes in the storyboard (e.g., pinned to the top and left of the main view of the view controller), it will not be assigned a default location, and it may not show up at all when you run your app.
I have tried with it on Swift 4.2, hopefully, it works for you!
#IBDesignable class PaddingLabel: UILabel {
#IBInspectable var topInset: CGFloat = 5.0
#IBInspectable var bottomInset: CGFloat = 5.0
#IBInspectable var leftInset: CGFloat = 7.0
#IBInspectable var rightInset: CGFloat = 7.0
override func drawText(in rect: CGRect) {
let insets = UIEdgeInsets.init(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
super.drawText(in: rect.inset(by: insets))
}
override var intrinsicContentSize: CGSize {
let size = super.intrinsicContentSize
return CGSize(width: size.width + leftInset + rightInset,
height: size.height + topInset + bottomInset)
}
}
Or
you can use CocoaPods here https://github.com/levantAJ/PaddingLabel
pod 'PaddingLabel', '1.1'
Another way would be to override textRect:forBounds:limitedToNumberOfLines:
Returns the drawing rectangle for the label’s text. Override this
method in subclasses that require changes in the label’s bounding
rectangle to occur before the system performs other text layout
calculations. Use the value in the numberOfLines parameter to limit
the height of the returned rectangle to the specified number of lines
of text. This method may be called by the system if there was a prior
call to the sizeToFit() or sizeThatFits(_:) method. Note that labels
in UITableViewCell objects are sized based on cell dimensions, and not
on a requested size.
public class JPSLabel : UILabel
{
open var textInsets: UIEdgeInsets = UIEdgeInsets.zero
override public func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect
{
var insetBounds = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)
insetBounds.size.width += self.textInsets.left + self.textInsets.right
insetBounds.size.height += self.textInsets.top + self.textInsets.bottom
return insetBounds
}
}
I have tested below codes to my app, Swift 5.0 +
class CustomLabel: UILabel {
var textInsets = UIEdgeInsets.zero {
didSet { invalidateIntrinsicContentSize() }
}
override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
let textRect = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)
let invertedInsets = UIEdgeInsets(top: -textInsets.top,
left: -textInsets.left,
bottom: -textInsets.bottom,
right: -textInsets.right)
return textRect.inset(by: invertedInsets)
}
override func drawText(in rect: CGRect) {
super.drawText(in: rect.inset(by: textInsets))
}
}

UILabel clipping italic (oblique) text at left and right edges of content ( iOS 6+)

Problem:
UILabel may clip italic (oblique) characters and even scripts at the left and right edges. The following screenshot displays the issue. At the left edge, the descender of the 'j' is clipped; at the right edge, the ascender of the 'l' is clipped. I realize this is subtle, and not everyone is going to care (however, the issue gets worse with larger font sizes).
Here's a less subtle example using Zapfino, size 22. Note the 'j' in jupiter looks almost like an 'i':
In the examples above, the background color of the label is orange, the text is left aligned, and the label maintains its intrinsic content size.
This is the default behavior of a UILabel and its been that way for multiple versions of iOS (so I'm not expecting a fix from Apple).
What I have tried:
Setting the label's clipsToBounds property to NO does not resolve the issue. I'm also aware that I could set a fixed width constraint on the label to give the text more room at the trailing edge. However, a fixed width constraint would not give the 'j', in the example above, more room.
I'm going to answer my own question using a solution that leverages Auto Layout and the label's alignmentRectInsets.
The top label shows the default behavior of a UILabel when the text is left aligned that the label maintains its intrinsic content size. The bottom label is a simple (almost trivial) subclass of UILabel. The bottom label does not clip the 'j' or the 'l'; instead, it gives the text some room to breathe at the left and right edges without center aligning the text (yuck).
Although the labels themselves don't appear aligned on screen, their text does appear aligned; and what's more, in IB, the labels actually have their left edges aligned because I override alignmentRectInsets in a UILabel subclass.
Here's the code that configures the two labels:
#import "ViewController.h"
#import "NonClippingLabel.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UILabel *topLabel;
#property (weak, nonatomic) IBOutlet NonClippingLabel *bottomLabel;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *string = #"jupiter ariel";
UIFont *font = [UIFont fontWithName:#"Helvetica-BoldOblique" size:28];
NSDictionary *attributes = #{NSFontAttributeName: font};
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:string attributes:attributes];
self.topLabel.attributedText = attrString;
self.bottomLabel.attributedText = attrString;
}
Here's the implementation of the NonClippingLabel subclass:
#import <UIKit/UIKit.h>
#interface NonClippingLabel : UILabel
#end
#implementation NonClippingLabel
#define GUTTER 4.0f // make this large enough to accommodate the largest font in your app
- (void)drawRect:(CGRect)rect
{
// fixes word wrapping issue
CGRect newRect = rect;
newRect.origin.x = rect.origin.x + GUTTER;
newRect.size.width = rect.size.width - 2 * GUTTER;
[self.attributedText drawInRect:newRect];
}
- (UIEdgeInsets)alignmentRectInsets
{
return UIEdgeInsetsMake(0, GUTTER, 0, GUTTER);
}
- (CGSize)intrinsicContentSize
{
CGSize size = [super intrinsicContentSize];
size.width += 2 * GUTTER;
return size;
}
#end
No editing a font file, no using Core Text; just a relatively simple UILabel subclass for those using iOS 6+ and Auto Layout.
Update:
Augie caught the fact that my original solution prevented word wrapping for multi-lined text. I fixed that issue by using drawInRect: instead of drawAtPoint: to draw the text in the label's drawRect: method.
Here's a screenshot:
The top label is a plain-vanilla UILabel. The bottom label is a NonClippingLabel with an extreme gutter setting to accommodate Zapfino at size 22.0. Both labels are left and right aligned using Auto Layout.
Swift version of NonClippingLabel with fixed sizeThatFits method from bilobatum answer.
class NonClippingLabel: UILabel {
let gutter: CGFloat = 4
override func draw(_ rect: CGRect) {
super.drawText(in: rect.insetBy(dx: gutter, dy: 0))
}
override var alignmentRectInsets: UIEdgeInsets {
return .init(top: 0, left: gutter, bottom: 0, right: gutter)
}
override var intrinsicContentSize: CGSize {
var size = super.intrinsicContentSize
size.width += gutter * 2
return size
}
override func sizeThatFits(_ size: CGSize) -> CGSize {
let fixedSize = CGSize(width: size.width - 2 * gutter, height: size.height)
let sizeWithoutGutter = super.sizeThatFits(fixedSize)
return CGSize(width: sizeWithoutGutter.width + 2 * gutter,
height: sizeWithoutGutter.height)
}
}
Rather than having to go thru a bunch of gymnastics to work around this silly Apple bug (which I did), a quick-and-dirty hack is just add a space to the end of your string to stop the last italic letter being clipped. Obviously doesn't help with multi-line labels alas, or clipped first letter descender...
Swift and SwiftUI version based on Vadim Akhmerov and bilobatum's answers. All four edges are now customizable and can be changed/updated.
Also hosted as a Github Gist: https://gist.github.com/ryanlintott/2340f35977bf2d1f7b6ea40aa379bcc6
import SwiftUI
import UIKit
struct NoClipText: UIViewRepresentable {
typealias UIViewType = NoClipLabel
let text: String
let font: UIFont
let clipExtension: EdgeSizes
func makeUIView(context: Context) -> UIViewType {
let uiView = UIViewType()
uiView.text = text
uiView.font = font
uiView.clipExtension = clipExtension
return uiView
}
func updateUIView(_ uiView: UIViewType, context: Context) {
uiView.text = text
uiView.font = font
uiView.clipExtension = clipExtension
}
}
class NoClipLabel: UILabel {
static let defaultClipExtension: EdgeSizes = .all(10)
var clipExtension: EdgeSizes
var top: CGFloat { clipExtension.top }
var left: CGFloat { clipExtension.left }
var bottom: CGFloat { clipExtension.bottom }
var right: CGFloat { clipExtension.right }
var width: CGFloat { left + right }
var height: CGFloat { bottom + top }
required init(clipExtension: EdgeSizes = defaultClipExtension) {
self.clipExtension = clipExtension
super.init(frame: CGRect.zero)
}
override init(frame: CGRect) {
clipExtension = Self.defaultClipExtension
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
clipExtension = Self.defaultClipExtension
super.init(coder: aDecoder)
}
override func draw(_ rect: CGRect) {
super.drawText(in: rect.inset(by: UIEdgeInsets(top: top, left: left, bottom: bottom, right: right)))
}
override var alignmentRectInsets: UIEdgeInsets {
return .init(top: top, left: left, bottom: bottom, right: right)
}
override var intrinsicContentSize: CGSize {
var size = super.intrinsicContentSize
size.width += width
size.height += height
return size
}
override func sizeThatFits(_ size: CGSize) -> CGSize {
let fixedSize = CGSize(width: size.width - width, height: size.height - height)
let sizeWithoutExtension = super.sizeThatFits(fixedSize)
return CGSize(width: sizeWithoutExtension.width + width,
height: sizeWithoutExtension.height + height)
}
}
struct EdgeSizes: Equatable {
let top: CGFloat
let left: CGFloat
let bottom: CGFloat
let right: CGFloat
init(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) {
self.top = top
self.left = left
self.bottom = bottom
self.right = right
}
init(vertical: CGFloat = 0, horizontal: CGFloat = 0) {
self.top = vertical
self.left = horizontal
self.bottom = vertical
self.right = horizontal
}
init(_ all: CGFloat) {
self.top = all
self.left = all
self.bottom = all
self.right = all
}
static let zero = EdgeSizes(0)
static func all(_ size: CGFloat) -> EdgeSizes {
EdgeSizes(size)
}
static func vertical(_ size: CGFloat) -> EdgeSizes {
EdgeSizes(vertical: size)
}
static func horizontal(_ size: CGFloat) -> EdgeSizes {
EdgeSizes(horizontal: size)
}
}

Set padding for UITextField with UITextBorderStyleNone

I wanted to use a custom background for my UITextFields. This works fine except for the fact that I have to use UITextBorderStyleNone to make it look pretty. This forces the text to stick to the left without any padding.
Can I set a padding manually so that it looks similar to UITextBorderStyleRoundedRect except for using my custom background image?
I found a neat little hack to set the left padding for this exact situation.
Basically, you set the leftView property of the UITextField to be an empty view of the size of the padding you want:
UIView *paddingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 5, 20)];
textField.leftView = paddingView;
textField.leftViewMode = UITextFieldViewModeAlways;
Worked like a charm for me!
In Swift 3/ Swift 4, it can be done by doing that
let paddingView: UIView = UIView(frame: CGRect(x: 0, y: 0, width: 5, height: 20))
textField.leftView = paddingView
textField.leftViewMode = .always
I created this category implementation and added it to the top of the .m file.
#implementation UITextField (custom)
- (CGRect)textRectForBounds:(CGRect)bounds {
return CGRectMake(bounds.origin.x + 10, bounds.origin.y + 8,
bounds.size.width - 20, bounds.size.height - 16);
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return [self textRectForBounds:bounds];
}
#end
Based off the link Piotr Blasiak provided. It seemed simpler then creating a whole new subclass, and also simpler then adding the additional UIView. Still, it seems like something is missing to not be able to control the padding inside a text field.
Swift 4 solution:
class CustomTextField: UITextField {
struct Constants {
static let sidePadding: CGFloat = 10
static let topPadding: CGFloat = 8
}
override func textRect(forBounds bounds: CGRect) -> CGRect {
return CGRect(
x: bounds.origin.x + Constants.sidePadding,
y: bounds.origin.y + Constants.topPadding,
width: bounds.size.width - Constants.sidePadding * 2,
height: bounds.size.height - Constants.topPadding * 2
)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return self.textRect(forBounds: bounds)
}
}
A Swift 3 version for Xcode >6, where you can edit the inset value in Interface Builder / Storyboard.
import UIKit
#IBDesignable
class FormTextField: UITextField {
#IBInspectable var inset: CGFloat = 0
override func textRect(forBounds bounds: CGRect) -> CGRect {
return bounds.insetBy(dx: inset, dy: inset)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return textRect(forBounds: bounds)
}
}
Edit: Still works in iOS 11.3.1
In iOS 6 myTextField.leftView = paddingView; is causing issue
This solves the problem
myTextField.layer.sublayerTransform = CATransform3DMakeTranslation(5, 0, 0)
For right aligned text field use CATransform3DMakeTranslation(-5, 0, 0) as mention by latenitecoder in comments
A good approach to add padding to UITextField is to subclass 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 = UIEdgeInsetsZero;
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if(self){
self.edgeInsets = UIEdgeInsetsZero;
}
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
Just subclass UITextField like this:
#implementation DFTextField
- (CGRect)textRectForBounds:(CGRect)bounds
{
return CGRectInset(bounds, 10.0f, 0);
}
- (CGRect)editingRectForBounds:(CGRect)bounds
{
return [self textRectForBounds:bounds];
}
#end
This adds horizontal padding of 10 points either side.
Create a textfield Custom
PaddingTextField.swift
import UIKit
class PaddingTextField: UITextField {
#IBInspectable var paddingLeft: CGFloat = 0
#IBInspectable var paddingRight: CGFloat = 0
override func textRectForBounds(bounds: CGRect) -> CGRect {
return CGRectMake(bounds.origin.x + paddingLeft, bounds.origin.y,
bounds.size.width - paddingLeft - paddingRight, bounds.size.height);
}
override func editingRectForBounds(bounds: CGRect) -> CGRect {
return textRectForBounds(bounds)
}}
Set your textfield class is PaddingTextField and custom your padding as you want
Enjoy it
Objective C Code
MyTextField.h
#import <UIKit/UIKit.h>
#interface MyTextField : UITextField
#property (nonatomic) IBInspectable CGFloat padding;
#end
MyTextField.m
#import "MyTextField.h"
IB_DESIGNABLE
#implementation MyTextField
#synthesize padding;
-(CGRect)textRectForBounds:(CGRect)bounds{
return CGRectInset(bounds, padding, padding);
}
-(CGRect)editingRectForBounds:(CGRect)bounds{
return [self textRectForBounds:bounds];
}
#end
Based on Evil Trout's answer you might wanna create a category to make it easier to use across multiple applications.
Header file:
#interface UITextField (PaddingText)
-(void) setLeftPadding:(int) paddingValue;
-(void) setRightPadding:(int) paddingValue;
#end
Implementation file:
#import "UITextField+PaddingText.h"
#implementation UITextField (PaddingText)
-(void) setLeftPadding:(int) paddingValue
{
UIView *paddingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, paddingValue, self.frame.size.height)];
self.leftView = paddingView;
self.leftViewMode = UITextFieldViewModeAlways;
}
-(void) setRightPadding:(int) paddingValue
{
UIView *paddingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, paddingValue, self.frame.size.height)];
self.rightView = paddingView;
self.rightViewMode = UITextFieldViewModeAlways;
}
#end
Usage Example
#import "UITextField+PaddingText.h"
[self.YourTextField setLeftPadding:20.0f];
Hope it helps you out guys
Cheers
Swift version:
extension UITextField {
#IBInspectable var padding_left: CGFloat {
get {
LF.log("WARNING no getter for UITextField.padding_left")
return 0
}
set (f) {
layer.sublayerTransform = CATransform3DMakeTranslation(f, 0, 0)
}
}
}
So that you can assign value in IB
You can't set padding. Instead have a UIView which has your background image and the UITextField inside of it. Set the UITextField width as UIViewWidth-(paddingSize x 2) and the height similarly and then set it at point paddingSize,paddingSize.
Just subclass UITextField like this (Swift version):
import UIKit
class CustomTextField: UITextField {
override func textRectForBounds(bounds: CGRect) -> CGRect {
return CGRectInset(bounds, 25.0, 0)
}
override func editingRectForBounds(bounds: CGRect) -> CGRect {
return self.textRectForBounds(bounds)
}
}
This adds horizontal padding of 25.0 points either side.
I was based off Nate's solution, but then i found it that this causes problems when you use the leftView/rightView properties, so its better tune the super's implementation, because it will take the left/right view's into account.
- (CGRect)textRectForBounds:(CGRect)bounds {
CGRect ret = [super textRectForBounds:bounds];
ret.origin.x = ret.origin.x + 5;
ret.size.width = ret.size.width - 10;
return ret;
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return [self textRectForBounds:bounds];
}
Updated version for Swift 3:
#IBDesignable
class FormTextField: UITextField {
#IBInspectable var paddingLeft: CGFloat = 0
#IBInspectable var paddingRight: CGFloat = 0
override func textRect(forBounds bounds: CGRect) -> CGRect {
return CGRect(x: bounds.origin.x + paddingLeft, y: bounds.origin.y, width: bounds.size.width - paddingLeft - paddingRight, height: bounds.size.height)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return textRect(forBounds: bounds)
}
}
Set padding for UITextField with UITextBorderStyleNone: Swift
Based on #Evil Trout's most voted answer I created a custom method in my ViewController class, like shown bellow:
- (void) modifyTextField:(UITextField *)textField
{
UIView *paddingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 5, 20)];
textField.leftView = paddingView;
textField.leftViewMode = UITextFieldViewModeAlways;
textField.rightView = paddingView;
textField.rightViewMode = UITextFieldViewModeAlways;
[textField setBackgroundColor:[UIColor whiteColor]];
[textField setTextColor:[UIColor blackColor]];
}
Now I can call that method inside (viewDidLoad method) and send any of my TextFields to that method and add padding for both right and left, and give text and background colors by writing just one line of code, as follows:
[self modifyTextField:self.firstNameTxtFld];
This Worked perfectly on iOS 7!
I know that adding too much Views might make this a bit heavier class to be loaded. But when concerned about the difficulty in other solutions, I found myself more biased to this method and more flexible with using this way. ;)
Thanks for the Hack "Evil Trout"! (bow)
I thought I should update this answer's code snippet with Swift:
Since Swift allow us to write extensions for the existing classes, let's write it in that way.
extension UITextField {
func addPaddingToTextField() {
let paddingView: UIView = UIView.init(frame: CGRectMake(0, 0, 8, 20))
self.leftView = paddingView;
self.leftViewMode = .Always;
self.rightView = paddingView;
self.rightViewMode = .Always;
self.backgroundColor = UIColor.whiteColor()
self.textColor = UIColor.blackColor()
}
}
Usage:
self.firstNameTxtFld.addPaddingToTextField()
Hope this would be helpful to somebody else out there!
Cheers!
Here's how to achieve this in SWIFT
#IBOutlet weak var yourTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
let paddingView = UIView(frame: CGRectMake(0, 0, 10, self.yourTextField.frame.height))
yourTextField.leftView = paddingView
yourTextField.leftViewMode = UITextFieldViewMode.Always
}
}
Resource
Swift 2.0 Version:
let paddingView: UIView = UIView(frame: CGRectMake(0, 0, 5, 20))
textField.leftView = paddingView
textField.leftViewMode = UITextFieldViewMode.Always;
If anyone is looking for Swift 4.0 version then below extension is work. It has both Left and Right padding for UITextField. Actually it is IBInspectable for storyboard configuration. You can set the value directly from the Interface Builder / Storyboard. This is tested code in Swift 4.0 version and Xcode 9.0
Keep in mind that if you want to enable Clear Button on the same UITextField then your have to keep Right Padding blank.
import UIKit
extension UITextField {
#IBInspectable var paddingLeft: CGFloat {
get {
return leftView!.frame.size.width
}
set {
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: newValue, height: frame.size.height))
leftView = paddingView
leftViewMode = .always
}
}
#IBInspectable var paddingRight: CGFloat {
get {
return rightView!.frame.size.width
}
set {
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: newValue, height: frame.size.height))
rightView = paddingView
rightViewMode = .always
}
}
}
^ these suggestions are great for those who are programmatically creating an interface.
But there are two LAZY EASY WAYS for those of us who use the Xcode interface builder:
easier: put a UIImageView behind a text field
easiest: change the border style on your to the simple black square (second from left option), then add your image as a background image. The image takes precedence over the square, so you still get the padding needed for a normal image background, without the square actually being drawn on.
EDIT: you can also use the black sphere (third from left option when selecting the UITextBox in IB), it does not work with the far right, "graphical sphere" style.
The best way to do this is simply make a class using subclass of UITextField and in .m file
#import "CustomTextField.h"
#import <QuartzCore/QuartzCore.h>
#implementation CustomTextField
- (id)initWithCoder:(NSCoder*)coder
{
self = [super initWithCoder:coder];
if (self) {
//self.clipsToBounds = YES;
//[self setRightViewMode:UITextFieldViewModeUnlessEditing];
self.leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0,15,46)];
self.leftViewMode=UITextFieldViewModeAlways;
}
return self;
}
by doing this go to your storyboard or xib and click on identity inspector and replace UITextfield with your own "CustomTextField" in class option.
Note: If you simply give padding with auto layout for textfield then your application will not run and show only blank screen.
Swift 3 Version:
class CustomTextField:UITextField{
required init?(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
override func textRect(forBounds bounds: CGRect) -> CGRect {
return CGRect.init(x: bounds.origin.x + 8, y: bounds.origin.y, width: bounds.width, height: bounds.height)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return self.textRect(forBounds:bounds)
}
}
Nate Flink's answer is my favourite, but don't forget about right/left views.
E.g for UITextField subclass:
override func rightViewRectForBounds(bounds: CGRect) -> CGRect {
let rightViewBounds = super.rightViewRectForBounds(bounds)
return CGRectMake(CGRectGetMinX(rightViewBounds) - 10, CGRectGetMinY(rightViewBounds), CGRectGetWidth(rightViewBounds), CGRectGetHeight(rightViewBounds))
}
Above code set right padding for rightView of UITextField.
Swift 3 Solution
class CustomTextField: UITextField {
override func textRect(forBounds bounds: CGRect) -> CGRect {
return CGRect(x: bounds.origin.x + 10, y: bounds.origin.y + 8, width: bounds.size.width - 20, height: bounds.size.height - 16)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return self.textRect(forBounds: bounds)
}
}
Here is a Swift code to give padding in UITextfield
func txtPaddingVw(txt:UITextField) {
let paddingView = UIView(frame: CGRectMake(0, 0, 10, 10))
txt.leftViewMode = .Always
txt.leftView = paddingView
}
and call using
self.txtPaddingVw(txtPin)
you can use category. set padding to left and right
UITextField+Padding.h
#interface UITextField (Padding)
#property (nonatomic, assign) CGFloat paddingValue;
#property (nonatomic, assign) CGFloat leftPadding;
#property (nonatomic, assign) CGFloat rightPadding;
//overwrite
-(CGRect)textRectForBounds:(CGRect)bounds;
-(CGRect)editingRectForBounds:(CGRect)bounds;
#end
UITextField+Padding.m
#import "UITextField+Padding.h"
#import <objc/runtime.h>
static char TAG_LeftPaddingKey;
static char TAG_RightPaddingKey;
static char TAG_Left_RightPaddingKey;
#implementation UITextField (Padding)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
-(CGRect)textRectForBounds:(CGRect)bounds {
CGFloat offset_Left=0;
CGFloat offset_Right=0;
if (self.paddingValue>0) {
offset_Left=self.paddingValue;
offset_Right=offset_Left;
}else{
if (self.leftPadding>0){
offset_Left=self.leftPadding;
}
if (self.rightPadding>0){
offset_Right=self.rightPadding;
}
}
if (offset_Left>0||offset_Right>0) {
return CGRectMake(bounds.origin.x+ offset_Left ,bounds.origin.y ,
bounds.size.width- (offset_Left+offset_Right), bounds.size.height-2 );
}else{
return bounds;
}
}
-(CGRect)editingRectForBounds:(CGRect)bounds {
return [self textRectForBounds:bounds];
}
#pragma clang diagnostic pop
#pragma maek -setter&&getter
- (CGFloat)paddingValue
{
return [objc_getAssociatedObject(self,&TAG_Left_RightPaddingKey) floatValue];
}
-(void)setPaddingValue:(CGFloat)paddingValue
{
objc_setAssociatedObject(self, &TAG_Left_RightPaddingKey, #(paddingValue), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(CGFloat)leftPadding
{
return [objc_getAssociatedObject(self,&TAG_LeftPaddingKey) floatValue];
}
-(void)setLeftPadding:(CGFloat)leftPadding
{
objc_setAssociatedObject(self, &TAG_LeftPaddingKey, #(leftPadding), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(CGFloat)rightPadding
{
return [objc_getAssociatedObject(self,&TAG_RightPaddingKey) floatValue];
}
-(void)setRightPadding:(CGFloat)rightPadding
{
objc_setAssociatedObject(self, &TAG_RightPaddingKey, #(rightPadding), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#end
you can set padding like this
self.phoneNumTF.paddingValue=10.f;
or
self.phoneNumTF.leftPadding=10.f;
#Evil trout's answer is great. I have been using this approach for quite a some time now. The only thing it lacks is "dealing with numerous text fields". I tried other approaches but does not seem to work.
Subclassing UITextField just to add a padding didn't make any sense to me. So, I iterated over all UITextFields to add the padding.
-(void) addPaddingToAllTextFields:(UIView*)view {
for(id currentView in [view subviews]){
if([currentView isKindOfClass:[UITextField class]]) {
// Change value of CGRectMake to fit ur need
[currentView setLeftView:[[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 20)]];
[currentView setLeftViewMode:UITextFieldViewModeAlways];
}
if([currentView respondsToSelector:#selector(subviews)]){
[textfieldarray addObjectsFromArray:[self addPaddingToAllTextFields:currentView]];
}
}
}
Brody's solution worked perfect for me. I have had to add side views on a textfield and add additional padding. So by implementing the custom UIEdgeInsets property to a UITextField subclass I have managed to achieve the task. I'm going to use this new subclass in all of my projects.
The best solution I found so far is a category. That's how I add a 5 points padding to left and right:
#implementation UITextField (Padding)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
- (CGRect)textRectForBounds:(CGRect)bounds {
return CGRectMake(bounds.origin.x + 5, bounds.origin.y,
bounds.size.width - 10, bounds.size.height);
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return [self textRectForBounds:bounds];
}
#pragma clang diagnostic pop
#end
The #pragma's are just for removing the annoying warnings
textField.layer.borderWidth = 3;
will add border, which worked as padding for me.
I found it far easier to use a non-editable UITextView and set the contentOffset
uiTextView.contentOffset = CGPointMake(8, 7);

Resources