754 Stimmen

Platzhalter in UITextView

Meine Anwendung verwendet ein UITextView . Jetzt möchte ich die UITextView einen Platzhalter zu haben, der dem Platzhalter ähnelt, den Sie für eine UITextField .

Wie kann man das tun?

0 Stimmen

Der TTTextEditor von Three20 (der seinerseits UITextField verwendet) unterstützt sowohl Platzhaltertext als auch das Wachsen nach Höhe (er wird zu einem UITextView).

0 Stimmen

24 Stimmen

Wie wäre es mit der Kategorie UITextView+Placeholder? github.com/devxoul/UITextView-Platzhalter

676voto

Jason George Punkte 6982

Ich habe ein paar kleinere Änderungen an der Lösung von bcd vorgenommen, um die Initialisierung von einer Xib Datei, den Textumbruch und die Beibehaltung der Hintergrundfarbe. Hoffentlich erspart das anderen die Mühe.

UIPlaceHolderTextView.h:

#import <Foundation/Foundation.h>
IB_DESIGNABLE
@interface UIPlaceHolderTextView : UITextView

@property (nonatomic, retain) IBInspectable NSString *placeholder;
@property (nonatomic, retain) IBInspectable UIColor *placeholderColor;

-(void)textChanged:(NSNotification*)notification;

@end

UIPlaceHolderTextView.m:

#import "UIPlaceHolderTextView.h"

@interface UIPlaceHolderTextView ()

@property (nonatomic, retain) UILabel *placeHolderLabel;

@end

@implementation UIPlaceHolderTextView

CGFloat const UI_PLACEHOLDER_TEXT_CHANGED_ANIMATION_DURATION = 0.25;

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
#if __has_feature(objc_arc)
#else
    [_placeHolderLabel release]; _placeHolderLabel = nil;
    [_placeholderColor release]; _placeholderColor = nil;
    [_placeholder release]; _placeholder = nil;
    [super dealloc];
#endif
}

- (void)awakeFromNib
{
    [super awakeFromNib];

    // Use Interface Builder User Defined Runtime Attributes to set
    // placeholder and placeholderColor in Interface Builder.
    if (!self.placeholder) {
        [self setPlaceholder:@""];
    }

    if (!self.placeholderColor) {
        [self setPlaceholderColor:[UIColor lightGrayColor]];
    }

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:nil];
}

- (id)initWithFrame:(CGRect)frame
{
    if( (self = [super initWithFrame:frame]) )
    {
        [self setPlaceholder:@""];
        [self setPlaceholderColor:[UIColor lightGrayColor]];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:nil];
    }
    return self;
}

- (void)textChanged:(NSNotification *)notification
{
    if([[self placeholder] length] == 0)
    {
        return;
    }

    [UIView animateWithDuration:UI_PLACEHOLDER_TEXT_CHANGED_ANIMATION_DURATION animations:^{
    if([[self text] length] == 0)
    {
        [[self viewWithTag:999] setAlpha:1];
    }
    else
    {
        [[self viewWithTag:999] setAlpha:0];
    }
    }];
}

- (void)setText:(NSString *)text {
    [super setText:text];
    [self textChanged:nil];
}

- (void)drawRect:(CGRect)rect
{
    if( [[self placeholder] length] > 0 )
    {
        if (_placeHolderLabel == nil )
        {
            _placeHolderLabel = [[UILabel alloc] initWithFrame:CGRectMake(8,8,self.bounds.size.width - 16,0)];
            _placeHolderLabel.lineBreakMode = NSLineBreakByWordWrapping;
            _placeHolderLabel.numberOfLines = 0;
            _placeHolderLabel.font = self.font;
            _placeHolderLabel.backgroundColor = [UIColor clearColor];
            _placeHolderLabel.textColor = self.placeholderColor;
            _placeHolderLabel.alpha = 0;
            _placeHolderLabel.tag = 999;
            [self addSubview:_placeHolderLabel];
        }

        _placeHolderLabel.text = self.placeholder;
        [_placeHolderLabel sizeToFit];
        [self sendSubviewToBack:_placeHolderLabel];
    }

    if( [[self text] length] == 0 && [[self placeholder] length] > 0 )
    {
        [[self viewWithTag:999] setAlpha:1];
    }

    [super drawRect:rect];
}

@end

0 Stimmen

Jedes Mal, wenn drawRect aufgerufen wird, wird ein neues UILabel zur Hierarchie der Ansicht hinzugefügt. Ich habe nicht viel um drawRect getan -- ist die Annahme, dass es keine Persistenz in der Ansicht heararchy, so dass jedes Mal drawRect aufgerufen wird die zuvor hinzugefügte Label-Unteransicht freigegeben wurde?

0 Stimmen

Insgesamt eine gute Implementierung, aber ich muss der Antwort von Sam Soffes zustimmen - das Hinzufügen eines Labels zur Ansichtshierarchie ist ziemlich schwer für das, was benötigt wird. Außerdem ist seine Implementierung von _updateShouldDrawPlaceholder und Verwendung von setNeedsDisplay ist gründlicher.

2 Stimmen

In einigen Fällen (insbesondere iOS 5-Kompatibilität) ist es erforderlich, paste zu überschreiben: - (void)paste:(id)sender { [super paste:sender]; [self textChanged:nil]; }

642voto

CmKndy Punkte 1341

Einfacher Weg, erstellen Sie einfach Platzhaltertext in UITextView durch die Verwendung der folgenden UITextViewDelegate Methoden:

- (void)textViewDidBeginEditing:(UITextView *)textView
{
    if ([textView.text isEqualToString:@"placeholder text here..."]) {
         textView.text = @"";
         textView.textColor = [UIColor blackColor]; //optional
    }
    [textView becomeFirstResponder];
}

- (void)textViewDidEndEditing:(UITextView *)textView
{
    if ([textView.text isEqualToString:@""]) {
        textView.text = @"placeholder text here...";
        textView.textColor = [UIColor lightGrayColor]; //optional
    }
    [textView resignFirstResponder];
}

vergessen Sie nicht, die myUITextView mit dem genauen Text bei der Erstellung z.B.

UITextView *myUITextView = [[UITextView alloc] init];
myUITextView.delegate = self;
myUITextView.text = @"placeholder text here...";
myUITextView.textColor = [UIColor lightGrayColor]; //optional

und die übergeordnete Klasse zu einer UITextViewDelegate bevor diese Methoden einbezogen werden, z. B.

@interface MyClass () <UITextViewDelegate>
@end

Code für Swift 3.1

func textViewDidBeginEditing(_ textView: UITextView) 
{
    if (textView.text == "placeholder text here..." && textView.textColor == .lightGray)
    {
        textView.text = ""
        textView.textColor = .black
    }
    textView.becomeFirstResponder() //Optional
}

func textViewDidEndEditing(_ textView: UITextView)
{
    if (textView.text == "")
    {
        textView.text = "placeholder text here..."
        textView.textColor = .lightGray
    }
    textView.resignFirstResponder()
}

vergessen Sie nicht, die myUITextView mit dem genauen Text bei der Erstellung z.B.

 let myUITextView = UITextView.init()
 myUITextView.delegate = self
 myUITextView.text = "placeholder text here..."
 myUITextView.textColor = .lightGray

und die übergeordnete Klasse zu einer UITextViewDelegate bevor diese Methoden einbezogen werden, z. B.

class MyClass: UITextViewDelegate
{

}

1 Stimmen

Dies ist großartig (ich liebe einfach) für 1 Bildschirm mit 1 UITextView. Der Grund für die komplizierteren Lösungen ist, dass, wenn Sie eine größere App mit VIEL Bildschirmen und VIEL UITextViews haben, Sie dies nicht immer wieder tun wollen. Wahrscheinlich möchten Sie UITextView subclass, um Ihre Bedürfnisse passen und dann verwenden, dass.

43 Stimmen

Wenn jemand "Platzhaltertext hier..." in das Textfeld eintippt, verhält er sich auch wie Platzhaltertext. Außerdem müssen Sie bei der Übermittlung alle diese Kriterien überprüfen.

1 Stimmen

Stimme mit @jklp überein, diese hochgestimmten übertechnisierten Methoden sind nicht so schön wie diese.

119voto

Sam Soffes Punkte 14520

Ich war mit keiner der geposteten Lösungen zufrieden, da sie ein wenig schwerfällig waren. Das Hinzufügen von Ansichten zur Ansicht ist nicht wirklich ideal (besonders in drawRect: ). Sie hatten beide undichte Stellen, was ebenfalls nicht akzeptabel ist.

Hier ist meine Lösung: SAMTextView

SAMTextView.h

//
//  SAMTextView.h
//  SAMTextView
//
//  Created by Sam Soffes on 8/18/10.
//  Copyright 2010-2013 Sam Soffes. All rights reserved.
//

#import <UIKit/UIKit.h>

/**
 UITextView subclass that adds placeholder support like UITextField has.
 */
@interface SAMTextView : UITextView

/**
 The string that is displayed when there is no other text in the text view.

 The default value is `nil`.
 */
@property (nonatomic, strong) NSString *placeholder;

/**
 The color of the placeholder.

 The default is `[UIColor lightGrayColor]`.
 */
@property (nonatomic, strong) UIColor *placeholderTextColor;

/**
 Returns the drawing rectangle for the text views’s placeholder text.

 @param bounds The bounding rectangle of the receiver.
 @return The computed drawing rectangle for the placeholder text.
 */
- (CGRect)placeholderRectForBounds:(CGRect)bounds;

@end

SAMTextView.m

//
//  SAMTextView.m
//  SAMTextView
//
//  Created by Sam Soffes on 8/18/10.
//  Copyright 2010-2013 Sam Soffes. All rights reserved.
//

#import "SAMTextView.h"

@implementation SAMTextView

#pragma mark - Accessors

@synthesize placeholder = _placeholder;
@synthesize placeholderTextColor = _placeholderTextColor;

- (void)setText:(NSString *)string {
  [super setText:string];
  [self setNeedsDisplay];
}

- (void)insertText:(NSString *)string {
  [super insertText:string];
  [self setNeedsDisplay];
}

- (void)setAttributedText:(NSAttributedString *)attributedText {
  [super setAttributedText:attributedText];
  [self setNeedsDisplay];
}

- (void)setPlaceholder:(NSString *)string {
  if ([string isEqual:_placeholder]) {
    return;
  }

  _placeholder = string;
  [self setNeedsDisplay];
}

- (void)setContentInset:(UIEdgeInsets)contentInset {
  [super setContentInset:contentInset];
  [self setNeedsDisplay];
}

- (void)setFont:(UIFont *)font {
  [super setFont:font];
  [self setNeedsDisplay];
}

- (void)setTextAlignment:(NSTextAlignment)textAlignment {
  [super setTextAlignment:textAlignment];
  [self setNeedsDisplay];
}

#pragma mark - NSObject

- (void)dealloc {
  [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextViewTextDidChangeNotification object:self];
}

#pragma mark - UIView

- (id)initWithCoder:(NSCoder *)aDecoder {
  if ((self = [super initWithCoder:aDecoder])) {
    [self initialize];
  }
  return self;
}

- (id)initWithFrame:(CGRect)frame {
  if ((self = [super initWithFrame:frame])) {
    [self initialize];
  }
  return self;
}

- (void)drawRect:(CGRect)rect {
  [super drawRect:rect];

  if (self.text.length == 0 && self.placeholder) {
    rect = [self placeholderRectForBounds:self.bounds];

    UIFont *font = self.font ? self.font : self.typingAttributes[NSFontAttributeName];

    // Draw the text
    [self.placeholderTextColor set];
    [self.placeholder drawInRect:rect withFont:font lineBreakMode:NSLineBreakByTruncatingTail alignment:self.textAlignment];
  }
}

#pragma mark - Placeholder

- (CGRect)placeholderRectForBounds:(CGRect)bounds {
  // Inset the rect
  CGRect rect = UIEdgeInsetsInsetRect(bounds, self.contentInset);

  if (self.typingAttributes) {
    NSParagraphStyle *style = self.typingAttributes[NSParagraphStyleAttributeName];
    if (style) {
      rect.origin.x += style.headIndent;
      rect.origin.y += style.firstLineHeadIndent;
    }
  }

  return rect;
}

#pragma mark - Private

- (void)initialize {
  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:self];

  self.placeholderTextColor = [UIColor colorWithWhite:0.702f alpha:1.0f];
}

- (void)textChanged:(NSNotification *)notification {
  [self setNeedsDisplay];
}

@end

Es ist viel einfacher als die anderen, da es keine Unteransichten verwendet (oder Lecks hat). Sie können es gerne verwenden.

Update 11/10/11: Sie ist jetzt dokumentiert und unterstützt die Verwendung im Interface Builder.

Update 24.11.13: Zeigen Sie auf das neue Repo.

0 Stimmen

Ihre Lösung gefällt mir, und ich habe einen Override von setText um den Platzhalter auch zu aktualisieren, wenn die Texteigenschaft programmatisch geändert wird: - (void)setText:(NSString *)string { [super setText:string]; [self _updateShouldDrawPlaceholder]; }

1 Stimmen

Ich mag deine Lösung auch, aber du hast die awakefromnib-Methode vergessen, so dass deine init-Methode nicht immer aufgerufen wird. Ich habe es von einem der anderen hier übernommen.

0 Stimmen

Hinweis: Die magischen Zahlen variieren je nach Schriftgröße. Die genaue Positionierung kann anhand der Schriftart berechnet werden, ist aber für diese Implementierung wahrscheinlich nicht sinnvoll. Die korrekte Positionierung des Platzhalters sollte 2px rechts von der Cursorposition sein, wenn kein Text vorhanden ist.

56voto

vdevos Punkte 491

Ich habe eine sehr einfache Möglichkeit gefunden, einen Platzhalter zu imitieren

  1. in der NIB oder im Code setzen Sie die textView's textColor auf lightGrayColor (meistens)
  2. stellen Sie sicher, dass der Delegat Ihrer textView mit dem Eigentümer der Datei verknüpft ist und implementieren Sie UITextViewDelegate in Ihrer Header-Datei
  3. Setzen Sie den Standardtext Ihrer Textansicht auf (Beispiel: "Foobar-Platzhalter")
  4. implementieren: (BOOL) textViewShouldBeginEditing:(UITextView *)textView

Editar:

Geänderte if-Anweisungen, um Tags statt Text zu vergleichen. Wenn der Benutzer seinen Text löschte, war es möglich, versehentlich auch einen Teil des Platzhalters zu löschen @"Foobar placeholder" Dies bedeutete, dass, wenn der Benutzer erneut in die textView eintrat, die folgende Delegatenmethode ausgeführt wurde, -(BOOL) textViewShouldBeginEditing:(UITextView *) textView funktioniert es nicht wie erwartet. Ich habe versucht, die Farbe des Textes in der if-Anweisung zu vergleichen, habe aber festgestellt, dass die im Interface Builder eingestellte hellgraue Farbe nicht dieselbe ist wie die hellgraue Farbe, die im Code mit [UIColor lightGreyColor]

- (BOOL) textViewShouldBeginEditing:(UITextView *)textView
{
    if(textView.tag == 0) {
        textView.text = @"";
        textView.textColor = [UIColor blackColor];
        textView.tag = 1;
    }
    return YES;
}

Es ist auch möglich, den Platzhaltertext zurückzusetzen, wenn die Tastatur zurückkehrt und die [textView length] == 0

EDITAR:

Nur um den letzten Teil zu verdeutlichen - hier ist, wie Sie den Platzhaltertext zurücksetzen können:

- (void)textViewDidChange:(UITextView *)textView
{
   if([textView.text length] == 0)
   {
       textView.text = @"Foobar placeholder";
       textView.textColor = [UIColor lightGrayColor];
       textView.tag = 0;
   }
}

15 Stimmen

Dieser Ansatz gefällt mir sehr gut! Das Einzige, was ich an der obigen Bearbeitung machen würde, wäre, die Implementierung aus der textViewDidChange:-Methode in die textViewDidEndEditing:-Methode zu verschieben, so dass der Platzhaltertext erst zurückkehrt, wenn die Arbeit mit dem Objekt beendet ist.

0 Stimmen

52voto

Tim Punkte 58529

Was Sie tun können, ist, die Textansicht mit einem Anfangswert in der text Eigenschaft, und ändern Sie die textColor a [UIColor grayColor] oder etwas Ähnliches. Wenn die Textansicht bearbeitbar wird, löschen Sie den Text und zeigen Sie einen Cursor an. Wenn das Textfeld wieder leer ist, setzen Sie den Platzhaltertext wieder ein. Ändern Sie die Farbe in [UIColor blackColor] nach Bedarf.

Es ist nicht genau dasselbe wie die Platzhalterfunktionalität in einem UITextField, aber es ist nahe dran.

9 Stimmen

Ich habe immer lightGrayColor was der Farbe des Platzhaltertextes zu entsprechen scheint.

0 Stimmen

Ich lese das jetzt erst, aber ich möchte hinzufügen, dass das Zurücksetzen der Farbe auf Schwarz und das Zurücksetzen der Texteigenschaft in textViewShouldBeginEditing:(UITextView *)textView sehr gut funktioniert. Dies ist eine sehr schöne und schnelle Lösung im Vergleich zu den Lösungen unten (aber immer noch elegante Lösungen unten, Subclassing uitextview. Viel modularer).

3 Stimmen

Stimmt, aber es ahmt nicht das Verhalten von UITextField nach, das seinen Platzhaltertext nur ersetzt, wenn der Benutzer etwas eintippt, nicht wenn die Bearbeitung beginnt, und das den Platzhalter wieder hinzufügt, sobald die Ansicht leer ist, nicht wenn die Bearbeitung tatsächlich beendet ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X