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?
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?
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
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?
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.
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]; }
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
{
}
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.
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.
Stimme mit @jklp überein, diese hochgestimmten übertechnisierten Methoden sind nicht so schön wie diese.
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.
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]; }
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.
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.
Ich habe eine sehr einfache Möglichkeit gefunden, einen Platzhalter zu imitieren
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;
}
}
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.
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.
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).
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 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.
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
Hier ist eine kleine und geniale Lösung für dieses Problem
24 Stimmen
Wie wäre es mit der Kategorie UITextView+Placeholder? github.com/devxoul/UITextView-Platzhalter
2 Stimmen
Ich bevorzuge die Lösung von @devxoul, da sie Kategorie und nicht Unterklasse verwendet. Außerdem erzeugt sie ein Feld für "Platzhalter"-Optionen (Platzhaltertext und Textfarbe) in IBs Inspektor. Sie verwendet einige Bindungstechniken. Was für ein toller Code
0 Stimmen
Wenn Sie Folgendes verwenden
UITextView
wird die Lösung ganz anders aussehen. Hier sind ein paar Lösungen. Schwebender Platzhalter y Gefälschte Native Platzhalter0 Stimmen
Versuchen Sie diese Antwort :) [_textView setTextColor:[UIColor lightGrayColor]];
0 Stimmen
@ICoffeeConsumer Interessant ist, dass die native iOS-Kalender-App mehrzeiligen editierbaren Text hat - mit einem Platzhalter (Hinzufügen von "Notizen" zu einem Ereignis). Es gibt also einen direkten Beweis dafür, dass Apple selbst die Notwendigkeit/Nutzung dafür erkennt.