文本插入UITextField?

我想插入UITextField文本

这可能吗?

222798 次浏览

在从UITextField派生的类中,至少重写以下两个方法:

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

如果你没有额外的内容,它可能就像这样简单:

return CGRectInset(bounds , 10, 10);

UITextField提供了几个可以覆盖的定位方法。

你可以通过使文本字段成为UITextField的子类并覆盖-textRectForBounds:方法来调整文本在文本字段中的位置。

重写-textRectForBounds:只会改变占位符文本的插入。要更改可编辑文本的插入,还需要重写-editingRectForBounds:

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


// text position
- (CGRect)editingRectForBounds:(CGRect)bounds {
return CGRectInset(bounds, 10, 10);
}

对于那些正在寻找更简单解决方案的人来说。

UIView中添加UITextField。为了模拟文本框周围的插入,我保持左10像素,宽度比视图小20像素。对于文本框周围的圆角边框,请使用视图的边框

viewBG.layer.cornerRadius = 8.0;
viewBG.layer.borderColor = [UIColor darkGrayColor].CGColor;
viewBG.layer.borderWidth = 1.0;

使用textRectForBounds:是正确的方法。我已经把这个包在我的子类中,所以你可以简单地使用textEdgeInsets。看到SSTextField

我做到了:

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

当然记得导入QuartzCore并将框架添加到您的项目中。

如果你只需要左边空白,你可以试试这个:

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;

这对我很管用。我希望这能有所帮助。

如果你想改变TOP和LEFT缩进只有这样

//占位符

- (CGRect)textRectForBounds:(CGRect)bounds {


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

//文本位置

- (CGRect)editingRectForBounds:(CGRect)bounds {


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

为UITextField添加填充的一个好方法是子类化UITextField并添加一个edgeInsets属性。然后设置edgeInsets和UITextField将相应地绘制。对于自定义的leftView或rightView集,这也能正确地发挥作用。

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

我子类化UITextField来处理这个,它支持左,上,右和底部的插入,以及清除按钮的定位。

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

*_someTextField*来自nib/storyboard视图,带有MRDInsetTextField自定义类的用法示例

[(MRDInsetTextField*)_someTextField setInset:CGRectMake(5, 0, 5, 0)]; // left, top, right, bottom inset

我通常尽量避免子类化,但如果你已经:

// 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)];
}

如果你有一个明确的按钮,接受的答案将不适合你。我们还应该防止苹果在未来通过调用super来改变事情。

因此,为了确保文本不会与clear按钮重叠,让我们先从super获取“默认”值,然后根据需要进行调整。

这段代码将在文本框的顶部、左侧和底部添加一个10px的insets:

@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

注意:UIEdgeInsetsMake接受参数的顺序是:正确的

你可以通过设置leftView来设置UITextField的文本插入。

是这样的:

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;

这个示例不像其他示例那么短,但是采用了一种完全不同的方法来解决这个问题。请注意,插入符号仍将开始与左边缘齐平,但文本将在输入/显示时适当缩进。如果你只寻找左边空白,并且你已经在文本字段中使用UITextFieldDelegate,那么这个方法不需要子类化。您需要设置默认的文本属性和键入属性。在创建文本字段时设置默认文本属性。需要在委托中设置的类型属性。如果你也在使用占位符,你也要设置相同的边距。把它们放在一起,你会得到这样的结果。

首先在UITextField类上创建一个类别。

//  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

然后,如果您使用放置占位符,请确保使用带有属性的占位符设置相同的缩进。创建一个具有正确属性的默认属性字典,如下所示:

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

然后,导入上述类别,每当你创建一个文本字段设置默认缩进,委托和使用默认占位符属性上面定义。例如:

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

最后,在委托中,实现textFieldDidBeginEditing方法,如下所示:

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
textField.indent = 7;
}

我想我可以提供一个快速解决方案

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

斯威夫特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)
}
}

抛出另一个不需要子类化的解决方案:

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

在iOS 7和iOS 8上测试。这两个工作。苹果仍然有可能修改UITextField的层层次结构,把事情搞砸了。

@IBInspectable@IBDesignable swift类怎么样?

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

你会在你的故事板中看到这个。

enter image description here

更新- 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)
}
}

你必须子类化是荒谬的,因为UITextField已经实现了这些方法,正如@Adam Waite指出的那样。下面是一个快速扩展,它公开了一个工厂方法,在类别回购中也可用:

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


}

斯威夫特

    // adjust place holder text
let paddingView = UIView(frame: CGRectMake(0, 0, 10, usernameOrEmailField.frame.height))
usernameOrEmailField.leftView = paddingView
usernameOrEmailField.leftViewMode = UITextFieldViewMode.Always

下面是一个全面的Swift答案,包括一个leftView(自定义图标)和一个自定义清除按钮,两者都设置在界面生成器中,具有可定制的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)
}


}

下面是在Swift 3中编写的相同子类UITextField。它与之前的Swift版本有很大的不同,正如你将看到的:

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

顺便说一句,如果你只想控制一侧的插入,你也可以像下面这样做。如果你把一个图像放在UITextField的顶部,但你想让它在用户看来是在文本框中,那么这个只调整左边插图的特殊例子就很方便了:

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

我在IB中这样做,我在textView后面创建了一个UIView,它有点长。与textField背景颜色设置为清除。 enter image description here < / p >

Swift 3 /可在界面构建器中设计/单独的水平和放大器;垂直昆虫/开箱即用

@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

,

enter image description here

斯威夫特

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

这是我发现的最快的方法,不需要做任何子类:

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

迅速:

let spacerView = UIView(frame:CGRect(x:0, y:0, width:10, height:10))
textField.leftViewMode = UITextFieldViewMode.Always
textField.leftView = spacerView

快速解决没有子类&还inspectable

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

斯威夫特4.2版本:

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


}

解决方案:实际有效且适用于所有情况的解决方案:

  • 应该使用offsetBy而不是insetBy
  • 也应该调用超函数来获取原始的Rect
  • Bounds故障。你需要抵消原来的X, Y,边界有X, Y为零。
  • 原始的x, y可以是非零,例如当设置UITextField的leftView时。

示例:

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

我发现了罗伯托发布的选项。buratti是最快的解决方案,这里是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

克里斯托弗的回答的Swift 5版本,带有额外的用法示例

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


}

用法:-

class ViewController: UIViewController {


private let passwordTextField: UITextField = {


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

return textField
}()


}

您可能需要这个同时支持leftView和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))
}
}