Encart texte pour UITextField?

Réponses:

628

La substitution -textRectForBounds:ne modifiera que l'encart du texte d'espace réservé. Pour modifier l'encart du texte modifiable, vous devez également remplacer-editingRectForBounds:

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

// text position
- (CGRect)editingRectForBounds:(CGRect)bounds {
     return CGRectInset(bounds, 10, 10);
}
azdev
la source
10
Cette solution a fonctionné pour moi, bien que j'aie utilisé une valeur de retour de CGRectInset (limites, 9, 0); J'ai également eu besoin de définir cette valeur pour textRectForBounds, editRectForBounds et placeholderRectForBounds.
RyJ
2
Cette solution ne fonctionne pas bien avec clearButton. Texte à l'intérieur du bouton de superpositions TextField.
Piotr
Je pense que remplacer les méthodes ci-dessus entraînera un défilement lent, si UITextFieldréside dans un UIScrollView.
Bharat Dodeja
2
Pour placer le ClearButton: - (CGRect)clearButtonRectForBounds:(CGRect)bounds { return CGRectMake(x, y, w, h); } Trouvé ici: stackoverflow.com/questions/5361369/…
Miros
22
Je suggère d'appeler [super textRectForBounds: bounds] et [super editRectForBounds: bounds] avant d'appeler CGRectInset (bounds, 10, 10). Cela résoudra le problème de superposition du bouton d'effacement.
mrvincenzo
294

J'ai pu le faire grâce à:

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

Bien sûr, n'oubliez pas d'importer QuartzCore et d'ajouter également le Framework à votre projet.

chuthan20
la source
38
+1 pour la créativité, mais c'est un peu problématique, il déplace également le bouton de suppression à l'intérieur du champ de texte
Nikita
2
vous pouvez faire myTextField.layer.sublayers qui est un tableau de toutes les sous-couches ... et si son UIImageView <- je suppose que le X est une image .. ou peut-être UIButton ... ou vous pouvez parcourir en boucle chacun d'eux et voir lequel appartient à quelle sous-vue ... mais myfield.layer.sublayerTransformez toutes les sous-couches et donc le bouton X se déplaçant également ..
chuthan20
Cette solution ne fonctionne pas pour moi. Je ne peux définir que la marge gauche et supérieure, mais pas la droite et le bas. UITextFieldchevauche le contenu à droite de celui-ci.
Bharat Dodeja
2
C'est la meilleure solution sans sous-classement et ne nécessite pas de vues supplémentaires inutiles pour être placées à l'écran! +1!
Rambatino
1
@ jeet.chanchawat Le bouton X sur le côté droit de cette image .. developer.apple.com/library/content/documentation/…
chuthan20
169

Si vous avez juste besoin d'une marge gauche, vous pouvez essayer ceci:

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;

Ça marche pour moi. J'espère que cela peut aider.

roberto.buratti
la source
17
C'est beaucoup plus facile que de sous-classer juste pour obtenir un encart, et vous permet d'ajouter n'importe quelle vue arbitraire à gauche (vous pouvez également utiliser rightView pour mettre quelque chose à droite). Mieux que la réponse acceptée à mon humble avis.
Kenny Grant
4
+1 facile, sans sous-classement, et conçu pour fonctionner avec des propriétés de champ de texte (plutôt que de «pirater»).
So Over It
La troisième ligne devrait être leftView.backgroundColor = textField.backgroundColor;... Autre que cette excellente solution ... Merci (:
Aviel Gross
Pas tout à fait aussi élégant / complet que la réponse d'azdev, mais une excellente solution pour un cas simple et commun!
Rembrandt
1
Le sous-classement vous fera gagner une tonne de temps sur cette réponse, à moins que vous n'ayez besoin d'une seule zone de texte pour cela.
Crake
168

Dans une classe dérivée de UITextField, remplacez au moins ces deux méthodes:

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

Cela peut être aussi simple que cela si vous n'avez pas de contenu supplémentaire:

return CGRectInset(bounds , 10, 10);

UITextField fournit plusieurs méthodes de positionnement que vous pouvez remplacer.

dessiné
la source
2
oui, si vous ne remplacez pas editRectForBounds, vous obtenez le texte lors de l'édition en haut à gauche du champ de texte. - (CGRect) editRectForBounds: (CGRect) limites {retour CGRectInset (limites, 10, 10); }
Mark W
1
Je viens d'éditer la réponse pour intégrer la méthode editRectForBounds dans
ıɾuǝʞ
5
Cela me semble un horrible hack - vous auriez également besoin de passer outre- (CGRect)borderRectForBounds:(CGRect)bounds; - (CGRect)placeholderRectForBounds:(CGRect)bounds; - (CGRect)clearButtonRectForBounds:(CGRect)bounds; - (CGRect)leftViewRectForBounds:(CGRect)bounds; - (CGRect)rightViewRectForBounds:(CGRect)bounds;
Zorayr
98

Que diriez - vous d' une @IBInspectable, @IBDesignableclasse rapide.

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

Vous verrez cela dans votre storyboard.

entrez la description de l'image ici

Mise à jour - 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)
    }
}
purée
la source
1
J'ai trouvé l'effet sur Y indésirable, je ne veux pas rétrécir le rect pour le texte, plutôt le pousser vers la ligne de base du champ. J'ai ajusté les implémentations pourlet rect = CGRect(x: bounds.minX, y: bounds.minY + insetY, width: bounds.width, height: bounds.height) return CGRectInset(rect , insetX , 0)
Chris Wagner
1
Et ajoutez-le aussi si vous utilisez un espace réservé `override func placeholderRectForBounds (bounds: CGRect) -> CGRect {return CGRectInset (bounds, insetX, insetY)}`
RameshVel
Étrangement, cela (définir les encarts dans textRect/ editingRect) affecte les performances de défilement (sur iOS 12 au moins), lorsque le texte déborde du rect visible. Avec un encart de 15, il arrête même de défiler.
Ixx
29

Si vous avez un bouton clair, la réponse acceptée ne fonctionnera pas pour vous. Nous devons également nous prémunir contre les changements futurs d'Apple en appelant super.

Donc, pour vous assurer que le texte ne chevauche pas le bouton d'effacement, obtenons d'abord la valeur `` par défaut '' super, puis ajustez-la si nécessaire.

Ce code ajoutera un encart de 10 pixels en haut, à gauche et en bas du champ de texte:

@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

Remarque: UIEdgeInsetsMake prend les paramètres dans l'ordre: haut , gauche , bas , droite .

Chris Nolet
la source
L'utilisation textRectForBounds:et les editingRectForBounds:méthodes sans clearButtonRectForBounds: sur iOS 7+ ont fonctionné pour moi.
Stunner
clearButtonRectForBounds:aide juste à pousser un peu le bouton d'effacement vers la gauche. Vous voudrez peut-être le laisser de côté. Mon champ de texte était sur un fond sombre et le bouton d'effacement avait besoin d'un peu de rembourrage supplémentaire sur la droite.
Chris Nolet
Bizarrement, cela affecte les performances de défilement (sur iOS 12 au moins), lorsque le texte déborde du rect visible. Avec un encart de 15, il arrête même de défiler.
Ixx
22

Je pensais que je fournirais une solution Swift

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
la source
2
N'oubliez pas override func placeholderRectForBounds(bounds: CGRect) -> CGRect { return CGRectInset(bounds, inset, inset) }
Eugene Braginets
Dans Swift 3, vous devez utiliser la méthode 'CGRect.insetBy ()'
Den
1
Dans iOS 11 au moins, si vous remplacez textRectForBounds, l'espace réservé est également affecté - donc l'ajout de la substitution d'espace réservé insère l'espace réservé de 10 points supplémentaires. Si c'est ce que vous recherchez, 👍🏼, mais sinon, il est bon d'en être conscient.
DesignatedNerd
Bizarrement, cela affecte les performances de défilement (sur iOS 12 au moins), lorsque le texte déborde du rect visible. Avec un encart de 15, il arrête même de défiler.
Ixx
14

L'utilisation textRectForBounds:est la bonne approche. J'ai enveloppé cela dans ma sous-classe afin que vous puissiez simplement l'utiliser textEdgeInsets. Voir SSTextField .

Sam Soffes
la source
Cette approche, ainsi que l'utilisation de cocoapods pour importer le pod SSToolkit, fonctionne très bien - je pense que c'est la meilleure façon de procéder.
Chris
Merci Chris! Content que vous ayez trouvé ça utile.
Sam Soffes
14

Rapide

 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
la source
Bizarrement, cela affecte les performances de défilement (sur iOS 12 au moins), lorsque le texte déborde du rect visible. Avec un encart de 15, il arrête même de défiler.
Ixx
12

Pour les personnes qui recherchent une solution plus simple.

Ajoutez l' UITextFieldintérieur a UIView. Pour simuler un encart autour du champ de texte, je garde 10 px à gauche et la largeur est inférieure de 20 pixels à la vue. Pour une bordure de coin arrondie autour du champ de texte, utilisez la bordure de la vue

viewBG.layer.cornerRadius = 8.0;
viewBG.layer.borderColor = [UIColor darkGrayColor].CGColor;
viewBG.layer.borderWidth = 1.0;
Karim
la source
2
Honnêtement, le simple fait de placer une UIView derrière UITextField est la solution la meilleure et la plus simple. Rendez UITextField transparent et terminé. Je l'ai aligné avec un UITextView - se révèle être environ 6 pixels en encart. Beaucoup plus facile et aussi plus flexible que la création d'une sous-classe ...
n13
Un problème avec cette approche est l'emplacement de la barre de défilement.
Doug Amos
@DougAmos Quelle barre de défilement? Parlez-vous UITextViewpeut-être?
signification est importante le
12

Vous pouvez définir un encart de texte pour UITextField en définissant leftView.

Comme ça:

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;
d'or
la source
1
Lorsque vous devez également utiliser la vue de gauche pour une icône, cela ne fonctionne pas
Reaper
@Reaper cette méthode fonctionnera également pour une image. ajoutez la quantité de remplissage souhaitée à la largeur du cadre de visualisation d'image et définissez le mode de contenu au centre. imageView.contentMode = UIViewContentMode.Center imageView.frame = CGRectMake(0.0, 0.0, imageView.image!.size.width + 16.0, imageView.image!.size.height)
Andy
c'est beaucoup trop hacky. il existe déjà une méthode textRectForBounds pour définir l'encart
Gerald
12

Rapide

    // adjust place holder text
    let paddingView = UIView(frame: CGRectMake(0, 0, 10, usernameOrEmailField.frame.height))
    usernameOrEmailField.leftView = paddingView
    usernameOrEmailField.leftViewMode = UITextFieldViewMode.Always
LondonGuy
la source
1
Il s'agit d'une solution de contournement vraiment bon marché et facile. Merci!
shnaz
11

Une bonne approche pour ajouter un remplissage à UITextField consiste à sous-classer UITextField et à ajouter une propriété edgeInsets. Vous définissez ensuite les edgeInsets et le UITextField sera dessiné en conséquence. Cela fonctionnera également correctement avec un ensemble leftView ou rightView personnalisé.

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
la source
Bonne réponse. Fournit la propriété manquante :-)
phatmann
6

Swift 3 / Designable dans le générateur d'interface / Insectes horizontaux et verticaux séparés / utilisables dès la sortie

@IBDesignable
class TextFieldWithPadding: UITextField {

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

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

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

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

usage:

usage

&

entrez la description de l'image ici

zgorawski
la source
5

Je l'ai fait dans IB où j'ai créé une UIView Derrière la textView qui était un peu plus longue. Avec la couleur d'arrière-plan textField définie sur clear. entrez la description de l'image ici

Jeremy Wilson
la source
5

C'est le moyen le plus rapide que j'ai trouvé sans faire de sous-classes:

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

Dans Swift:

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

Voici le même UITextField sous-classé écrit dans Swift 3. Il est assez différent des versions précédentes de Swift, comme vous le verrez:

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

Par ailleurs, vous pouvez également faire quelque chose comme ce qui suit, si vous souhaitez contrôler l'encart d'un seul côté. Cet exemple particulier d'ajustement uniquement de l'encart de gauche est utile si vous placez une image au-dessus de l'UITextField mais que vous souhaitez qu'elle apparaisse à l'utilisateur comme étant dans le champ de texte:

    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
la source
4

Version 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
la source
Bizarrement, cela affecte les performances de défilement (sur iOS 12 au moins), lorsque le texte déborde du rect visible. Avec un encart de 15, il arrête même de défiler.
Ixx
3

Vous pouvez ajuster le positionnement du texte dans un champ de texte en en faisant une sous-classe UITextFieldet en remplaçant la -textRectForBounds:méthode.

Noah Witherspoon
la source
3

Il est absurde de devoir sous- UITextFieldclasser , car il met déjà en œuvre les méthodes, comme le souligne @Adam Waite. Voici une extension rapide qui expose une méthode d'usine, également disponible dans notre référentiel de catégories :

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
la source
Le lien dans votre réponse est mort, pouvez-vous le mettre à jour?
WendiKidd
Correction de l'URL @WendiKidd
Christopher Pickslay
2

J'ai sous-classé UITextField pour gérer cela qui prend également en charge l'encart gauche, haut, droit et bas et le positionnement des boutons clairs.

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

Exemple d'utilisation où * _someTextField * provient de la vue nib / storyboard avec la classe personnalisée MRDInsetTextField

[(MRDInsetTextField*)_someTextField setInset:CGRectMake(5, 0, 5, 0)]; // left, top, right, bottom inset
Firula
la source
Je vous remercie. Une suggestion à votre code cependant - pourquoi avez-vous utilisé CGRect pour l'encart et non UIEdgeInsets?
sha
2

Ce n'est pas aussi court que les autres exemples, mais adopte une approche entièrement différente pour résoudre ce problème. Notez que le curseur commence toujours à affleurer sur le bord gauche mais le texte sera correctement mis en retrait lors de la frappe / affichage. Cela fonctionne sans sous-classement si vous recherchez juste une marge gauche et que vous utilisez déjàUITextFieldDelegate vos champs de texte. Vous devez définir à la fois les attributs de texte par défaut et les attributs de frappe. Vous définissez les attributs de texte par défaut lorsque vous créez le champ de texte. Les attributs de frappe que vous devez définir dans le délégué. Si vous utilisez également un espace réservé, vous souhaiterez également le définir sur la même marge. En résumé, vous obtenez quelque chose comme ça.

Créez d'abord une catégorie sur la UITextFieldclasse.

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

Ensuite, si vous utilisez des supports placés, assurez-vous d'utiliser un espace réservé attribué définissant le même retrait. Créez un dictionnaire attribué par défaut avec les attributs appropriés, quelque chose comme ceci:

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

Ensuite, importez la catégorie ci-dessus et chaque fois que vous créez un champ de texte, définissez le retrait par défaut, le délégué et utilisez les attributs d'espace réservé par défaut définis ci-dessus. Par exemple:

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

Enfin, dans le délégué, implémentez la textFieldDidBeginEditingméthode, quelque chose comme ceci:

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    textField.indent = 7;
}
user3878720
la source
L'hypothèse qui defaultTextAttributescontient NSMutableParagraphStyleest assez dangereuse. Je préfère mutable Copiez tout cela.
Ben Sinclair
1

J'essaye normalement d'éviter le sous-classement, mais cela fonctionne si vous avez déjà:

// 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
la source
Une raison pour laquelle vous évitez le sous-classement? Il s'agit d' un paradigme de conception valide.
Stunner
1

Pour ajouter une autre solution qui n'a pas besoin de sous-classement:

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

UITextField d'origine UITextField fixe

Testé avec iOS 7 et iOS 8. Les deux fonctionnent. Il y a peut-être encore la possibilité qu'Apple modifie la hiérarchie des couches d'UITextField en vissant les choses.

TheGrumpyCoda
la source
1

Voici une réponse Swift complète qui comprend une leftView (icône personnalisée) et un bouton d'effacement personnalisé, tous deux définis dans Interface Builder avec des encarts personnalisables.

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
la source
1

Une solution qui fonctionne réellement et couvre tous les cas:

  • Devrait utiliser offsetBypasinsetBy .
  • Devrait également appeler la super fonction pour obtenir l'original Rect .
  • Bounds est défectueux. vous devez décaler les X, Y d'origine. Les limites ont X, Y comme zéros.
  • Les x, y d'origine peuvent être différents de zéro par exemple lors de la définition de la vue de gauche de UITextField.

Échantillon:

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
la source
0

Si vous souhaitez uniquement modifier le retrait TOP et LEFT

// position d'espace réservé

- (CGRect)textRectForBounds:(CGRect)bounds {

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

// position du texte

- (CGRect)editingRectForBounds:(CGRect)bounds {

CGRect frame = bounds;
frame.origin.y = 3;
 frame.origin.x = 5;
bounds = frame;
return CGRectInset( bounds , 0 , 0 );
}
Mann
la source
-1

Solution rapide sans sous-classe et également inspectable

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