Odpowiedzi:
Zastąpienie -textRectForBounds:
zmieni tylko wstawkę tekstu zastępczego. Aby zmienić wstawkę edytowalnego tekstu, musisz również zastąpić-editingRectForBounds:
// placeholder position
- (CGRect)textRectForBounds:(CGRect)bounds {
return CGRectInset(bounds, 10, 10);
}
// text position
- (CGRect)editingRectForBounds:(CGRect)bounds {
return CGRectInset(bounds, 10, 10);
}
UITextField
znajduje się w ciągu UIScrollView
.
- (CGRect)clearButtonRectForBounds:(CGRect)bounds { return CGRectMake(x, y, w, h); }
Znaleziono tutaj: stackoverflow.com/questions/5361369/…
Byłem w stanie to zrobić poprzez:
myTextField.layer.sublayerTransform = CATransform3DMakeTranslation(5, 0, 0);
Oczywiście pamiętaj, aby zaimportować QuartzCore, a także dodać Framework do swojego projektu.
UITextField
nakłada się na treść po prawej stronie.
Jeśli potrzebujesz tylko lewego marginesu, możesz spróbować:
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;
Mi to pasuje. Mam nadzieję, że to może pomóc.
leftView.backgroundColor = textField.backgroundColor;
... Inne niż to świetne rozwiązanie ... Dzięki (:
W klasie pochodnej od UITextField, przesłoń przynajmniej te dwie metody:
- (CGRect)textRectForBounds:(CGRect)bounds;
- (CGRect)editingRectForBounds:(CGRect)bounds;
To może być tak proste, jeśli nie masz dodatkowej zawartości:
return CGRectInset(bounds , 10, 10);
UITextField zapewnia kilka metod pozycjonowania, które można zastąpić.
- (CGRect)borderRectForBounds:(CGRect)bounds; - (CGRect)placeholderRectForBounds:(CGRect)bounds; - (CGRect)clearButtonRectForBounds:(CGRect)bounds; - (CGRect)leftViewRectForBounds:(CGRect)bounds; - (CGRect)rightViewRectForBounds:(CGRect)bounds;
Jak o @IBInspectable
, @IBDesignable
szybkiej klasie.
@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)
}
}
Zobaczysz to w swojej serii ujęć.
Aktualizacja - 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)
}
}
let rect = CGRect(x: bounds.minX, y: bounds.minY + insetY, width: bounds.width, height: bounds.height) return CGRectInset(rect , insetX , 0)
textRect
/ editingRect
) wpływa na wydajność przewijania (przynajmniej w iOS 12), gdy tekst przepełnia widoczny prostokąt. Przy wstawce 15 przestaje nawet przewijać.
Jeśli masz wyraźny przycisk, zaakceptowana odpowiedź nie zadziała. Powinniśmy również chronić się przed zmianami Apple w przyszłości, dzwoniąc super
.
Tak więc, aby upewnić się, że tekst nie nakłada się na czysty przycisk, super
najpierw uzyskamy wartość „domyślną” , a następnie dostosujemy w razie potrzeby.
Ten kod doda wstawki 10px w górnej, lewej i dolnej części pola tekstowego:
@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
Uwaga: UIEdgeInsetsMake przyjmuje parametry w kolejności: góra , lewo , dół , prawo .
textRectForBounds:
i editingRectForBounds:
metody bez clearButtonRectForBounds:
na iOS 7+ działały dla mnie.
clearButtonRectForBounds:
po prostu pomaga przesunąć nieco przycisk usuwania w lewo. Może chcesz to pominąć. Moje pole tekstowe było na ciemnym tle, a wyraźny przycisk wymagał trochę dopełnienia po prawej stronie.
Myślałem, że dostarczę szybkie rozwiązanie
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)
}
}
override func placeholderRectForBounds(bounds: CGRect) -> CGRect { return CGRectInset(bounds, inset, inset) }
textRectForBounds
, zmieniony zostanie również symbol zastępczy - więc dodanie zastąpienia zastępczego wstawia symbol zastępczy o kolejne 10 punktów. Jeśli tego właśnie szukasz, 👍🏼, ale jeśli nie, dobrze jest być tego świadomym.
Używanie textRectForBounds:
jest właściwym podejściem. Zawarłem to w mojej podklasie, abyś mógł po prostu użyć textEdgeInsets
. Zobacz SSTextField .
Szybki
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)
}
}
Dla osób, które szukają łatwiejszego rozwiązania.
Dodaj UITextField
wnętrze a UIView
. Aby zasymulować wstawkę wokół pola tekstowego, pozostaję 10 pikseli na lewo, a szerokość jest o 20 pikseli mniejsza niż widok. Aby zaokrąglić obramowanie narożnika wokół pola tekstowego, użyj obramowania widoku
viewBG.layer.cornerRadius = 8.0;
viewBG.layer.borderColor = [UIColor darkGrayColor].CGColor;
viewBG.layer.borderWidth = 1.0;
UITextView
może?
Możesz ustawić wstawkę tekstu dla UITextField, ustawiając leftView.
Lubię to:
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;
imageView.contentMode = UIViewContentMode.Center imageView.frame = CGRectMake(0.0, 0.0, imageView.image!.size.width + 16.0, imageView.image!.size.height)
Szybki
// adjust place holder text
let paddingView = UIView(frame: CGRectMake(0, 0, 10, usernameOrEmailField.frame.height))
usernameOrEmailField.leftView = paddingView
usernameOrEmailField.leftViewMode = UITextFieldViewMode.Always
Dobrym podejściem do dodawania dopełnienia do UITextField jest podklasa UITextField i dodanie właściwości edgeInsets. Następnie ustawisz edgeInsets, a pole UITextField zostanie odpowiednio narysowane. Będzie to również działać poprawnie z niestandardowym zestawem leftView lub 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
Swift 3 / Designable in Builder Interface / Oddzielne owady poziome i pionowe / użyteczne po wyjęciu z pudełka
@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)
}
}
stosowanie:
I
To najszybszy sposób, jaki udało mi się znaleźć bez robienia podklas:
UIView *spacerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10., 10.)];
[textField setLeftViewMode:UITextFieldViewModeAlways];
[textField setLeftView:spacerView];
W Swift:
let spacerView = UIView(frame:CGRect(x:0, y:0, width:10, height:10))
textField.leftViewMode = UITextFieldViewMode.Always
textField.leftView = spacerView
Oto ta sama podklasa UITextField napisana w Swift 3. Zupełnie różni się od poprzednich wersji Swift, jak zobaczysz:
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)
}
}
Nawiasem mówiąc, możesz również wykonać następujące czynności, jeśli chcesz kontrolować wstawkę tylko jednej strony. Ten konkretny przykład dopasowania tylko lewej wstawki jest przydatny, jeśli umieścisz obraz na polu UITextField, ale chcesz, aby pojawił się on w polu tekstowym:
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)
}
Wersja Swift 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)
}
}
Możesz dostosować położenie tekstu w polu tekstowym, czyniąc go podklasą UITextField
i zastępując -textRectForBounds:
metodę.
Absurdalne jest, że trzeba UITextField
podklasować , ponieważ już implementuje metody, jak wskazuje @Adam Waite. Oto szybkie rozszerzenie, które ujawnia fabryczną metodę, dostępną również w naszym repozytorium kategorii :
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)
}
}
Podklasowałem UITextField, aby obsłużyć tę funkcję, która obsługuje również wstawkę lewą, górną, prawą i dolną, a także wyraźne pozycjonowanie przycisków.
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
Przykład użycia, w którym * _someTextField * pochodzi z widoku stalówki / serii ujęć z niestandardową klasą MRDInsetTextField
[(MRDInsetTextField*)_someTextField setInset:CGRectMake(5, 0, 5, 0)]; // left, top, right, bottom inset
Nie jest to tak krótkie jak inne przykłady, ale ma zupełnie inne podejście do rozwiązania tego problemu. Uwaga: kursor nadal zacznie być wyrównany do lewej krawędzi, ale po wpisaniu / wyświetleniu tekst będzie odpowiednio wcięty. Działa to bez tworzenia podklasy, jeśli szukasz tylko lewego marginesu i już używasz UITextFieldDelegate
pól tekstowych. Musisz ustawić zarówno domyślne atrybuty tekstu, jak i atrybuty pisania. Domyślne atrybuty tekstu ustawia się podczas tworzenia pola tekstowego. Atrybuty pisania, które należy ustawić w delegacie. Jeśli używasz również symbolu zastępczego, chcesz ustawić go również na tym samym marginesie. Podsumowując, otrzymujesz coś takiego.
Najpierw utwórz kategorię w UITextField
klasie.
// 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
Następnie, jeśli używasz uchwytów umieszczonych, upewnij się, że używasz przypisanego elementu zastępczego, który ustawia to samo wcięcie. Utwórz domyślny słownik z właściwymi atrybutami, podobny do tego:
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.firstLineHeadIndent = 7;
paragraphStyle.headIndent = 7;
NSDictionary *placeholderAttributes = [NSDictionary dictionaryWithObjectsAndKeys: paragraphStyle, NSParagraphStyleAttributeName, nil];
Następnie zaimportuj powyższą kategorię i za każdym razem, gdy utworzysz pole tekstowe, ustaw domyślne wcięcie, deleguj i użyj domyślnych atrybutów symboli zastępczych zdefiniowanych powyżej. Na przykład:
UITextField *textField = [[UITextField alloc] init];
textField.indent = 7;
textField.delegate = self;
textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"Placeholder Text" attributes:placeholderAttributes];
Na koniec, w delegacie, zaimplementuj textFieldDidBeginEditing
metodę, mniej więcej tak:
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
textField.indent = 7;
}
defaultTextAttributes
zawiera, NSMutableParagraphStyle
jest dość niebezpieczne. Wolę mutable. Skopiuj to wszystko.
Zwykle staram się unikać podklas, ale działa to, jeśli masz już:
// 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)];
}
Aby wprowadzić inne rozwiązanie, które nie wymaga podklasowania:
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];
Testowane z iOS 7 i iOS 8. Oba działają. Nadal może istnieć szansa, że Apple zmodyfikuje hierarchię warstw UITextField, co źle psuje.
Oto wyczerpująca odpowiedź Swift, która zawiera lewy widok (ikona niestandardowa) i niestandardowy przycisk czyszczenia, oba ustawione w Konstruktorze interfejsów z dostosowywanymi wstawkami.
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)
}
}
Rozwiązanie, które faktycznie działa i obejmuje wszystkie przypadki:
offsetBy
nieinsetBy
.Rect
.Próba:
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)
}
Jeśli chcesz zmienić wcięcie GÓRA i LEWO tylko wtedy
// pozycja zastępcza
- (CGRect)textRectForBounds:(CGRect)bounds {
CGRect frame = bounds;
frame.origin.y = 3;
frame.origin.x = 5;
bounds = frame;
return CGRectInset( bounds , 0 , 0 );
}
// pozycja tekstu
- (CGRect)editingRectForBounds:(CGRect)bounds {
CGRect frame = bounds;
frame.origin.y = 3;
frame.origin.x = 5;
bounds = frame;
return CGRectInset( bounds , 0 , 0 );
}
Szybkie rozwiązanie bez podklasy i również sprawdzalne
extension UITextField {
@IBInspectable var textInsets: CGPoint {
get {
return CGPoint.zero
}
set {
layer.sublayerTransform = CATransform3DMakeTranslation(newValue.x, newValue.y, 0);
}
}
}