Platzhalter in UITextView


717

Meine Anwendung verwendet eine UITextView. Jetzt möchte ich, dass der UITextViewPlatzhalter ähnlich dem ist, den Sie für einen festlegen können UITextField.

Wie macht man das?


Der TTTextEditor von Three20 (der selbst UITextField verwendet) unterstützt Platzhaltertext und wächst mit der Höhe (er wird zu einer UITextView).
Josh Benjamin


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

2
Ich bevorzuge die Lösung von @ devxoul, da sie eine Kategorie und keine Unterklasse verwendet. Außerdem wird im Inspektor von IB ein Feld für Platzhalteroptionen (Platzhaltertext und Textfarbe) erstellt. Es werden einige Bindungstechniken verwendet. Was für ein großartiger Code
samthui7

Wenn Sie UITextViewdie Lösung verwenden, wird sie ganz anders. Hier sind einige Lösungen. Floating Placeholder und Fake Native Placeholders
klares Licht

Antworten:


672

Ich habe ein paar kleinere Änderungen an der Lösung von bcd vorgenommen, um die Initialisierung aus einer XibDatei und das Umbrechen von Text zu ermöglichen und die Hintergrundfarbe beizubehalten. Hoffentlich erspart es 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

2
In einigen Fällen (insbesondere bei iOS 5-Kompatibilität) muss das Einfügen überschrieben werden: - (void) paste: (id) sender {[super paste: sender]; [self textChanged: nil]; }
Martin Ullrich

3
Gutes Zeug! Zur Erinnerung an Best Practices für NSString (oder eine Klasse mit einem NSMutableXXX-Äquivalent) sollte die Eigenschaft "copy" und nicht "beibehalten" sein.
Oli

2
Wie instanziieren Sie diesen Code? Ich sehe keinen Platzhaltertext und nichts wird klar, wenn ich anfange zu tippen.
user798719

40
Dies ist eine sehr, sehr schlecht geschriebene Implementierung. Hier ist eine weitaus sauberere Version, die auch auf Änderungen des Diktats
achtet

10
Ändern Sie die Ansichtshierarchie in drawRect nicht.
Karmeye

634

Erstellen Sie auf einfache Weise Platzhaltertext UITextViewmit den folgenden UITextViewDelegateMethoden:

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

Denken Sie daran, myUITextViewden genauen Text für die Erstellung festzulegen, z

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

und machen Sie die übergeordnete Klasse zu einem, UITextViewDelegatebevor Sie diese Methoden einschließen, z

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

Denken Sie daran, myUITextViewden genauen Text für die Erstellung festzulegen, z

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

und machen Sie die übergeordnete Klasse zu einem, UITextViewDelegatebevor Sie diese Methoden einschließen, z

class MyClass: UITextViewDelegate
{

}

1
Dies ist großartig (ich liebe einfach) für 1 Bildschirm mit 1 UITextView. Der Grund für die komplizierteren Lösungen ist, dass Sie dies nicht immer wieder tun möchten, wenn Sie eine größere App mit vielen Bildschirmen und vielen UITextViews haben. Sie möchten wahrscheinlich UITextView in eine Unterklasse unterteilen, die Ihren Anforderungen entspricht, und diese dann verwenden.
Ghostatron

42
Wenn jemand "Platzhaltertext hier ..." in das Textfeld eingibt, verhält es sich auch wie Platzhaltertext. Außerdem müssen Sie während der Einreichung alle diese Kriterien überprüfen.
Anindya Sengupta

7
Der Platzhaltertext soll auch dann angezeigt werden, wenn das Feld zum Responder wird, und diese Methode funktioniert hierfür nicht.
Phatmann

17
@jklp Ich würde argumentieren, dass der "überentwickelte" Weg sauberer und wiederverwendbarer ist ... und es sieht so aus, als würde er das textAttribut der Textansicht nicht manipulieren, was irgendwie nett ist ... obwohl diese Methode es modifiziert
Cameron Askew

2
Warum werden die Aufrufe in den Delegate-Methoden "FirstResponder" und "ResignFirstResponder"?
Adam Johns

119

Ich war mit keiner der veröffentlichten Lösungen zufrieden, da sie etwas schwer waren. Das Hinzufügen von Ansichten zur Ansicht ist nicht wirklich ideal (insbesondere in drawRect:). Sie hatten beide Lecks, was auch 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 aufweist). Fühlen Sie sich frei, es zu benutzen.

Update 10.11.11: Es ist jetzt dokumentiert und unterstützt die Verwendung in Interface Builder.

Update 24.11.13: Zeigen Sie auf neues Repo.


Ich mag Ihre Lösung und habe eine Überschreibung von hinzugefügt, setTextum auch den Platzhalter zu aktualisieren, wenn die Texteigenschaft programmgesteuert geändert wird: - (void) setText: (NSString *) string {[super setText: string]; [self _updateShouldDrawPlaceholder]; }
Olegueret

1
Ich mag Ihre Lösung auch, aber Sie haben die awakefromnib-Methode verpasst, sodass Ihre init-Methode nicht immer aufgerufen wird. Ich habe es von einem der anderen hier genommen.
Toxaq

Hinweis - Die magischen Zahlen variieren je nach Schriftgröße. Die genaue Positionierung kann aus der Schriftart berechnet werden, lohnt sich aber für diese Implementierung wahrscheinlich nicht. Die korrekte Position des Platzhalters sollte 2 Pixel rechts von der Cursorposition sein, wenn kein Text vorhanden ist.
Memmons

So lösen Sie das Laden von nib: Sie möchten wahrscheinlich einen - (id) initWithCoder hinzufügen: (NSCoder *) aDecoder; Initialisierer zur Begleitung des vorhandenen.
Joris Kluivers

Das ist süß. Das notificationArgument ist jedoch in der letzten Methode falsch geschrieben, und es ist eine zu kleine Änderung, um sie als Bearbeitung einzureichen.
Phil Calvin

53

Ich fand einen sehr einfachen Weg, einen Platzhalter nachzuahmen

  1. Setzen Sie in der NIB oder im Code die textColor Ihrer textView 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 Ihre Header-Datei
  3. Setzen Sie den Standardtext Ihrer Textansicht auf (Beispiel: "Foobar-Platzhalter")
  4. implementieren: (BOOL) textViewShouldBeginEditing: (UITextView *) textView

Bearbeiten:

Die if-Anweisungen wurden geändert, um Tags anstelle von Text zu vergleichen. Wenn der Benutzer seinen Text löschte, war es auch möglich, versehentlich einen Teil des @"Foobar placeholder"Platzhalters zu löschen. Dies bedeutete, dass der Benutzer -(BOOL) textViewShouldBeginEditing:(UITextView *) textViewnicht wie erwartet funktionieren würde, wenn der Benutzer die folgende Delegate-Methode erneut eingeben würde. Ich habe versucht, anhand der Farbe des Textes in der if-Anweisung zu vergleichen, aber festgestellt, dass die im Interface Builder festgelegte hellgraue Farbe nicht mit der im Code mit festgelegten hellgrauen Farbe übereinstimmt[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-Länge] == 0 ist

BEARBEITEN:

Um den letzten Teil klarer zu machen, können Sie den Platzhaltertext folgendermaßen zurücksetzen:

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

12
Ich mag diesen Ansatz sehr! Das einzige, was ich an der obigen Bearbeitung tun würde, wäre, die Implementierung aus der textViewDidChange: -Methode in die textViewDidEndEditing: -Methode zu verschieben, sodass der Platzhaltertext erst zurückgegeben wird, wenn Sie mit dem Objekt fertig sind.
Hufeisen7

52

Sie können die Textansicht mit einem Anfangswert in der textEigenschaft einrichten und den textColorin [UIColor grayColor]oder etwas Ähnliches ändern . Wenn die Textansicht bearbeitet werden kann, löschen Sie den Text und zeigen Sie einen Cursor an. Wenn das Textfeld jemals wieder leer ist, setzen Sie Ihren Platzhaltertext zurück. Ändern Sie die Farbe [UIColor blackColor]entsprechend.

Es ist nicht genau das Gleiche wie die Platzhalterfunktionalität in einem UITextField, aber es ist nah.


9
Ich habe immer lightGrayColor verwendet , das der Farbe des Platzhaltertextes zu entsprechen scheint.
Bill

Ich lese das gerade, 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 folgenden Lösungen (aber immer noch elegante Lösungen unten, Unterklasse uitextview. Viel modularer).
Enrico Susatyo

3
Richtig, aber es ahmt nicht das Verhalten von UITextField nach, das seinen Platzhaltertext nur ersetzt, wenn der Benutzer etwas eingibt, nicht zu Beginn der Bearbeitung, und der den Platzhalter wieder hinzufügt, sobald die Ansicht leer ist und nicht, wenn die Bearbeitung tatsächlich abgeschlossen ist.
Asche

47

Sie können die Beschriftung auf dem UITextViewby einstellen

[UITextView addSubView:lblPlaceHoldaer];

und verstecke es auf TextViewdidChangeMethode.

Dies ist der einfache und einfache Weg.


45

Wenn jemand eine Lösung für Swift benötigt:

Fügen Sie Ihrer Klasse UITextViewDelegate hinzu

var placeHolderText = "Placeholder Text..."

override func viewDidLoad() {
    super.viewDidLoad()
    textView.delegate = self
}

func textViewShouldBeginEditing(textView: UITextView) -> Bool {

    self.textView.textColor = .black

    if(self.textView.text == placeHolderText) {
        self.textView.text = ""
    }

    return true
}

func textViewDidEndEditing(textView: UITextView) {
    if(textView.text == "") {
        self.textView.text = placeHolderText
        self.textView.textColor = .lightGray
    }
}

override func viewWillAppear(animated: Bool) {

    if(currentQuestion.answerDisplayValue == "") {
        self.textView.text = placeHolderText
        self.textView.textColor = .lightGray
    } else {
        self.textView.text = "xxx" // load default text / or stored 
        self.textView.textColor = .black
    }
}

Das ist in Ordnung, aber nicht gut genug. Wenn der Benutzer "Platzhaltertext ..." eingibt (Randfall offensichtlich), bricht dies Ihre Logik
Lucas Chwe

45

Einfache Swift 3- Lösung

Fügen Sie UITextViewDelegateIhrer Klasse hinzu

einstellen yourTextView.delegate = self

Erstellen placeholderLabelund positionieren Sie es im InnerenyourTextView

Jetzt nur noch animieren placeholderLabel.alphaauf textViewDidChange:

  func textViewDidChange(_ textView: UITextView) {
    let newAlpha: CGFloat = textView.text.isEmpty ? 1 : 0
    if placeholderLabel.alpha != newAlpha {
      UIView.animate(withDuration: 0.3) {
        self.placeholderLabel.alpha = newAlpha
      }
    }
  }

Möglicherweise müssen Sie mit der placeholderLabelPosition spielen, um sie richtig einzurichten, aber das sollte nicht zu schwer sein


1
Tolle Antwort, einfache Lösung. Ich habe eine kleine Verbesserung hinzugefügt, um nur dann zu animieren, wenn sich das Alpha ändern sollte: let alpha = CGFloat(textView.text.isEmpty ? 1.0 : 0.0) if alpha != lblPlaceholder.alpha { UIView.animate(withDuration: 0.3) { self.lblPlaceholder.alpha = alpha } }
Luciano Sclovsky

24

Ich habe die Antwort von KmKndy erweitert, sodass der Platzhalter sichtbar bleibt, bis der Benutzer mit der Bearbeitung beginnt, UITextViewanstatt nur darauf zu tippen. Dies spiegelt die Funktionalität in den Twitter- und Facebook-Apps wider. Meine Lösung erfordert keine Unterklasse und funktioniert, wenn der Benutzer Text direkt eingibt oder einfügt!

Beispiel für einen Platzhalter Twitter App

- (void)textViewDidChangeSelection:(UITextView *)textView{
    if ([textView.text isEqualToString:@"What's happening?"] && [textView.textColor isEqual:[UIColor lightGrayColor]])[textView setSelectedRange:NSMakeRange(0, 0)];

}

- (void)textViewDidBeginEditing:(UITextView *)textView{

    [textView setSelectedRange:NSMakeRange(0, 0)];
}

- (void)textViewDidChange:(UITextView *)textView
{
    if (textView.text.length != 0 && [[textView.text substringFromIndex:1] isEqualToString:@"What's happening?"] && [textView.textColor isEqual:[UIColor lightGrayColor]]){
        textView.text = [textView.text substringToIndex:1];
        textView.textColor = [UIColor blackColor]; //optional

    }
    else if(textView.text.length == 0){
        textView.text = @"What's happening?";
        textView.textColor = [UIColor lightGrayColor];
        [textView setSelectedRange:NSMakeRange(0, 0)];
    }
}

- (void)textViewDidEndEditing:(UITextView *)textView
{
    if ([textView.text isEqualToString:@""]) {
        textView.text = @"What's happening?";
        textView.textColor = [UIColor lightGrayColor]; //optional
    }
    [textView resignFirstResponder];
}

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
    if (textView.text.length > 1 && [textView.text isEqualToString:@"What's happening?"]) {
         textView.text = @"";
         textView.textColor = [UIColor blackColor];
    }

    return YES;
}

Denken Sie daran, myUITextView mit dem genauen Text für die Erstellung festzulegen, z

UITextView *myUITextView = [[UITextView alloc] init];
myUITextView.delegate = self;
myUITextView.text = @"What's happening?";
myUITextView.textColor = [UIColor lightGrayColor]; //optional

und machen Sie die übergeordnete Klasse zu einem UITextView-Delegaten, bevor Sie diese Methoden einschließen, z

@interface MyClass () <UITextViewDelegate>
@end

20

Ich empfehle zu verwenden SZTextView.

https://github.com/glaszig/SZTextView

Fügen Sie Ihre Standardeinstellung UITextViewvon hinzu storyboardund ändern Sie die benutzerdefinierte Klasse SZTextViewwie folgt 👇👇👇👇

Geben Sie hier die Bildbeschreibung ein

Dann sehen Sie zwei neue Optionen im Attribute Inspector👇👇👇👇

Geben Sie hier die Bildbeschreibung ein


20

Unten finden Sie eine Swift-Portierung des ObjC-Codes "SAMTextView", die als eine der ersten paar Antworten auf die Frage veröffentlicht wurde. Ich habe es unter iOS 8 getestet. Ich habe einige Dinge optimiert, einschließlich des Grenzversatzes für die Platzierung des Platzhaltertextes, da das Original zu hoch und zu weit rechts war (in einem der Kommentare zu diesem Beitrag wurde ein Vorschlag verwendet).

Ich weiß, dass es viele einfache Lösungen gibt, aber ich mag den Ansatz der Unterklasse von UITextView, weil es wiederverwendbar ist und ich keine Klassen durcheinander bringen muss, die es mit den Mechanismen verwenden.

Swift 2.2:

import UIKit

class PlaceholderTextView: UITextView {

    @IBInspectable var placeholderColor: UIColor = UIColor.lightGrayColor()
    @IBInspectable var placeholderText: String = ""

    override var font: UIFont? {
        didSet {
            setNeedsDisplay()
        }
    }

    override var contentInset: UIEdgeInsets {
        didSet {
            setNeedsDisplay()
        }
    }

    override var textAlignment: NSTextAlignment {
        didSet {
            setNeedsDisplay()
        }
    }

    override var text: String? {
        didSet {
            setNeedsDisplay()
        }
    }

    override var attributedText: NSAttributedString? {
        didSet {
            setNeedsDisplay()
        }
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setUp()
    }

    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
    }

    private func setUp() {
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(PlaceholderTextView.textChanged(_:)),
                                                         name: UITextViewTextDidChangeNotification, object: self)
    }

    func textChanged(notification: NSNotification) {
        setNeedsDisplay()
    }

    func placeholderRectForBounds(bounds: CGRect) -> CGRect {
        var x = contentInset.left + 4.0
        var y = contentInset.top  + 9.0
        let w = frame.size.width - contentInset.left - contentInset.right - 16.0
        let h = frame.size.height - contentInset.top - contentInset.bottom - 16.0

        if let style = self.typingAttributes[NSParagraphStyleAttributeName] as? NSParagraphStyle {
            x += style.headIndent
            y += style.firstLineHeadIndent
        }
        return CGRect(x: x, y: y, width: w, height: h)
    }

    override func drawRect(rect: CGRect) {
        if text!.isEmpty && !placeholderText.isEmpty {
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.alignment = textAlignment
            let attributes: [ String: AnyObject ] = [
                NSFontAttributeName : font!,
                NSForegroundColorAttributeName : placeholderColor,
                NSParagraphStyleAttributeName  : paragraphStyle]

            placeholderText.drawInRect(placeholderRectForBounds(bounds), withAttributes: attributes)
        }
        super.drawRect(rect)
    }
}

Swift 4.2:

import UIKit

class PlaceholderTextView: UITextView {

    @IBInspectable var placeholderColor: UIColor = UIColor.lightGray
    @IBInspectable var placeholderText: String = ""

    override var font: UIFont? {
        didSet {
            setNeedsDisplay()
        }
    }

    override var contentInset: UIEdgeInsets {
        didSet {
            setNeedsDisplay()
        }
    }

    override var textAlignment: NSTextAlignment {
        didSet {
            setNeedsDisplay()
        }
    }

    override var text: String? {
        didSet {
            setNeedsDisplay()
        }
    }

    override var attributedText: NSAttributedString? {
        didSet {
            setNeedsDisplay()
        }
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setUp()
    }

    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
    }

    private func setUp() {
        NotificationCenter.default.addObserver(self,
         selector: #selector(self.textChanged(notification:)),
         name: Notification.Name("UITextViewTextDidChangeNotification"),
         object: nil)
    }

    @objc func textChanged(notification: NSNotification) {
        setNeedsDisplay()
    }

    func placeholderRectForBounds(bounds: CGRect) -> CGRect {
        var x = contentInset.left + 4.0
        var y = contentInset.top  + 9.0
        let w = frame.size.width - contentInset.left - contentInset.right - 16.0
        let h = frame.size.height - contentInset.top - contentInset.bottom - 16.0

        if let style = self.typingAttributes[NSAttributedString.Key.paragraphStyle] as? NSParagraphStyle {
            x += style.headIndent
            y += style.firstLineHeadIndent
        }
        return CGRect(x: x, y: y, width: w, height: h)
    }

    override func draw(_ rect: CGRect) {
        if text!.isEmpty && !placeholderText.isEmpty {
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.alignment = textAlignment
            let attributes: [NSAttributedString.Key: Any] = [
            NSAttributedString.Key(rawValue: NSAttributedString.Key.font.rawValue) : font!,
            NSAttributedString.Key(rawValue: NSAttributedString.Key.foregroundColor.rawValue) : placeholderColor,
            NSAttributedString.Key(rawValue: NSAttributedString.Key.paragraphStyle.rawValue)  : paragraphStyle]

            placeholderText.draw(in: placeholderRectForBounds(bounds: bounds), withAttributes: attributes)
        }
        super.draw(rect)
    }
}

Können Sie erklären, wozu eine awakeFromNib-Methode gut ist, wenn Sie die schnelle Version ausführen?
Pierre

Der Platzhalter wird festgelegt, aber nicht aktualisiert, sobald Sie mit der Eingabe beginnen.
David

Trotzdem habe ich den Benachrichtigungsaufruf in awakeFromNib eingefügt und er funktioniert jetzt.
David

12

so habe ich es gemacht:

UITextView2.h

#import <UIKit/UIKit.h>

@interface UITextView2 : UITextView <UITextViewDelegate> {
 NSString *placeholder;
 UIColor *placeholderColor;
}

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

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

@end

UITextView2.m

@implementation UITextView2

@synthesize placeholder, placeholderColor;

- (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*)notif {
    if ([[self placeholder] length]==0)
        return;
    if ([[self text] length]==0) {
        [[self viewWithTag:999] setAlpha:1];
    } else {
        [[self viewWithTag:999] setAlpha:0];
    }

}

- (void)drawRect:(CGRect)rect {
    if ([[self placeholder] length]>0) {
        UILabel *l = [[UILabel alloc] initWithFrame:CGRectMake(8, 8, 0, 0)];
        [l setFont:self.font];
        [l setTextColor:self.placeholderColor];
        [l setText:self.placeholder];
        [l setAlpha:0];
        [l setTag:999];
        [self addSubview:l];
        [l sizeToFit];
        [self sendSubviewToBack:l];
        [l release];
    }
    if ([[self text] length]==0 && [[self placeholder] length]>0) {
        [[self viewWithTag:999] setAlpha:1];
    }
    [super drawRect:rect];
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [super dealloc];
}


@end

12

Hier ist eine viel einfachere Lösung, die sich genau wie der Platzhalter von UITextField verhält, jedoch keine benutzerdefinierten Ansichten zeichnen oder den Ersthelfer zurücktreten muss.

- (void) textViewDidChange:(UITextView *)textView{

    if (textView.text.length == 0){
        textView.textColor = [UIColor lightGrayColor];
        textView.text = placeholderText;
        [textView setSelectedRange:NSMakeRange(0, 0)];
        isPlaceholder = YES;

    } else if (isPlaceholder && ![textView.text isEqualToString:placeholderText]) {
        textView.text = [textView.text substringToIndex:1];
        textView.textColor = [UIColor blackColor];
        isPlaceholder = NO;
    }

}

(Die zweite Überprüfung in der else if-Anweisung gilt für den Fall, dass nichts eingegeben wird und der Benutzer die Rücktaste drückt.)

Legen Sie einfach Ihre Klasse als UITextViewDelegate fest. In viewDidLoad sollten Sie wie initialisieren

- (void) viewDidLoad{
    // initialize placeholder text
    placeholderText = @"some placeholder";
    isPlaceholder = YES;
    self.someTextView.text = placeholderText;
    self.someTextView.textColor = [UIColor lightGrayColor];
    [self.someTextView setSelectedRange:NSMakeRange(0, 0)];

    // assign UITextViewDelegate
    self.someTextView.delegate = self;
}

2
Wenn der Benutzer irgendwo in der Mitte des "Platzhaltertextes" tippt, bleibt das Caret dort.
Alex Sorokoletov

10

Hallo, Sie können IQTextView verwenden, das in IQKeyboard Manager verfügbar ist. Es ist einfach zu verwenden und zu integrieren. Legen Sie einfach die Klasse Ihrer Textansicht in IQTextView fest. Sie können die Eigenschaft zum Festlegen der Platzhalterbeschriftung mit der gewünschten Farbe verwenden. Sie können die Bibliothek von IQKeyboardManager herunterladen

oder Sie können es von Cocoapods installieren.


IQKeyboardManager ist sehr nützlich und codelos. Die beste Antwort für mich!
NSDeveloper

1
Eigentlich stimme ich zu und hinterlasse einen Kommentar aus dem Grund. Ich weiß nicht, dass IQTextView zuvor in IQKeyboardManager verfügbar war.
NSDeveloper

Ich hatte ein Problem mit dem Delegierten. Ich entfernte aus der Klasse die Delegatenüberschreibung und die UITextViewDelegatefunktionierte
einwandfrei

unterbewertete Antwort
grantespo

10

Es tut mir leid, eine weitere Antwort hinzuzufügen, aber ich habe gerade so etwas abgezogen und dadurch den Platzhalter erstellt, der UITextField am nächsten kommt.

Hoffe das hilft jemandem.

-(void)textViewDidChange:(UITextView *)textView{
    if(textView.textColor == [UIColor lightGrayColor]){
        textView.textColor  = [UIColor blackColor]; // look at the comment section in this answer
        textView.text       = [textView.text substringToIndex: 0];// look at the comment section in this answer
    }else if(textView.text.length == 0){
        textView.text       = @"This is some placeholder text.";
        textView.textColor  = [UIColor lightGrayColor];
        textView.selectedRange = NSMakeRange(0, 0);
    }
}

-(void)textViewDidChangeSelection:(UITextView *)textView{
    if(textView.textColor == [UIColor lightGrayColor] && (textView.selectedRange.location != 0 || textView.selectedRange.length != 0)){
        textView.selectedRange = NSMakeRange(0, 0);
    }
}

1
Ich musste die Reihenfolge der Befehle in der ersten if-Anweisung ändern. if(textView.textColor == [UIColor lightGrayColor]){ textView.textColor = [UIColor blackColor]; textView.text = [textView.text substringToIndex: 1]; Andernfalls wurde das erste Zeichen, das in die Textansicht eingegeben wurde, am Ende des Textes platziert
Flexicoder

7

Einfache Möglichkeit, dies in einer Codezeile zu verwenden:

Nehmen Sie ein Etikett bis zu UITextView in .nib und verbinden Sie dieses Etikett mit Ihrem Code.

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{

    if (range.location>0 || text.length!=0) {
        placeholderLabel1.hidden = YES;
    }else{
        placeholderLabel1.hidden = NO;
    }
    return YES;
}

7

Ich habe die Implementierung von Sam Soffes für iOS7 geändert:

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

    if (_shouldDrawPlaceholder)
    {
        UIEdgeInsets insets = self.textContainerInset;        
        CGRect placeholderRect = CGRectMake(
                insets.left + self.textContainer.lineFragmentPadding,
                insets.top,
                self.frame.size.width - insets.left - insets.right,
                self.frame.size.height - insets.top - insets.bottom);

        [_placeholderText drawWithRect:placeholderRect
                           options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine
                        attributes:self.placeholderAttributes
                           context:nil];
    }
}

- (NSDictionary *)placeholderAttributes
{
    if (_placeholderAttributes == nil)
    {
        _placeholderAttributes = @
        {
            NSFontAttributeName : self.font,
            NSForegroundColorAttributeName : self.placeholderColor
        };
    }

    return _placeholderAttributes;
}

Denken Sie daran, _placeholderAttribues = nilMethoden festzulegen, die die Schriftart ändern können, sowie andere Zeichen, die sie möglicherweise beeinflussen. Möglicherweise möchten Sie auch das "faule" Erstellen des Attributwörterbuchs überspringen, wenn Sie dies nicht stört.

BEARBEITEN:

Denken Sie daran, setNeedsDisplay in einer überschriebenen Version von setBounds aufzurufen, wenn der Platzhalter nach Autolayout-Animationen und Ähnlichem gut aussehen soll.


Ich denke, Sie sollten insets.left zum Parameter offset x hinzufügen.
Karmeye

ist es nicht setFrame statt setBounds?
JakubKnejzlik

Nee! setFrame wird anscheinend bei Layoutanimationen nicht aufgerufen.
Nagler

6

Sie können auch eine neue Klasse TextViewWithPlaceholder als Unterklasse von UITextView erstellen.

(Dieser Code ist etwas rau - aber ich denke, er ist auf dem richtigen Weg.)

@interface TextViewWithPlaceholder : UITextView
{

    NSString *placeholderText;  // make a property
    UIColor *placeholderColor;  // make a property
    UIColor *normalTextColor;   // cache text color here whenever you switch to the placeholderColor
}

- (void) setTextColor: (UIColor*) color
{
   normalTextColor = color;
   [super setTextColor: color];
}

- (void) updateForTextChange
{
    if ([self.text length] == 0)
    { 
        normalTextColor = self.textColor;
        self.textColor = placeholderColor;
        self.text = placeholderText;
    }
    else
    {
        self.textColor = normalTextColor;
    }

}

Fügen Sie in Ihrem Delegaten Folgendes hinzu:

- (void)textViewDidChange:(UITextView *)textView
{
    if ([textView respondsToSelector: @selector(updateForTextChange)])
    {
        [textView updateForTextChange];
    }

}

1
Um das genaue Verhalten zu erhalten, sollten Sie Ihren eigenen Platzhalter malen, indem Sie drawRect überschreiben: (Platzhalter nur zeichnen, wenn! [Self isFirstResponder] && [[self text] length] == 0) und setNeedsDisplay in WerdenFirstResponder und resignFirstResponder aufrufen
rpetrich

6

Ich habe meine eigene Version der Unterklasse von 'UITextView' erstellt. Ich mochte Sam Soffes 'Idee, die Benachrichtigungen zu verwenden, aber ich mochte das drawRect: overwrite nicht. Scheint mir übertrieben. Ich denke, ich habe eine sehr saubere Implementierung gemacht.

Sie können meine Unterklasse hier ansehen . Ein Demo-Projekt ist ebenfalls enthalten.


6

Dieser Thread hatte viele Antworten, aber hier ist die Version, die ich bevorzuge.

Es erweitert die vorhandene UITextViewKlasse, ist also leicht wiederverwendbar und es nicht abfangen , die Ereignisse wie textViewDidChange(die Benutzer-Code brechen könnten, wenn sie bereits diese Ereignisse an anderer Stelle abfängt wurden).

Mit meinem Code (siehe unten) können Sie jedem Ihrer Platzhalter ganz einfach einen Platzhalter hinzufügen UITextViews :

self.textViewComments.placeholder = @"(Enter some comments here.)";

Wenn Sie diesen neuen Platzhalterwert festlegen, wird stillschweigend ein Zusatz UILabelzu Ihrem hinzugefügtUITextView bei Bedarf ausgeblendet / angezeigt:

Geben Sie hier die Bildbeschreibung ein

Okay, um diese Änderungen vorzunehmen, fügen Sie eine "UITextViewHelper.h" -Datei hinzu, die diesen Code enthält:

//  UITextViewHelper.h
//  Created by Michael Gledhill on 13/02/15.

#import <Foundation/Foundation.h>

@interface UITextView (UITextViewHelper)

@property (nonatomic, strong) NSString* placeholder;
@property (nonatomic, strong) UILabel* placeholderLabel;
@property (nonatomic, strong) NSString* textValue;

-(void)checkIfNeedToDisplayPlaceholder;

@end

... und eine UITextViewHelper.m-Datei, die Folgendes enthält:

//  UITextViewHelper.m
//  Created by Michael Gledhill on 13/02/15.
//
//  This UITextView category allows us to easily display a PlaceHolder string in our UITextView.
//  The downside is that, your code needs to set the "textValue" rather than the "text" value to safely set the UITextView's text.
//
#import "UITextViewHelper.h"
#import <objc/runtime.h>

@implementation UITextView (UITextViewHelper)

#define UI_PLACEHOLDER_TEXT_COLOR [UIColor colorWithRed:170.0/255.0 green:170.0/255.0 blue:170.0/255.0 alpha:1.0]

@dynamic placeholder;
@dynamic placeholderLabel;
@dynamic textValue;

-(void)setTextValue:(NSString *)textValue
{
    //  Change the text of our UITextView, and check whether we need to display the placeholder.
    self.text = textValue;
    [self checkIfNeedToDisplayPlaceholder];
}
-(NSString*)textValue
{
    return self.text;
}

-(void)checkIfNeedToDisplayPlaceholder
{
    //  If our UITextView is empty, display our Placeholder label (if we have one)
    if (self.placeholderLabel == nil)
        return;

    self.placeholderLabel.hidden = (![self.text isEqualToString:@""]);
}

-(void)onTap
{
    //  When the user taps in our UITextView, we'll see if we need to remove the placeholder text.
    [self checkIfNeedToDisplayPlaceholder];

    //  Make the onscreen keyboard appear.
    [self becomeFirstResponder];
}

-(void)keyPressed:(NSNotification*)notification
{
    //  The user has just typed a character in our UITextView (or pressed the delete key).
    //  Do we need to display our Placeholder label ?
   [self checkIfNeedToDisplayPlaceholder];
}

#pragma mark - Add a "placeHolder" string to the UITextView class

NSString const *kKeyPlaceHolder = @"kKeyPlaceHolder";
-(void)setPlaceholder:(NSString *)_placeholder
{
    //  Sets our "placeholder" text string, creates a new UILabel to contain it, and modifies our UITextView to cope with
    //  showing/hiding the UILabel when needed.
    objc_setAssociatedObject(self, &kKeyPlaceHolder, (id)_placeholder, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    self.placeholderLabel = [[UILabel alloc] initWithFrame:self.frame];
    self.placeholderLabel.numberOfLines = 1;
    self.placeholderLabel.text = _placeholder;
    self.placeholderLabel.textColor = UI_PLACEHOLDER_TEXT_COLOR;
    self.placeholderLabel.backgroundColor = [UIColor clearColor];
    self.placeholderLabel.userInteractionEnabled = true;
    self.placeholderLabel.font = self.font;
    [self addSubview:self.placeholderLabel];

    [self.placeholderLabel sizeToFit];

    //  Whenever the user taps within the UITextView, we'll give the textview the focus, and hide the placeholder if necessary.
    [self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap)]];

    //  Whenever the user types something in the UITextView, we'll see if we need to hide/show the placeholder label.
    [[NSNotificationCenter defaultCenter] addObserver:self selector: @selector(keyPressed:) name:UITextViewTextDidChangeNotification object:nil];

    [self checkIfNeedToDisplayPlaceholder];
}
-(NSString*)placeholder
{
    //  Returns our "placeholder" text string
    return objc_getAssociatedObject(self, &kKeyPlaceHolder);
}

#pragma mark - Add a "UILabel" to this UITextView class

NSString const *kKeyLabel = @"kKeyLabel";
-(void)setPlaceholderLabel:(UILabel *)placeholderLabel
{
    //  Stores our new UILabel (which contains our placeholder string)
    objc_setAssociatedObject(self, &kKeyLabel, (id)placeholderLabel, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

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

    [self checkIfNeedToDisplayPlaceholder];
}
-(UILabel*)placeholderLabel
{
    //  Returns our new UILabel
    return objc_getAssociatedObject(self, &kKeyLabel);
}
@end

Ja, es ist viel Code, aber sobald Sie ihn Ihrem Projekt hinzugefügt und die .h-Datei hinzugefügt haben ...

#import "UITextViewHelper.h"

... können Sie problemlos Platzhalter in verwenden UITextViews.

Es gibt aber einen Fall.

Wenn du das tust:

self.textViewComments.placeholder = @"(Enter some comments here.)";
self.textViewComments.text = @"Ooooh, hello there";

... wird der Platzhalter über dem Text angezeigt. Wenn Sie den textWert festlegen , wird keine der regulären Benachrichtigungen aufgerufen, sodass ich nicht herausfinden konnte, wie ich meine Funktion aufrufen soll, um zu entscheiden, ob der Platzhalter ein- oder ausgeblendet werden soll.

Die Lösung besteht darin, textValueFolgendes festzulegen text:

self.textViewComments.placeholder = @"(Enter some comments here.)";
self.textViewComments.textValue = @"Ooooh, hello there";

Alternativ können Sie den textWert festlegen und dann aufrufen checkIfNeedToDisplayPlaceholder.

self.textViewComments.text = @"Ooooh, hello there";
[self.textViewComments checkIfNeedToDisplayPlaceholder];

Ich mag solche Lösungen, weil sie die Lücke zwischen dem, was Apple uns bietet, und dem, was wir (als Entwickler) tatsächlich brauchen , "füllen" in unseren Apps . Sie schreiben diesen Code einmal, fügen ihn Ihrer Bibliothek mit "helper" .m / .h-Dateien hinzu, und im Laufe der Zeit wird das SDK tatsächlich weniger frustrierend.

(Ich habe einen ähnlichen Helfer geschrieben, um meinen UITextViews einen "Löschen" -Button hinzuzufügen, eine andere Sache, die ärgerlich in, UITextFieldaber nicht in UITextView... existiert.)


Ich mag, wie sauber das ist, aber ich mag nicht, wie es ein zweites UIView / UILabel erfordert (und erbt die Attribute / Farben / Schriftarten nicht wirklich einfach von der UITextView). Netter Beitrag
Mattsven

6

Nehmen Sie zuerst ein Etikett in die .h-Datei.

Hier nehme ich

UILabel * lbl;

Dann deklarieren Sie es in .m unter viewDidLoad

lbl = [[UILabel alloc] initWithFrame:CGRectMake(8.0, 0.0,250, 34.0)];

lbl.font=[UIFont systemFontOfSize:14.0];

[lbl setText:@"Write a message..."];

[lbl setBackgroundColor:[UIColor clearColor]];

[lbl setTextColor:[UIColor lightGrayColor]];

[textview addSubview:lbl];

Textansicht ist meine Textansicht.

Jetzt erklären

-(void)textViewDidChange:(UITextView *)textView {

 if (![textView hasText]){

    lbl.hidden = NO;

 }
 else{
    lbl.hidden = YES;
 }

}

Und Ihr Textview-Platzhalter ist fertig!


6

Ich empfehle die Verwendung des Pods 'UITextView + Platzhalter'

pod 'UITextView+Placeholder'

auf Ihrem Code

#import "UITextView+Placeholder.h"

////    

UITextView *textView = [[UITextView alloc] init];
textView.placeholder = @"How are you?";
textView.placeholderColor = [UIColor lightGrayColor];

5
    - (void)textViewDidChange:(UITextView *)textView
{
    placeholderLabel.hidden = YES;
}

Beschriften Sie die Textansicht.


Oder noch besser, Sie können es erneut anzeigen,
Despotovic

Ich mag diese saubere Lösung, keine Injektion in die Textansicht.
Itachi

5

Es ist nicht möglich, einen Platzhalter in UITextView zu erstellen, aber Sie können dadurch einen Effekt wie einen Platzhalter erzeugen.

  - (void)viewDidLoad{      
              commentTxtView.text = @"Comment";
              commentTxtView.textColor = [UIColor lightGrayColor];
              commentTxtView.delegate = self;

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

     -(void) textViewDidChange:(UITextView *)textView
     {

    if(commentTxtView.text.length == 0){
        commentTxtView.textColor = [UIColor lightGrayColor];
        commentTxtView.text = @"Comment";
        [commentTxtView resignFirstResponder];
    }
    }

ODER Sie können Beschriftungen in der Textansicht genau wie hinzufügen

       lbl = [[UILabel alloc] initWithFrame:CGRectMake(10.0, 0.0,textView.frame.size.width - 10.0, 34.0)];


[lbl setText:kDescriptionPlaceholder];
[lbl setBackgroundColor:[UIColor clearColor]];
[lbl setTextColor:[UIColor lightGrayColor]];
textView.delegate = self;

[textView addSubview:lbl];

und setzen

- (void)textViewDidEndEditing:(UITextView *)theTextView
{
     if (![textView hasText]) {
     lbl.hidden = NO;
}
}

- (void) textViewDidChange:(UITextView *)textView
{
    if(![textView hasText]) {
      lbl.hidden = NO;
}
else{
    lbl.hidden = YES;
 }  
}

5

Dies ahmt den Platzhalter von UITextField perfekt nach, wobei der Platzhaltertext so lange erhalten bleibt, bis Sie tatsächlich etwas eingeben.

private let placeholder = "Type here"

@IBOutlet weak var textView: UITextView! {
    didSet {
        textView.textColor = UIColor.lightGray
        textView.text = placeholder
        textView.selectedRange = NSRange(location: 0, length: 0)
    }
}

extension ViewController: UITextViewDelegate {

    func textViewDidChangeSelection(_ textView: UITextView) {
        // Move cursor to beginning on first tap
        if textView.text == placeholder {
            textView.selectedRange = NSRange(location: 0, length: 0)
        }
    }

    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        if textView.text == placeholder && !text.isEmpty {
            textView.text = nil
            textView.textColor = UIColor.black
            textView.selectedRange = NSRange(location: 0, length: 0)
        }
        return true
    }

    func textViewDidChange(_ textView: UITextView) {
        if textView.text.isEmpty {
            textView.textColor = UIColor.lightGray
            textView.text = placeholder
        }
    }
}

4

Hier ist noch eine andere Möglichkeit, die die leichte Einrückung des UITextFieldPlatzhalters reproduziert :

Ziehen Sie eine UITextFieldrechte unter die, UITextViewso dass ihre oberen linken Ecken ausgerichtet sind. Fügen Sie Ihren Platzhaltertext zum Textfeld hinzu.

Fügen Sie in viewDidLoad Folgendes hinzu:

[tView setDelegate:self];
tView.contentInset = UIEdgeInsetsMake(-8,-8,0,0);
tView.backgroundColor = [UIColor clearColor];

Dann füge hinzu:

- (void)textViewDidChange:(UITextView *)textView {
    if (textView.text.length == 0) {
        textView.backgroundColor = [UIColor clearColor];            
    } else {
        textView.backgroundColor = [UIColor whiteColor];
    }
}

4

Machen wir es uns einfach

Erstellen Sie ein UILabel und platzieren Sie es in Ihrer Textansicht (Geben Sie den Text als Platzhalter-festgelegte Farbe grau an - Sie können dies alles in Ihrer xib tun). Jetzt deklarieren Sie in Ihrer Header-Datei das UILabel und auch das textviewDelegate. Jetzt können Sie die Beschriftung einfach ausblenden wenn Sie auf die Textansicht klicken

vollständiger Code unten

Header

@interface ViewController :UIViewController<UITextViewDelegate>{
 }
   @property (nonatomic,strong) IBOutlet UILabel *PlceHolder_label;
   @property (nonatomic,strong) IBOutlet UITextView *TextView;

@end

Implementierung

@implementation UploadFoodImageViewController
@synthesize PlceHolder_label,TextView;

  - (void)viewDidLoad
    {
       [super viewDidLoad];
    }


 - (BOOL)textViewShouldBeginEditing:(UITextView *)textView{

       if([textView isEqual:TextView]){
            [PlceHolder_label setHidden:YES];
            [self.tabScrlVw setContentOffset:CGPointMake(0,150) animated:YES];
          }
      return YES;
    }

@Ende

Vergessen Sie nicht, textView und UILabel von xib aus mit dem Dateibesitzer zu verbinden


4

Schauen Sie sich UTPlaceholderTextView an .

Dies ist eine praktische Unterklasse von UITextView, die Platzhalter unterstützt, die denen von UITextField ähneln. Hauptbesonderheiten:

  • Verwendet keine Unteransichten
  • DrawRect wird nicht überschrieben:
  • Platzhalter können beliebig lang sein und genauso wie normaler Text gerendert werden

4

Ich habe all dies durchgelesen, aber eine sehr kurze Swift 3-Lösung gefunden, die in all meinen Tests funktioniert hat. Es könnte etwas allgemeiner sein, aber der Prozess ist einfach. Hier ist die ganze Sache, die ich "TextViewWithPlaceholder" nenne.

import UIKit

class TextViewWithPlaceholder: UITextView {

    public var placeholder: String?
    public var placeholderColor = UIColor.lightGray

    private var placeholderLabel: UILabel?

    // Set up notification listener when created from a XIB or storyboard.
    // You can also set up init() functions if you plan on creating
    // these programmatically.
    override func awakeFromNib() {
        super.awakeFromNib()

        NotificationCenter.default.addObserver(self,
                                           selector: #selector(TextViewWithPlaceholder.textDidChangeHandler(notification:)),
                                           name: .UITextViewTextDidChange,
                                           object: self)

        placeholderLabel = UILabel()
        placeholderLabel?.alpha = 0.85
        placeholderLabel?.textColor = placeholderColor
    }

    // By using layoutSubviews, you can size and position the placeholder
    // more accurately. I chose to hard-code the size of the placeholder
    // but you can combine this with other techniques shown in previous replies.
    override func layoutSubviews() {
        super.layoutSubviews()

        placeholderLabel?.textColor = placeholderColor
        placeholderLabel?.text = placeholder

        placeholderLabel?.frame = CGRect(x: 6, y: 4, width: self.bounds.size.width-16, height: 24)

        if text.isEmpty {
            addSubview(placeholderLabel!)
            bringSubview(toFront: placeholderLabel!)
        } else {
            placeholderLabel?.removeFromSuperview()
        }
    }

    // Whenever the text changes, just trigger a new layout pass.
    func textDidChangeHandler(notification: Notification) {
        layoutSubviews()
    }
}

Einige Bedenken hier. Sie sollten nicht layoutSubviews()direkt anrufen . Und Sie haben den NotificationCenter-Beobachter nicht entfernt.
Hlung

4

Ich habe schnell eine Klasse geschrieben. Sie können diese Klasse bei Bedarf importieren.

import UIKit

öffentliche Klasse CustomTextView: UITextView {

private struct Constants {
    static let defaultiOSPlaceholderColor = UIColor(red: 0.0, green: 0.0, blue: 0.0980392, alpha: 0.22)
}
private let placeholderLabel: UILabel = UILabel()

private var placeholderLabelConstraints = [NSLayoutConstraint]()

@IBInspectable public var placeholder: String = "" {
    didSet {
        placeholderLabel.text = placeholder
    }
}

@IBInspectable public var placeholderColor: UIColor = CustomTextView.Constants.defaultiOSPlaceholderColor {
    didSet {
        placeholderLabel.textColor = placeholderColor
    }
}

override public var font: UIFont! {
    didSet {
        placeholderLabel.font = font
    }
}

override public var textAlignment: NSTextAlignment {
    didSet {
        placeholderLabel.textAlignment = textAlignment
    }
}

override public var text: String! {
    didSet {
        textDidChange()
    }
}

override public var attributedText: NSAttributedString! {
    didSet {
        textDidChange()
    }
}

override public var textContainerInset: UIEdgeInsets {
    didSet {
        updateConstraintsForPlaceholderLabel()
    }
}

override public init(frame: CGRect, textContainer: NSTextContainer?) {
    super.init(frame: frame, textContainer: textContainer)
    commonInit()
}

required public init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    commonInit()
}

private func commonInit() {
    NSNotificationCenter.defaultCenter().addObserver(self,
                                                     selector: #selector(textDidChange),
                                                     name: UITextViewTextDidChangeNotification,
                                                     object: nil)

    placeholderLabel.font = font
    placeholderLabel.textColor = placeholderColor
    placeholderLabel.textAlignment = textAlignment
    placeholderLabel.text = placeholder
    placeholderLabel.numberOfLines = 0
    placeholderLabel.backgroundColor = UIColor.clearColor()
    placeholderLabel.translatesAutoresizingMaskIntoConstraints = false
    addSubview(placeholderLabel)
    updateConstraintsForPlaceholderLabel()
}

private func updateConstraintsForPlaceholderLabel() {
    var newConstraints = NSLayoutConstraint.constraintsWithVisualFormat("H:|-(\(textContainerInset.left + textContainer.lineFragmentPadding))-[placeholder]",
                                                                        options: [],
                                                                        metrics: nil,
                                                                        views: ["placeholder": placeholderLabel])
    newConstraints += NSLayoutConstraint.constraintsWithVisualFormat("V:|-(\(textContainerInset.top))-[placeholder]",
                                                                     options: [],
                                                                     metrics: nil,
                                                                     views: ["placeholder": placeholderLabel])
    newConstraints.append(NSLayoutConstraint(
        item: placeholderLabel,
        attribute: .Width,
        relatedBy: .Equal,
        toItem: self,
        attribute: .Width,
        multiplier: 1.0,
        constant: -(textContainerInset.left + textContainerInset.right + textContainer.lineFragmentPadding * 2.0)
        ))
    removeConstraints(placeholderLabelConstraints)
    addConstraints(newConstraints)
    placeholderLabelConstraints = newConstraints
}

@objc private func textDidChange() {
    placeholderLabel.hidden = !text.isEmpty
}

public override func layoutSubviews() {
    super.layoutSubviews()
    placeholderLabel.preferredMaxLayoutWidth = textContainer.size.width - textContainer.lineFragmentPadding * 2.0
}

deinit {
    NSNotificationCenter.defaultCenter().removeObserver(self,
                                                        name: UITextViewTextDidChangeNotification,
                                                        object: nil)
}

}}

Geben Sie hier die Bildbeschreibung ein


1
Dies ist tatsächlich eine der saubersten Lösungen, die ich finden konnte, und am Ende habe ich diese verwendet. Insbesondere die Berücksichtigung von Einsätzen und die Verwendung von Einschränkungen für das Etikett ist eine nette Geste, ich habe nicht viele (irgendwelche?) Andere Lösungen gesehen Pflege Grenzen, Schriftarten zu verändern, RTL usw.
Mark
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.