Wstawka tekstu dla UITextField?

Odpowiedzi:

628

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);
}
azdev
źródło
10
To rozwiązanie działało dla mnie, chociaż użyłem wartości zwracanej CGRectInset (bounds, 9, 0); Musiałem także ustawić tę wartość dla textRectForBounds, editRectForBounds i placeholderRectForBounds.
RyJ
2
To rozwiązanie nie działa dobrze z clearButton. Tekst wewnątrz przycisku nakładek TextField.
Piotr,
Myślę, że zastąpienie powyższych metod spowoduje spowolnienie przewijania, jeśli UITextFieldznajduje się w ciągu UIScrollView.
Bharat Dodeja
2
Za umieszczenie przycisku ClearButton: - (CGRect)clearButtonRectForBounds:(CGRect)bounds { return CGRectMake(x, y, w, h); } Znaleziono tutaj: stackoverflow.com/questions/5361369/…
Miros
22
Proponuję wywołać [super textRectForBounds: bounds] i [super editRectForBounds: bounds] przed wywołaniem CGRectInset (bounds, 10, 10). Naprawi to problem z nakładaniem się przycisku usuwania.
mrvincenzo
294

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.

chuthan20
źródło
38
+1 za kreatywność, ale jest to trochę problematyczne, przenosi również przycisk usuwania w polu tekstowym
Nikita
2
możesz zrobić myTextField.layer.sublayers, który jest tablicą wszystkich podwarstw… i jeśli jego UIImageView <- Zakładam, że X jest obrazem… a może UIButton… lub możesz zapętlić każdą z nich i zobaczyć który należy do którego widoku podrzędnego ... ale myfield.layer.sublayer Przetransformuj wszystkie podwarstwy, a zatem przycisk X również się porusza ..
chuthan20
To rozwiązanie nie działa dla mnie. Mogę ustawić tylko lewy i górny margines, ale nie prawy i dolny. UITextFieldnakłada się na treść po prawej stronie.
Bharat Dodeja
2
To najlepsze rozwiązanie bez podziału na klasy i nie wymaga dodatkowych, niepotrzebnych widoków do umieszczenia na ekranie! +1!
Rambatino,
1
@ jeet.chanchawat Przycisk X po prawej stronie na tym zdjęciu .. developer.apple.com/library/content/documentation/…
chuthan20
169

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.

roberto.buratti
źródło
17
Jest to o wiele łatwiejsze niż podklasowanie, aby uzyskać wstawkę, i pozwala dodać dowolny dowolny widok po lewej stronie (możesz także użyć rightView, aby umieścić coś po prawej). Lepsza niż zaakceptowana odpowiedź IMHO.
Kenny Grant
4
+1 łatwe, bezklasowania i zaprojektowane do pracy z właściwościami pola tekstowego (zamiast „hakowania”).
So Over It
Trzecia linia powinna być leftView.backgroundColor = textField.backgroundColor;... Inne niż to świetne rozwiązanie ... Dzięki (:
Aviel Gross
Nie tak elegancki / dokładny jak odpowiedź azdev, ale świetne rozwiązanie dla zwykłego i prostego przypadku!
Rembrandt Q. Einstein
1
Podklasowanie pozwoli ci zaoszczędzić mnóstwo czasu nad tą odpowiedzią, chyba że masz jedno pole tekstowe, do którego potrzebujesz.
Crake
168

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ć.

pociągnięty
źródło
2
tak, jeśli nie przesłonisz editRectForBounds, otrzymasz tekst podczas edycji w lewym górnym rogu pola tekstowego. - (CGRect) editRectForBounds: (CGRect) bounds {return CGRectInset (bounds, 10, 10); }
Mark W
1
Właśnie zredagowałem odpowiedź, aby zintegrować metodę editRectForBounds w
ɾɾɾǝʞ
5
Wydaje mi się to okropnym włamaniem - trzeba by też to zmienić- (CGRect)borderRectForBounds:(CGRect)bounds; - (CGRect)placeholderRectForBounds:(CGRect)bounds; - (CGRect)clearButtonRectForBounds:(CGRect)bounds; - (CGRect)leftViewRectForBounds:(CGRect)bounds; - (CGRect)rightViewRectForBounds:(CGRect)bounds;
Zorayr,
97

Jak o @IBInspectable, @IBDesignableszybkiej 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ęć.

wprowadź opis zdjęcia tutaj

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)
    }
}
zacier
źródło
1
Znalazłem niepożądany efekt na Y, nie chcę zmniejszać prostokąta dla tekstu, a raczej przesunąć go w kierunku linii bazowej pola. Dostosowałem implementacje dolet rect = CGRect(x: bounds.minX, y: bounds.minY + insetY, width: bounds.width, height: bounds.height) return CGRectInset(rect , insetX , 0)
Chris Wagner
1
I dodaj to również, jeśli używasz symbolu zastępczego `override func placeholderRectForBounds (bounds: CGRect) -> CGRect {return CGRectInset (bounds, insetX, insetY)}`
RameshVel
Dziwnie to (ustawienie wstawek w textRect/ editingRect) wpływa na wydajność przewijania (przynajmniej w iOS 12), gdy tekst przepełnia widoczny prostokąt. Przy wstawce 15 przestaje nawet przewijać.
Ixx 18.10.19
29

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, supernajpierw 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 .

Chris Nolet
źródło
Korzystanie textRectForBounds:i editingRectForBounds:metody bez clearButtonRectForBounds: na iOS 7+ działały dla mnie.
Stunner,
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.
Chris Nolet,
Dziwnie wpływa to na wydajność przewijania (przynajmniej na iOS 12), gdy tekst przepełnia widoczny prostokąt. Przy wstawce 15 przestaje nawet przewijać.
Ixx 18.10.19
22

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)
    }
}
smitt04
źródło
2
Nie zapomnij override func placeholderRectForBounds(bounds: CGRect) -> CGRect { return CGRectInset(bounds, inset, inset) }
Eugene Braginets
W Swift 3 musisz użyć metody „CGRect.insetBy ()”
Den
1
Przynajmniej w iOS 11, jeśli przesłonisz 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.
Wyznaczono
Dziwnie wpływa to na wydajność przewijania (przynajmniej na iOS 12), gdy tekst przepełnia widoczny prostokąt. Przy wstawce 15 przestaje nawet przewijać.
Ixx 18.10.19
14

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 .

Sam Soffes
źródło
To podejście, wraz z używaniem kokosowych do importowania kapsuły SSToolkit, działa świetnie - myślę, że jest to najładniejszy sposób.
Chris
Dzięki Chris! Cieszę się, że przydało ci się.
Sam Soffes,
14

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)
    }
}
lcl
źródło
Dziwnie wpływa to na wydajność przewijania (przynajmniej na iOS 12), gdy tekst przepełnia widoczny prostokąt. Przy wstawce 15 przestaje nawet przewijać.
Ixx 18.10.19
12

Dla osób, które szukają łatwiejszego rozwiązania.

Dodaj UITextFieldwnę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;
karim
źródło
2
Szczerze mówiąc, umieszczenie UIView za UITextField jest najlepszym i najprostszym rozwiązaniem. Uczyń UITextField przezroczystym i gotowym. Wyrównałem go za pomocą UITextView - okazuje się, że ma około 6 pikseli. O wiele łatwiejsze i bardziej elastyczne niż tworzenie podklasy ...
n13
Problemem z tym podejściem jest lokalizacja, w której pojawi się pasek przewijania.
Doug Amos,
@DougAmos Jaki pasek przewijania? Masz na myśli UITextViewmoże?
znaczenie-ważne
12

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;
Złoty
źródło
1
Kiedy musisz także użyć lewego widoku dla ikony, to nie działa
Reaper
@Reaper ta metoda będzie również działać dla obrazu. dodaj ilość dopełnienia, którą chcesz, do szerokości ramki widoku obrazu i ustaw tryb treści na środek. imageView.contentMode = UIViewContentMode.Center imageView.frame = CGRectMake(0.0, 0.0, imageView.image!.size.width + 16.0, imageView.image!.size.height)
Andy,
to jest zbyt zuchwałe. istnieje już metoda textRectForBounds do ustawiania wstawki
Gerald
12

Szybki

    // adjust place holder text
    let paddingView = UIView(frame: CGRectMake(0, 0, 10, usernameOrEmailField.frame.height))
    usernameOrEmailField.leftView = paddingView
    usernameOrEmailField.leftViewMode = UITextFieldViewMode.Always
LondonGuy
źródło
1
To jest naprawdę tanie i łatwe obejście. Dzięki!
shnaz
11

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
Brody Robertson
źródło
Dobra odpowiedź. Dostarcza brakującą właściwość :-)
phatmann
6

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:

stosowanie

I

wprowadź opis zdjęcia tutaj

zgorawski
źródło
5

Zrobiłem to w IB, gdzie utworzyłem UIView Behind the textView, który był nieco dłuższy. Po wyczyszczeniu koloru tła textField. wprowadź opis zdjęcia tutaj

Jeremy Wilson
źródło
5

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
Max
źródło
4

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)
        }
Gene Loparco
źródło
4

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

}
Bruno Paulino
źródło
Dziwnie wpływa to na wydajność przewijania (przynajmniej na iOS 12), gdy tekst przepełnia widoczny prostokąt. Przy wstawce 15 przestaje nawet przewijać.
Ixx 18.10.19
3

Możesz dostosować położenie tekstu w polu tekstowym, czyniąc go podklasą UITextFieldi zastępując -textRectForBounds:metodę.

Noah Witherspoon
źródło
3

Absurdalne jest, że trzeba UITextFieldpodklasować , 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)
    }

}
Christopher Pickslay
źródło
Link w Twojej odpowiedzi jest martwy, czy możesz go zaktualizować?
WendiKidd,
Naprawiono adres URL @WendiKidd
Christopher Pickslay
2

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
Firula
źródło
Dziękuję Ci. Jedna sugestia do twojego kodu - dlaczego użyłeś CGRect do wstawienia, a nie UIEdgeInsets?
sha
2

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 UITextFieldDelegatepó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 UITextFieldklasie.

//  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 textFieldDidBeginEditingmetodę, mniej więcej tak:

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    textField.indent = 7;
}
użytkownik3878720
źródło
Założenie, które defaultTextAttributeszawiera, NSMutableParagraphStylejest dość niebezpieczne. Wolę mutable. Skopiuj to wszystko.
Ben Sinclair,
1

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)];
}
Adam Waite
źródło
Czy jest jakiś powód, dla którego unikasz podklasy? Jest to prawidłowy paradygmat projektowy.
Stunner,
1

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];

Oryginalny UITextField Naprawiono UITextField

Testowane z iOS 7 i iOS 8. Oba działają. Nadal może istnieć szansa, że ​​Apple zmodyfikuje hierarchię warstw UITextField, co źle psuje.

TheGrumpyCoda
źródło
1

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

}
rmooney
źródło
1

Rozwiązanie, które faktycznie działa i obejmuje wszystkie przypadki:

  • Należy używać offsetBynieinsetBy .
  • Powinien także wywołać funkcję super, aby uzyskać oryginał Rect .
  • Granice są wadliwe. musisz przesunąć oryginalne X, Y. Granice mają X, Y jako zera.
  • Oryginalne x, y może być niezerowe na przykład podczas ustawiania leftView pola UITextField.

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)
}
Hasan
źródło
0

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 );
}
Mann
źródło
-1

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);
            }
        }
}
Bhavesh Tiwari
źródło