Заполнитель в UITextView

В моем приложении используется UITextView. Теперь я хочу, чтобы у UITextView был заполнитель, аналогичный тому, который вы можете установить для UITextField.

Как это сделать?

Изменить:

Use this cocoapod:
https://cocoapods.org/pods/UITextView+Placeholder

person Community    schedule 25.08.2009    source источник
comment
TTTextEditor от Three20 (сам использует UITextField) поддерживает текст заполнителя, а также его рост по высоте (он превращается в UITextView).   -  person Josh Benjamin    schedule 04.08.2011
comment
comment
Как насчет использования категории UITextView + Placeholder? github.com/devxoul/UITextView-Placeholder   -  person devxoul    schedule 27.02.2015
comment
Я предпочитаю решение @ devxoul, потому что оно использует категорию, а не подкласс. И он также создает поле для параметров «заполнитель» (текст заполнителя и цвет текста) в инспекторе IB. Он использует некоторые методы связывания. Какой отличный код   -  person samthui7    schedule 21.10.2016
comment
Если вы используете UITextView, решение становится совсем другим. Вот пара решений. Плавающий заполнитель и Поддельные собственные заполнители   -  person clearlight    schedule 08.01.2017
comment
Попробуйте этот ответ :) [_textView setTextColor: [UIColor lightGrayColor]];   -  person Kingsley Mitchell    schedule 20.03.2017
comment
@ICoffeeConsumer Что интересно, так это то, что в родном приложении Календаря iOS есть редактируемый многострочный текст - с заполнителем (добавление «Заметок» к событию). Итак, есть некоторые прямые доказательства того, что Apple сама осознает необходимость / использование этого.   -  person wardw    schedule 29.07.2018


Ответы (61)


Я внес несколько незначительных изменений в решение bcd, чтобы обеспечить возможность инициализации из файла Xib, переноса текста и сохранения цвета фона. Надеюсь, это избавит других от неприятностей.

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
person Community    schedule 09.11.2009
comment
Каждый раз, когда вызывается drawRect, в иерархию представления добавляется новый UILabel. Я мало что сделал для drawRect - это предположение, что в представлении нет постоянства, так что каждый раз, когда вызывается drawRect, ранее добавленное подпредставление метки было выпущено? - person memmons; 17.03.2011
comment
В целом хорошая реализация, но я должен согласиться с ответом Сэма Соффеса - добавление метки в иерархию представлений довольно сложно для того, что необходимо. Вдобавок его реализация _updateShouldDrawPlaceholder и использование setNeedsDisplay более тщательны. - person memmons; 17.03.2011
comment
в некоторых случаях (особенно для совместимости с iOS 5) требуется переопределить paste: - (void) paste: (id) sender {[super paste: sender]; [self textChanged: nil]; } - person Martin Ullrich; 14.07.2011
comment
Хорошая вещь! Напоминание о лучших практиках для NSString (или любого класса, имеющего эквивалент NSMutableXXX), свойство следует копировать, а не сохранять. - person Oli; 29.07.2011
comment
@gamma: нельзя ли дублировать код в initWithFrame: и awakeFromNib: в одном методе (чтобы облегчить любые дополнительные стили)? Или это имеет дополнительные последствия? - person SpacyRicochet; 05.09.2011
comment
@SpacyRicochet Думаю, да (я только что взял исходный код от Джейсона Джорджа;)) - но это вас не пощадит. Я считаю, что код вполне читабельный. - person gamma; 05.09.2011
comment
Как создать экземпляр этого кода? Я не вижу текста-заполнителя и ничего не очищается, когда я начинаю печатать. - person user798719; 21.12.2012
comment
@JasonGeorge вам нужно изменить прерыватель строки на [placeHolderLabel setLineBreakMode: NSLineBreakByWordWrapping]; в IOs6 - person Dejell; 27.01.2013
comment
Было бы лучше проверить заполнитель == nil перед установкой @. В построителе интерфейса вы можете использовать определяемые пользователем атрибуты времени выполнения, чтобы установить заполнитель (и placeholderColor), и в настоящее время они переопределяются методом awakeFromNib. Я поменял свой. - person Fogmeister; 18.04.2013
comment
@RazorSharp Попробуйте использовать: ((UIPlaceHolderTextView *)MyTextView.placeholder).placeholder = @"blabla"; - person Jeremy; 29.07.2013
comment
@RazorSharp Джереми ответ дал мне sigAbort, поэтому я попробовал экземпляр UIPlaceHolderTextView, например: - profleBio = [[UIPlaceHolderTextView alloc] init ... и [profleBio setPlaceholder: @Blah]; - person iMeMyself; 01.08.2013
comment
Это очень и очень плохо написанная реализация. Вот значительно более чистая версия, которая также отслеживает изменения диктовки: github.com/cbowns/MPTextView - person cbowns; 02.08.2013
comment
Я думаю, вы можете добавить заполнитель в IB. - person Bagusflyer; 27.10.2013
comment
Не изменяйте иерархию представлений в drawRect. - person Karmeye; 31.10.2013
comment
Эта практика может привести к некоторым ошибкам. Насколько я знаю, это может как-то нарушить анимацию в свойстве backgroundColor. Я думаю, это потому, что вы изменяете иерархию представлений в drawRect. - person Henry H Miao; 13.06.2014
comment
Чтобы иметь дело не только с text, но и с такими свойствами, как font или textAlignment, вы можете попробовать эту категорию: github.com / devxoul / UITextView-Placeholder - person devxoul; 27.02.2015
comment
Заполнитель не будет обновляться, если он снова будет установлен кодом. Переопределите сеттер, чтобы исправить это: - (void)setPlaceholder:(NSString *)placeholder { _placeholder = placeholder; [self setNeedsDisplay]; } - person Justin; 16.07.2015
comment
Вы можете использовать другой UITextView вместо UILabel для сопоставления текстовых вставок. - person Uladzimir; 06.08.2015
comment
К сожалению, репозиторий @cbowns MPTextView больше не поддерживается. - person ThomasW; 14.08.2015
comment
Совет: вы можете добавить IBDesignable к свойствам .h (непосредственно перед объявлением типа), чтобы иметь возможность установить текст и цвет заполнителя в раскадровке! - person gklka; 20.08.2015
comment
Я не понимаю, почему Apple не добавила свойство заполнителя в UITextView. Я не понимаю. - person crubio; 03.11.2015
comment
содержит слишком много логических ошибок. В моем случае мне нужно сделать ярлык общедоступным, потому что фрейм не работает должным образом. Затем мне нужно установить ограничения - они тоже не будут работать по умолчанию из-за глупой инициализации метки (внутри drawRect и не создается, если заполнитель пуст) и т. Д. - person Vyachaslav Gerchicov; 02.01.2019

Простой способ, просто создайте текст-заполнитель в UITextView, используя следующие UITextViewDelegate методы:

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

просто не забудьте установить myUITextView с точным текстом при создании, например.

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

и сделайте родительский класс UITextViewDelegate перед включением этих методов, например.

@interface MyClass () <UITextViewDelegate>
@end

Код для 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()
}

просто не забудьте установить myUITextView с точным текстом при создании, например.

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

и сделайте родительский класс UITextViewDelegate перед включением этих методов, например.

class MyClass: UITextViewDelegate
{

}
person Community    schedule 18.04.2012
comment
Это здорово (я люблю простые) для 1 экрана с 1 UITextView. Причина более сложных решений в том, что если у вас есть более крупное приложение с МНОГИМИ экранами и МНОГИМИ UITextViews, вы не хотите делать это снова и снова. Вероятно, вы захотите создать подкласс UITextView в соответствии со своими потребностями, а затем использовать его. - person ghostatron; 24.01.2013
comment
Если кто-то вводит здесь текст-заполнитель ... в текстовое поле, он также ведет себя как текст-заполнитель. Кроме того, во время подачи вам нужно будет проверить все эти критерии. - person Anindya Sengupta; 18.09.2013
comment
Согласитесь с @jklp, эти зарекомендовавшие себя чрезмерно продуманные методы не так хороши, как этот. - person Tony Xu; 14.11.2013
comment
Это ломает enablesReturnKeyAutomatically. :( - person Rudolf Adamkovič; 04.01.2014
comment
Текст-заполнитель должен отображаться, даже когда поле становится респондентом, и этот метод не будет работать для этого. - person phatmann; 06.01.2014
comment
Используйте [UIColor colorWithWhite: 0.8 alpha: 1.0] для того же цвета, что и цвет заполнителя UITextField в iOS 7. - person Fogh; 25.02.2014
comment
@jklp Я бы сказал, что чрезмерно спроектированный способ чище и более пригоден для повторного использования ... и похоже, что он не вмешивается в атрибут text текстового представления, что довольно приятно ... тогда как этот метод изменяет его - person Cameron Askew; 07.04.2014
comment
Это будет показывать странное поведение, если пользователь нажимает на текст заполнителя. Обычно вы хотите, чтобы текст исчез, если это произойдет, но это предполагает, что пользователь будет только печатать. Не большая проблема, но заметная тонкость, которая заставила меня выбрать решение Сэма Соффса. - person Usman Mutawakil; 05.05.2014
comment
@phatmann - Но это игнорирует тот факт, что продолжать показывать текст заполнителя после того, как поле получает фокус, - это очень глупое соглашение, которое стало обычным только потому, что это то, что выбрала Apple. - person aroth; 25.07.2014
comment
@aroth Мне тоже не нравится такое поведение, но OP сказал, что им нужен заполнитель, аналогичный тому, который вы можете установить для UITextField. Это означает, что они хотят, чтобы их заполнитель вел себя как можно больше как UITextField. - person phatmann; 27.07.2014
comment
почему вызовы статьFirstResponder и resignFirstResponder в методах делегата? - person Adam Johns; 03.03.2015
comment
Думал, что это хорошо, но у него нет необходимого UX - т.е. текст-заполнитель остается в непосредственном фокусе, а введенный текст не появляется снова, когда нет введенного текста. Хорошая попытка :) - person dijipiji; 22.06.2015

Я не был слишком доволен ни одним из опубликованных решений, поскольку они были немного тяжелыми. Добавление представлений к представлению на самом деле не идеально (особенно в drawRect:). У них обоих были утечки, что тоже недопустимо.

Вот мое решение: 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

Это намного проще, чем другие, поскольку он не использует подпредставления (и не имеет утечек). Не стесняйтесь использовать это.

Обновление от 10.11.11: теперь оно задокументировано и поддерживает использование в Interface Builder.

Обновление 24.11.13: укажите новое репо.

person Community    schedule 18.08.2010
comment
Мне нравится ваше решение, и я добавил переопределение setText, чтобы также обновлять заполнитель при программном изменении свойства текста: - (void) setText: (NSString *) string {[super setText: string]; [self _updateShouldDrawPlaceholder]; } - person olegueret; 08.09.2010
comment
Мне тоже нравится ваше решение, но вы пропустили метод awakefromnib, поэтому ваш метод инициализации не всегда вызывается. Я взял его у кого-то из присутствующих. - person toxaq; 06.12.2010
comment
Примечание - магические числа будут варьироваться в зависимости от размера шрифта. Точное расположение можно рассчитать по шрифту, но, вероятно, этого не стоит для этой реализации. Правильное расположение заполнителя должно быть на 2 пикселя вправо от позиции курсора, когда нет текста. - person memmons; 18.03.2011
comment
Чтобы решить проблему загрузки из пера: вы, вероятно, захотите добавить - (id) initWithCoder: (NSCoder *) aDecoder; инициализатор для сопровождения существующего. - person Joris Kluivers; 04.05.2011
comment
Это мило. Однако аргумент notification в последнем методе написан с ошибкой, и это слишком маленькое изменение, чтобы отправлять его в качестве редактирования. - person Phil Calvin; 11.05.2012
comment
Мне нравится подход. Я портировал его на Swift / iOS 8 и выложил ниже. Несколько настроек, но пока что у меня работает с базовым UITextView. Спасибо - person clearlight; 02.01.2015
comment
Это не работает в iOS 9.2, я бы выбрал этот github.com/gcamp/GCPlaceholderTextView (следующий ответ), он работает отлично, и вы можете установить текст заполнителя и цвет текста заполнителя. - person Boris Nikolic; 08.01.2016

Я нашел себе очень простой способ имитировать заполнитель

  1. в NIB или коде установите textView для textColor на lightGrayColor (большую часть времени)
  2. убедитесь, что ваш делегат textView связан с владельцем файла и реализует UITextViewDelegate в файле заголовка
  3. установите текст по умолчанию для текстового представления (пример: «Заполнитель Foobar»)
  4. реализовать: (BOOL) textViewShouldBeginEditing: (UITextView *) textView

Изменить:

Изменены операторы if для сравнения тегов, а не текста. Если пользователь удалил свой текст, можно было также случайно удалить часть заполнителя @"Foobar placeholder". Это означало, что если пользователь повторно ввел textView, следующий метод делегата, -(BOOL) textViewShouldBeginEditing:(UITextView *) textView, он не будет работать должным образом. Я попытался сравнить по цвету текста в операторе if, но обнаружил, что светло-серый цвет, установленный в конструкторе интерфейса, не совпадает со светло-серым цветом, установленным в коде с [UIColor lightGreyColor]

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

Также можно сбросить текст заполнителя, когда клавиатура вернется и [textView length] == 0

РЕДАКТИРОВАТЬ:

Чтобы прояснить последнюю часть - вот как вы можете вернуть текст заполнителя:

- (void)textViewDidChange:(UITextView *)textView
{
   if([textView.text length] == 0)
   {
       textView.text = @"Foobar placeholder";
       textView.textColor = [UIColor lightGrayColor];
       textView.tag = 0;
   }
}
person Community    schedule 17.08.2011
comment
Мне такой подход очень нравится! Единственное, что я бы сделал с редактированием выше, - это переместить реализацию из метода textViewDidChange: в метод textViewDidEndEditing: так, чтобы текст заполнителя возвращался только после того, как вы закончите работу с объектом. - person horseshoe7; 01.04.2012
comment
Используйте этот cocoapods.org/pods/UITextView+Placeholder - person Abhishek Bedi; 01.02.2021

Что вы можете сделать, так это настроить текстовое представление с некоторым начальным значением в свойстве text и изменить textColor на [UIColor grayColor] или что-то подобное. Затем, когда текстовое представление становится доступным для редактирования, очистите текст и установите курсор, а если текстовое поле снова станет пустым, верните текст заполнителя. При необходимости измените цвет на [UIColor blackColor].

Это не совсем то же самое, что функциональность заполнителя в UITextField, но близко.

person Community    schedule 25.08.2009
comment
Я всегда использовал lightGrayColor, который, кажется, соответствует цвету текста заполнителя. - person Bill; 08.11.2010
comment
Я просто читаю это сейчас, но хочу добавить, что сброс цвета на черный и сброс свойства текста в textViewShouldBeginEditing: (UITextView *) textView работает очень хорошо. Это очень хорошее и быстрое решение по сравнению с решениями ниже (но все же элегантные решения ниже, подклассы uitextview. Намного более модульные). - person Enrico Susatyo; 13.04.2011
comment
Верно, но он не имитирует поведение UITextField, который заменяет свой текст-заполнитель только тогда, когда пользователь что-то вводит, а не когда начинается редактирование, и который снова добавляет заполнитель, когда представление становится пустым, а не когда редактирование фактически заканчивается. - person Ash; 13.03.2012

Вы можете установить метку на UITextView,

[UITextView addSubView:lblPlaceHoldaer];

и скрыть это методом TextViewdidChange.

Это простой и легкий способ.

person Community    schedule 20.12.2009

Простое решение Swift 3

Добавьте UITextViewDelegate в свой класс

Установить yourTextView.delegate = self

Создайте placeholderLabel и поместите его внутри yourTextView

Теперь просто анимируйте placeholderLabel.alpha на 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
      }
    }
  }

вам, возможно, придется поиграть с placeholderLabel позицией, чтобы настроить ее правильно, но это не должно быть слишком сложно

person Community    schedule 03.02.2015
comment
Отличный ответ, простое решение. Я добавил небольшое улучшение для анимации только тогда, когда альфа должна измениться: let alpha = CGFloat(textView.text.isEmpty ? 1.0 : 0.0) if alpha != lblPlaceholder.alpha { UIView.animate(withDuration: 0.3) { self.lblPlaceholder.alpha = alpha } } - person Luciano Sclovsky; 01.05.2018

Если кому-то нужно решение для Swift:

Добавьте UITextViewDelegate в свой класс

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
    }
}
person Community    schedule 31.10.2014
comment
это нормально, но недостаточно хорошо. если пользователь вводит текст-заполнитель ... (очевидно, крайний регистр), это нарушает вашу логику - person Lucas Chwe; 19.09.2018

Я расширил ответ KmKndy, так что заполнитель остается видимым, пока пользователь не начнет редактировать UITextView, а не просто коснется его. Это отражает функциональность приложений Twitter и Facebook. Мое решение не требует от вас подкласса и работает, если пользователь вводит текст напрямую или вставляет его!

Пример заполнителяПриложение Twitter

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

просто не забудьте установить myUITextView с точным текстом при создании, например.

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

и сделайте родительский класс делегатом UITextView перед включением этих методов, например.

@interface MyClass () <UITextViewDelegate>
@end
person Community    schedule 08.01.2014

Рекомендую использовать SZTextView.

https://github.com/glaszig/SZTextView

Добавьте свой UITextView по умолчанию из storyboard, а затем измените его собственный класс на SZTextView, как показано ниже ????????????????

введите описание изображения здесь

Затем вы увидите две новые опции в Attribute Inspector ????????????????

введите описание изображения здесь

person Community    schedule 10.04.2015

Ниже приведен Swift-порт кода ObjC SAMTextView, опубликованный в качестве одного из первых ответов на вопрос. Я протестировал его на iOS 8. Я изменил несколько вещей, включая смещение границ для размещения текста заполнителя, так как оригинал был слишком высоким и слишком правым (использовалось предложение в одном из комментариев к этому сообщению).

Я знаю, что существует множество простых решений, но мне нравится подход к подклассу UITextView, потому что он многоразовый, и мне не нужно загромождать классы, используя его с механизмами.

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)
    }
}
person Community    schedule 02.01.2015
comment
спасибо за быструю версию, не могли бы вы объяснить, какой смысл иметь метод awakeFromNib, который просто вызывает super? - person Pierre; 22.01.2015
comment
Он устанавливает заполнитель, но не обновляется, когда вы начинаете печатать. - person David; 03.02.2015
comment
Неважно, я поставил вызов уведомления в awakeFromNib, и теперь он работает. - person David; 03.02.2015

вот как я это сделал:

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
person Community    schedule 09.10.2009

Вот более простое решение, которое ведет себя точно так же, как заполнитель UITextField, но не требует рисования пользовательских представлений или увольнения первого респондента.

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

}

(вторая проверка в инструкции else if предназначена для случая, когда ничего не вводится и пользователь нажимает клавишу возврата)

Просто установите свой класс как UITextViewDelegate. В viewDidLoad вы должны инициализировать как

- (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;
}
person Community    schedule 14.11.2012
comment
Дело в том, что если пользователь нажимает где-нибудь в середине текстового заполнителя, курсор остается там. - person Alex Sorokoletov; 04.07.2014

Привет, вы можете использовать IQTextView, доступный в IQKeyboard Manager, его просто использовать и интегрировать, просто установите класс вашего текстового представления в IQTextView, и вы можете использовать его свойство для установки метки-заполнителя с желаемым цветом. Вы можете загрузить библиотеку из IQKeyboardManager

или вы можете установить его из cocoapods.

person Community    schedule 25.09.2015
comment
IQKeyboardManager очень полезен и не требует кода. Лучший ответ для меня! - person NSDeveloper; 22.01.2016
comment
На самом деле я голосую и оставляю комментарий по причине. Я не знаю, что IQTextView раньше доступен в IQKeyboardManager. - person NSDeveloper; 23.01.2016
comment
У меня была проблема с делегатом. Я удалил из класса переопределение делегата, и UITextViewDelegate работал нормально - person TheoK; 18.06.2016
comment
недооцененный ответ - person grantespo; 07.04.2020

Извините, что добавляю еще один ответ, но я просто снял что-то подобное, и это создало заполнитель, ближайший к UITextField.

Надеюсь, это кому-то поможет.

-(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);
    }
}
person Community    schedule 08.05.2013
comment
Мне пришлось изменить порядок команд в первом операторе if if(textView.textColor == [UIColor lightGrayColor]){ textView.textColor = [UIColor blackColor]; textView.text = [textView.text substringToIndex: 1]; В противном случае первый символ, введенный в текстовое представление, был помещен в конец текста - person Flexicoder; 31.01.2014

Простой способ использовать это в какой-нибудь строке кода:

Поднесите одну метку к UITextView в .nib, подключив эту метку к вашему коду, после нее.

- (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;
}
person Community    schedule 28.03.2012

Я модифицировал реализацию Сэма Соффса для работы с iOS7:

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

Не забудьте указать _placeholderAttribues = nil в методах, которые могут изменить шрифт и другие элементы, которые могут на них повлиять. Вы также можете отказаться от «ленивого» создания словаря атрибутов, если это вас не беспокоит.

РЕДАКТИРОВАТЬ:

Не забудьте вызвать setNeedsDisplay в переопределенной версии setBounds, если вы хотите, чтобы заполнитель хорошо выглядел после анимации автоматического размещения и т.п.

person Community    schedule 22.10.2013
comment
Я думаю, вам следует добавить insert.left к параметру смещения x. - person Karmeye; 31.10.2013
comment
Разве это не setFrame вместо setBounds? - person JakubKnejzlik; 13.01.2014
comment
Неа! Похоже, что setFrame не вызывается во время анимации макета. - person Nailer; 13.01.2014

Это идеально имитирует заполнитель UITextField, где текст заполнителя остается до тех пор, пока вы действительно что-то не введете.

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
        }
    }
}
person Community    schedule 12.11.2016
comment
Необходимо добавить еще несколько строк, чтобы мое приложение SwiftUI работало с оболочкой UITextView SwuftUI, но похоже, что это самое простое решение, которое не включает в себя код ObjC, стороннюю платформу или специально замену методов. - person vedrano; 27.04.2021

Вы также можете создать новый класс TextViewWithPlaceholder как подкласс UITextView.

(Этот код довольно грубый, но я думаю, что он на правильном пути.)

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

}

В своем делегате добавьте это:

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

}
person Community    schedule 25.08.2009
comment
Чтобы получить точное поведение, вы должны нарисовать свой собственный заполнитель, переопределив drawRect: (рисовать заполнитель, только если! [Self isFirstResponder] && [[self text] length] == 0) и вызвать setNeedsDisplay внутри статьFirstResponder и resignFirstResponder - person rpetrich; 26.08.2009

Я сделал свою версию подкласса UITextView. Мне понравилась идея Сэма Соффеса об использовании уведомлений, но мне не понравилась перезапись drawRect:. Мне это кажется излишним. Я думаю, что сделал очень чистую реализацию.

Вы можете посмотреть мой подкласс здесь. Также включен демонстрационный проект.

person Community    schedule 22.04.2011

В этой ветке было много ответов, но вот версия, которую я предпочитаю.

Он расширяет существующий класс UITextView, поэтому его можно легко использовать повторно, и он не перехватывает такие события, как textViewDidChange (которые могут нарушить код пользователя, если они уже перехватывали эти события в другом месте. ).

Используя мой код (показанный ниже), вы можете легко добавить заполнитель к любому из ваших UITextViews следующим образом:

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

Когда вы устанавливаете это новое значение заполнителя, оно незаметно добавляет UILabel поверх вашего UITextView, а затем при необходимости скрывает / отображает его:

введите описание изображения здесь

Хорошо, чтобы внести эти изменения, добавьте файл «UITextViewHelper.h», содержащий этот код:

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

... и файл UITextViewHelper.m, содержащий это:

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

Да, кода много, но как только вы добавили его в свой проект и включили файл .h ...

#import "UITextViewHelper.h"

... вы можете легко использовать заполнители в UITextViews.

Однако есть одна ошибка.

Если вы сделаете это:

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

... заполнитель появится поверх текста. Когда вы устанавливаете значение text, ни одно из обычных уведомлений не вызывается, поэтому я не мог понять, как вызвать свою функцию, чтобы решить, показывать / скрывать заполнитель.

Решение состоит в том, чтобы установить textValue, а не text:

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

В качестве альтернативы вы можете установить значение text, а затем вызвать checkIfNeedToDisplayPlaceholder.

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

Мне нравятся такие решения, поскольку они «заполняют пробел» между тем, что Apple предоставляет нам, и тем, что нам (как разработчикам) действительно нужно в наших приложениях. Вы пишете этот код один раз, добавляете его в свою библиотеку «вспомогательных» файлов .m / .h, и со временем SDK начинает становиться все менее разочаровывающим.

(Я написал похожий помощник для добавления кнопки «очистить» в мои UITextViews, еще одна вещь, которая досадно существует в UITextField, но не в _20 _...)

person Community    schedule 13.02.2015
comment
Мне нравится, насколько это чисто, но мне не нравится, что для этого требуется второй UIView / UILabel (и на самом деле не так легко наследовать атрибуты / цвета / шрифт от UITextView). Хороший вклад, хотя - person mattsven; 10.03.2015

Сначала возьмите метку в файле .h.

Вот я беру

UILabel * lbl;

Затем в .m под 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];

textview - это мой TextView.

Теперь объявите

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

 if (![textView hasText]){

    lbl.hidden = NO;

 }
 else{
    lbl.hidden = YES;
 }

}

И ваш заполнитель Textview готов!

person Community    schedule 04.12.2015

Я рекомендую использовать модуль UITextView + Placeholder.

pod 'UITextView+Placeholder'

на вашем коде

#import "UITextView+Placeholder.h"

////    

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

Основываясь уже на некоторых замечательных предложениях, я смог собрать следующий легкий подкласс UITextView, совместимый с Interface-Builder, который:

  • Включает настраиваемый текст-заполнитель, стилизованный так же, как у UITextField.
  • Не требует дополнительных вложений или ограничений.
  • Не требует делегирования или другого поведения от ViewController.
  • Не требует уведомлений.
  • Сохраняет этот текст полностью отделенным от любых внешних классов, просматривающих свойство поля text.

Предложения по улучшению приветствуются.

Изменить 1: обновлено для сброса форматирования заполнителя, если фактический текст задается программно.

Изменить 2: цвет текста заполнителя теперь можно получить программно.

Swift v5:

import UIKit
@IBDesignable class TextViewWithPlaceholder: UITextView {
    
    override var text: String! { // Ensures that the placeholder text is never returned as the field's text
        get {
            if showingPlaceholder {
                return "" // When showing the placeholder, there's no real text to return
            } else { return super.text }
        }
        set {
            if showingPlaceholder {
                removePlaceholderFormatting() // If the placeholder text is what's being changed, it's no longer the placeholder
            }
            super.text = newValue
        }
    }
    @IBInspectable var placeholderText: String = ""
    @IBInspectable var placeholderTextColor: UIColor = .placeholderText
    private var showingPlaceholder: Bool = true // Keeps track of whether the field is currently showing a placeholder
    
    override func didMoveToWindow() {
        super.didMoveToWindow()
        if text.isEmpty {
            showPlaceholderText() // Load up the placeholder text when first appearing, but not if coming back to a view where text was already entered
        }
    }
    
    override public func becomeFirstResponder() -> Bool {
        
        // If the current text is the placeholder, remove it
        if showingPlaceholder {
            text = nil
            removePlaceholderFormatting()
        }
        return super.becomeFirstResponder()
    }
    
    override public func resignFirstResponder() -> Bool {
        
        // If there's no text, put the placeholder back
        if text.isEmpty {
            showPlaceholderText()
        }
        return super.resignFirstResponder()
    }
    
    private func showPlaceholderText() {
        
        text = placeholderText
        showingPlaceholder = true
        textColor = placeholderTextColor
    }
    
    private func removePlaceholderFormatting() {
        
        showingPlaceholder = false
        textColor = nil // Put the text back to the default, unmodified color
    }
}
person Community    schedule 27.04.2019
comment
При установке текста uitextview вручную возникает проблема, т.е. мой TextViewWithPlaceholder.text = some text не отображает эту строку как txt, а как текст-заполнитель. - person AfnanAhmad; 06.12.2019
comment
@AfnanAhmad Чтобы установить text программно, сделайте переменную showingPlaceHolder общедоступной, затем перед установкой текста установите для showingPlaceHolder значение false и установите текст. Он должен тогда появиться. - person Ahmet Gokdayi; 13.02.2020
comment
Спасибо за отзыв @AfnanAhmad. Сейчас я внес несколько изменений, чтобы лучше поддерживать программно установленный текст. Лучше не делать общедоступные компоненты, которые должны быть частными. - person TheNeil; 14.02.2020

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

поместите метку поверх текстового представления.

person Community    schedule 22.08.2012
comment
Или, что еще лучше, вы можете показать его снова, когда текста нет: lblPlaceholder.hidden =! [TextView.text isEqualToString: @]; - person Despotovic; 30.12.2013
comment
Мне нравится это чистое решение, без инъекций в текстовое представление. - person Itachi; 05.05.2020

Невозможно создать заполнитель в UITextView, но с его помощью вы можете создать эффект, такой как заполнитель.

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

ИЛИ вы можете добавить ярлык в текстовом окне, как

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

и установите

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

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

Вот еще один способ сделать это, воспроизводящий небольшой отступ заполнителя UITextField:

Перетащите UITextField прямо под UITextView так, чтобы их верхние левые углы были выровнены. Добавьте свой текст-заполнитель в текстовое поле.

В viewDidLoad добавьте:

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

Затем добавьте:

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

Давай упростим

Создайте один UILabel и поместите его в текстовое представление (дайте текст серому цвету с установленным заполнителем - вы можете сделать все это в своем xib). Теперь в заголовочном файле объявите UILabel, а также textviewDelegate. Теперь вы можете просто скрыть метку. когда вы нажимаете на текстовое изображение

полный код ниже

заголовок

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

@end

реализация

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

@конец

Не забудьте подключить textView и UILabel к fileowner из xib

person Community    schedule 29.11.2012


Я прочитал все это, но придумал очень короткое решение Swift 3, которое работало во всех моих тестах. Можно было бы немного обобщить, но процесс прост. Вот все, что я называю «TextViewWithPlaceholder».

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()
    }
}
person Community    schedule 11.11.2016
comment
Здесь есть некоторые опасения. Вы не должны звонить layoutSubviews() напрямую. И вы не удалили наблюдателя NotificationCenter. - person Hlung; 27.11.2018

Я быстро написал класс. При необходимости вы можете импортировать этот класс.

import UIKit

общедоступный класс 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)
}

}

введите здесь описание изображения

person Community    schedule 27.12.2016
comment
На самом деле это одно из самых чистых решений, которые я смог найти, и в итоге я использовал это .. Особенно, если учесть вставки и использование ограничений для метки, я не видел много (каких-либо?) Других решений, использующих забота об изменении границ, шрифтов, RTL и т. д. - person Mark; 15.10.2018

Я создал быструю 3-ю версию ответа с самым высоким рейтингом

Вам просто нужно создать подкласс UITextView.

import UIKit

 class UIPlaceHolderTextView: UITextView {


//MARK: - Properties
@IBInspectable var placeholder: String?
@IBInspectable var placeholderColor: UIColor?
var placeholderLabel: UILabel?


//MARK: - Initializers
override func awakeFromNib() {
    super.awakeFromNib()


}

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

    // Use Interface Builder User Defined Runtime Attributes to set
    // placeholder and placeholderColor in Interface Builder.
    if self.placeholder == nil {
        self.placeholder = ""
    }

    if self.placeholderColor == nil {
        self.placeholderColor = UIColor.black
    }

    NotificationCenter.default.addObserver(self, selector: #selector(textChanged(_:)), name: NSNotification.Name.UITextViewTextDidChange, object: nil)

}

func textChanged(_ notification: Notification) -> Void {
    if self.placeholder?.count == 0 {
        return
    }

    UIView.animate(withDuration: 0.25) {
        if self.text.count == 0 {
            self.viewWithTag(999)?.alpha = 1
        }
        else {
            self.viewWithTag(999)?.alpha = 0
        }
    }
}

// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
    super.draw(rect)

    if (self.placeholder?.count ?? 0) > 0 {
        if placeholderLabel == nil {
            placeholderLabel = UILabel.init()
            placeholderLabel?.lineBreakMode = .byWordWrapping
            placeholderLabel?.numberOfLines = 0
            placeholderLabel?.font = self.font
            placeholderLabel?.backgroundColor = self.backgroundColor
            placeholderLabel?.textColor = self.placeholderColor
            placeholderLabel?.alpha = 0
            placeholderLabel?.tag = 999
            self.addSubview(placeholderLabel!)

            placeholderLabel?.translatesAutoresizingMaskIntoConstraints = false
            placeholderLabel?.topAnchor.constraint(equalTo: self.topAnchor, constant: 7).isActive = true
            placeholderLabel?.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 4).isActive = true
            placeholderLabel?.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
            placeholderLabel?.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
        }

        placeholderLabel?.text = self.placeholder
        placeholderLabel?.sizeToFit()
        self.sendSubview(toBack: self.placeholderLabel!)
    }

    if self.text.count == 0 && (self.placeholder?.count ?? 0) > 0 {
        self.viewWithTag(999)?.alpha = 1
    }
 }
}
person Community    schedule 30.01.2018
comment
Заполнитель вообще не отображается. Использование Swift 5. Есть идеи? - person Rowan Gontier; 07.04.2019

Хорошо, мой коллектор немного отличается, я создаю небольшой класс, чтобы сделать это за вас.

TextViewShader.m файл

#import "TextViewShader.h"

@implementation TextViewShader
-(id)initWithShadedTextView:(NSString *)text textViewToShade:(UITextView *)textview {
    self = [super initWithFrame:textview.frame];
    if (self) {
        if (shadeLabel==nil)
        {
            shadeLabel= [[UILabel alloc]initWithFrame:CGRectMake(10, 0, textview.frame.size.width, 30)];


    }
    shadeLabel.text =text;// @"Enter Your Support Request";
    shadeLabel.textColor = [UIColor lightGrayColor];
    [textview setDelegate: self];
    [textview addSubview:shadeLabel];
}
return self;
}

-(void)textViewDidChange:(UITextView *)textView{
        if (textView.text.length==0)
        {
            shadeLabel.hidden=false; 
        }
        else
        {
            shadeLabel.hidden=true;
        }

}

@end

Файл TextViewShader.h

#import <UIKit/UIKit.h>

@interface TextViewShader : UIView<UITextViewDelegate>{
    UILabel *shadeLabel;

}
-(id)initWithShadedTextView:(NSString *)text textViewToShade:(UITextView *)textview ;
@end

это простая одна строка использования кода (не забудьте добавить #import "TextViewShader.h")

 TextViewShader* shader = [[TextViewShader alloc]initWithShadedTextView:@"Enter Your Support Request" textViewToShade: youruitextviewToshade];

повеселись :)

person Community    schedule 01.02.2012
comment
Метод делегата UITextView не вызывается в классе шейдера. - person Birju; 14.12.2019

Я создал переменную экземпляра, чтобы проверить, показываю ли я заполнитель или нет:

BOOL showPlaceHolder;
UITextView * textView; // and also the textView

На viewDidLoad я установил:

[self setPlaceHolder]; 

Вот что это делает:

- (void)setPlaceholder
{
    textView.text = NSLocalizedString(@"Type your question here", @"placeholder");
    textView.textColor = [UIColor lightGrayColor];
    self.showPlaceHolder = YES; //we save the state so it won't disappear in case you want to re-edit it
}

Еще я создал кнопку, чтобы отказаться от клавиатуры. Вам не обязательно этого делать, но замечательно то, что заполнитель отображается снова, если ничего не было введено.

- (void)textViewDidBeginEditing:(UITextView *)txtView 
{
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStyleDone target:self action:@selector(resignKeyboard)];
    if (self.showPlaceHolder == YES) 
    {
        textView.textColor = [UIColor blackColor];
        textView.text = @"";
        self.showPlaceHolder = NO;
    }
}

- (void)resignKeyboard 
{
    [textView resignFirstResponder];
    //here if you created a button like I did to resign the keyboard, you should hide it
    if (textView.text.length == 0) {
        [self setPlaceholder];
    }       
}
person Community    schedule 01.08.2011

Вот простой и умный способ добиться идеального поведения.

Позаимствуем заполнитель у UITextField.

введите описание изображения здесь

  1. Настройте textField и сделайте его текст прозрачным.

    self.placeholderTextField = [[UITextField alloc] init];
    
    /* adjust the frame to fit it in the first line of your textView */
    self.placeholderTextField.frame = CGRectMake(0.0, 0.0, yourTextView.width, 30.0);
    
    self.placeholderTextField.textColor = [UIColor clearColor];
    self.placeholderTextField.userInteractionEnabled = NO;
    self.placeholderTextField.font = yourTextView.font;
    self.placeholderTextField.placeholder = @"sample placeholder";
    [yourTextView addSubview:self.placeholderTextField];
    
  2. Установите делегата textView и синхронизируйте textField и textView.

    yourTextView.delegate = self;
    

    тогда

    - (void)textViewDidChange:(UITextView *)textView {
    
        self.placeholderTextField.text = textView.text;
    
    }
    
  3. Это все.
person Community    schedule 05.03.2015
comment
Сначала мне понравилось это решение - новаторская идея - но оно вызывало проблемы с прокруткой и задержку, и не было модульным, поэтому я переключился на SAMTextView. - person jk7; 28.02.2020

Более простой подход - создать вторичный UITextView со всеми теми же атрибутами, что и исходное текстовое представление, за исключением другого textColor, с ограничениями, обеспечивающими их выравнивание. Затем, когда какие-либо символы вводятся в основное текстовое представление, скройте клонированное текстовое представление, в противном случае покажите клонированное текстовое представление с некоторым текстом.

Это может быть достигнуто несколькими способами, но относительно чистый способ - создать подкласс UITextView и сохранить всю эту логику внутри подкласса.

Итак, создайте подкласс UITextView и позвольте ему лениво создавать представление заполнителя:

Файл интерфейса:

@interface FOOTextView : UITextView <UITextViewDelegate>

@property (nonatomic, copy) NSString *placeholderText;

- (void)checkPlaceholder;

@end

Файл реализации:

#import "FOOTextView.h"

@interface FOOTextView ()

@property (nonatomic, strong) UITextView *placeholderTextView;

@end

@implementation FOOTextView

- (void)checkPlaceholder {
    // Hide the placeholder text view if we've got any text
    self.placeholderTextView.hidden = (self.text.length > 0 || self.attributedText.length > 0);
}

- (void)setPlaceholderText:(NSString *)placeholderText {
    _placeholderText = [placeholderText copy];

    // Setup the placeholder text view if we haven't already
    [self setupPlaceholderTextView];

    // Apply the placeholder text to the placeholder text view
    self.placeholderTextView.text = placeholderText;
}

- (void)setupPlaceholderTextView {
    if (!self.placeholderTextView) {

        // Setup the place holder text view, duplicating our visual setup
        self.placeholderTextView = [[UITextView alloc] initWithFrame:CGRectZero];
        self.placeholderTextView.translatesAutoresizingMaskIntoConstraints = NO;
        self.placeholderTextView.textColor = self.placeholderTextColor ? self.placeholderTextColor : [UIColor colorWithRed:199.f/255.f green:199.f/255.f blue:205.f/255.f alpha:1.f];
        self.placeholderTextView.userInteractionEnabled = NO;
        self.placeholderTextView.font = self.font;
        self.placeholderTextView.textAlignment = self.textAlignment;
        self.placeholderTextView.backgroundColor = self.backgroundColor;
        self.placeholderTextView.editable = NO;

        // Our background color must be clear for the placeholder text view to show through
        self.backgroundColor = [UIColor clearColor];

        // Insert the placeholder text view into our superview, below ourself so it shows through
        [self.superview insertSubview:self.placeholderTextView belowSubview:self];

        // Setup constraints to ensure the placeholder text view stays aligned with us
        NSLayoutConstraint *constraintCenterX = [NSLayoutConstraint constraintWithItem:self.placeholderTextView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.f constant:0.f];
        NSLayoutConstraint *constraintCenterY = [NSLayoutConstraint constraintWithItem:self.placeholderTextView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.f constant:0.f];
        NSLayoutConstraint *constraintWidth = [NSLayoutConstraint constraintWithItem:self.placeholderTextView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1.f constant:0.f];
        NSLayoutConstraint *constraintHeight = [NSLayoutConstraint constraintWithItem:self.placeholderTextView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeHeight multiplier:1.f constant:0.f];
        NSArray *constraints = @[constraintCenterX, constraintCenterY, constraintWidth, constraintHeight];
        [self.superview addConstraints:constraints];

    }
}

- (void)setPlaceholderTextColor:(UIColor *)placeholderTextColor {
    _placeholderTextColor = placeholderTextColor;
    self.placeholderTextView.textColor = _placeholderTextColor;
}

- (void)setBackgroundColor:(UIColor *)backgroundColor {
    // We don't want a background color ourselves, instead we want our placeholder text view to have the desired background color
    [self.placeholderTextView setBackgroundColor:backgroundColor];
}

- (void)removeFromSuperview {
    // Ensure we also remove our placeholder text view
    [self.placeholderTextView removeFromSuperview];
    self.placeholderTextView = nil;
    [super removeFromSuperview];
}

#pragma mark - Text View Delegation 
- (void)textViewDidChange:(UITextView *)textView {
    [self checkPlaceholder];
}

@end

Используя указанный выше класс, если вы установите экземпляр делегата FOOTextView самому себе, все будет работать из коробки:

FOOTextView *myTextView = ...
myTextView.placeholderText = @"What's on your mind?";
myTextView.placeholderTextColor = [UIColor lightGrayColor];
myTextView.delegate = myTextView;

Если вы хотите, чтобы другой объект выполнял функции делегата, вам просто нужно вызвать метод checkPlaceholder текстового представления в методе делегата textViewDidChange:, например;

FOOTextView *myTextView = ...
myTextView.placeholderText = @"What's on your mind?";
myTextView.placeholderTextColor = [UIColor lightGrayColor];
myTextView.delegate = self;
self.myTextView = myTextView;

- (void)textViewDidChange:(UITextView *)textView {
    // Call the checkPlaceholder method to update the visuals
    [self.myTextView checkPlaceholder];
}
person Community    schedule 14.12.2015

Я знаю, что на этот вопрос уже есть много ответов, но я не нашел ни одного из них достаточным (по крайней мере, в Swift). Мне нужна была функция «заполнитель» UITextField в моем UITextView (мне нужно было точное поведение, включая атрибуты отображения текста, анимацию и т. Д., И я не хотел поддерживать это с течением времени). Я также хотел решение, которое обеспечивало бы ту же точную границу, что и UITextField (не приблизительное, которое выглядит примерно так, как сейчас, а такое, которое выглядит точно так же и всегда будет выглядеть точно так же). Так что, хотя я изначально не был поклонником добавления дополнительного элемента управления в микс, казалось, что для достижения моих целей мне пришлось использовать настоящий UITextField и позволить ему делать всю работу.

Это решение обрабатывает размещение заполнителя и синхронизацию шрифта между двумя элементами управления, так что текст заполнителя является точным шрифтом и положением текста, введенного в элемент управления (что многие другие решения не решают).

// This class is necessary to support "inset" (required to position placeholder 
// appropriately in TextView)
//
class TextField: UITextField
{
    var inset: UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0);

    override func textRectForBounds(bounds: CGRect) -> CGRect
    {
        return UIEdgeInsetsInsetRect(bounds, inset);
    }

    override func placeholderRectForBounds(bounds: CGRect) -> CGRect
    {
        return UIEdgeInsetsInsetRect(bounds, inset);
    }
}

// This class implements a UITextView that has a UITextField behind it, where the 
// UITextField provides the border and the placeholder text functionality (so that the
// TextView looks and works like a UITextField).
//
class TextView : UITextView, UITextViewDelegate
{
    var textField = TextField();

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

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

        self.delegate = self;

        // Create a background TextField with clear (invisible) text and disabled
        self.textField.borderStyle = UITextBorderStyle.RoundedRect;
        self.textField.textColor = UIColor.clearColor();
        self.textField.userInteractionEnabled = false;

        // Align the background TextView to where text appears in the TextField, so
        // that any placeholder will be in the correct position.
        self.textField.contentVerticalAlignment = UIControlContentVerticalAlignment.Top;
        self.textField.inset = UIEdgeInsets(
            top: self.textContainerInset.top,
            left: self.textContainerInset.left + self.textContainer.lineFragmentPadding,
            bottom: self.textContainerInset.bottom,
            right: self.textContainerInset.right
        );

        // The background TextField should use the same font (for the placeholder)
        self.textField.font = self.font;

        self.addSubview(textField);
        self.sendSubviewToBack(textField);
    }

    convenience init()
    {
        self.init(frame: CGRectZero, textContainer: nil)
    }

    override var font: UIFont?
    {
        didSet
        {
            // Keep the font of the TextView and background textField in sync
            self.textField.font = self.font;
        }
    }

    var placeholder: String? = nil
    {
        didSet
        {
            self.textField.placeholder = self.placeholder;
        }
    }

    override func layoutSubviews()
    {
        super.layoutSubviews()
        // Do not scroll the background textView
        self.textField.frame = CGRectMake(0, self.contentOffset.y, self.frame.width, self.frame.height);
    }

    // UITextViewDelegate - Note: If you replace delegate, your delegate must call this
    func scrollViewDidScroll(scrollView: UIScrollView)
    {
        // Do not scroll the background textView
        self.textField.frame = CGRectMake(0, self.contentOffset.y, self.frame.width, self.frame.height);
    }

    // UITextViewDelegate - Note: If you replace delegate, your delegate must call this
    func textViewDidChange(textView: UITextView)
    {
        // Updating the text in the background textView will cause the placeholder to 
        // appear/disappear (including any animations of that behavior - since the
        // textView is doing this itself).
        self.textField.text = self.text;
    }
}
person Community    schedule 11.04.2016
comment
Я исправил (дайте мне знать, работает ли). Раньше было сложно заставить инициализатор работать, поэтому я не поддерживал инициализацию Storyboard / XIB (мы не используем их в нашем приложении - мы создаем все элементы управления программно). Я возражаю против сбоев. Цель fatalError состояла в том, чтобы указать, что он не поддерживает инициализацию из Storyboard / XIB (хотя это, по общему признанию, не было полностью ясно из сообщения). - person BobDickinson; 18.06.2016

- (BOOL) textViewShouldBeginEditing:(UITextView *)textView
{
    //NSLog(@"textViewShouldBeginEditing");
    if( [tvComment.text isEqualToString:@"Comment"] && [tvComment.textColor isEqual:[UIColor lightGrayColor]] ){
        tvComment.text = @"";
        tvComment.textColor = [UIColor blackColor];
    }
    return YES;
}

- (void)keyboardWillBeHidden:(NSNotification*)aNotification{
    //NSLog(@"keyboardWillBeHidden");

    //Manage comment field placeholdertext
    if(tvComment.text.length == 0){
        tvComment.textColor = [UIColor lightGrayColor];
        tvComment.text = @"Comment";
    }
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    tvComment.textColor = [UIColor lightGrayColor];
}

TVComment - это свойство, содержащее рассматриваемый textView. Это поможет.

person Community    schedule 23.05.2013

Я написал более чистую реализацию, попробовав некоторые из предложенных подходов, и разместил ее на Github. Запросы на вытягивание и проблемы приветствуются.

Некоторые ключевые улучшения по сравнению с другими подходами, представленными здесь:

  • Не размещает UILabel внутри drawRect:. (Пожалуйста, никогда не делайте этого.)
  • Не сравнивает текущий текст текстового представления с желаемым заполнителем для замены цветов.
  • Скрывает заполнитель при активном диктовке (например, UITextField).
person Community    schedule 29.07.2013

В классе .h

@interface RateCommentViewController : UIViewController<UITextViewDelegate>{IBoutlet UITextview *commentTxtView;}

В классе .m

- (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];
    }
}
person Community    schedule 23.03.2015


Имитация собственных заполнителей


Распространенная проблема заключается в том, что iOS не предоставляет встроенную функцию заполнителя для текстовых представлений. Расширение UITextView ниже пытается решить эту проблему, предлагая удобство, которое можно ожидать от встроенной функции, требуя только одной строки кода для добавления заполнителя к экземпляру textview.

Обратной стороной этого решения является то, что оно уязвимо для (что маловероятно) изменений протокола UITextViewDelegate в обновлении iOS, поскольку оно включает вызовы делегатов в гирляндную цепочку. В частности, если iOS добавляет новые методы протокола и вы реализуете любой из них в делегате для текстового представления с заполнителем, эти методы не будут вызываться, если вы также не обновили расширение для пересылки этих звонки.

В качестве альтернативы, ответ Inline Placeholder надежен и настолько прост, насколько это возможно.


Примеры использования:


• Если текстовое представление, получающее заполнитель, не использует UITextViewDelegate:

    /* Swift 3 */

    class NoteViewController : UIViewController {
        @IBOutlet weak var noteView: UITextView!
        override func viewDidLoad() {
            noteView.addPlaceholder("Enter some text...",  color: UIColor.lightGray)
        }
    }

- ИЛИ -

• Если текстовое представление получает заполнитель действительно, используйте UITextViewDelegate:

    /* Swift 3 */

    class NoteViewController : UIViewController, UITextViewDelegate {
        @IBOutlet weak var noteView: UITextView!
        override func viewDidLoad() {
            noteView.addPlaceholder("Phone #", color: UIColor.lightGray, delegate: self)
        }
    }

Реализация (UITextView расширение):


/* Swift 3 */

extension UITextView: UITextViewDelegate
{

    func addPlaceholder(_ placeholderText : String, 
                      color : UIColor? = UIColor.lightGray,
                      delegate : UITextViewDelegate? = nil) {

        self.delegate = self             // Make receiving textview instance a delegate
        let placeholder = UITextView()   // Need delegate storage ULabel doesn't provide
        placeholder.isUserInteractionEnabled = false  //... so we *simulate* UILabel
        self.addSubview(placeholder)     // Add to text view instance's view tree               
        placeholder.sizeToFit()          // Constrain to fit inside parent text view
        placeholder.tintColor = UIColor.clear // Unused in textviews. Can host our 'tag'
        placeholder.frame.origin = CGPoint(x: 5, y: 0) // Don't cover I-beam cursor
        placeholder.delegate = delegate  // Use as cache for caller's delegate 
        placeholder.font = UIFont.italicSystemFont(ofSize: (self.font?.pointSize)!)
        placeholder.text = placeholderText
        placeholder.textColor = color
    }

      
    func findPlaceholder() -> UITextView? { // find placeholder by its tag 
        for subview in self.subviews {
            if let textview = subview as? UITextView {
                if textview.tintColor == UIColor.clear { // sneaky tagging scheme
                    return textview
                }
            }
        }
        return nil
    }
     
    /* 
     * Safely daisychain to caller delegate methods as appropriate...
     */

    public func textViewDidChange(_ textView: UITextView) { // ←  need this delegate method
        if let placeholder = findPlaceholder() {
            placeholder.isHidden = !self.text.isEmpty // ← ... to do this
            placeholder.delegate?.textViewDidChange?(textView)
        } 
    }

    /* 
     * Since we're becoming a delegate on behalf of this placeholder-enabled
     * text view instance, we must forward *all* that protocol's activity expected
     * by the instance, not just the particular optional protocol method we need to
     * intercept, above.
     */

    public func textViewDidEndEditing(_ textView: UITextView) {
        if let placeholder = findPlaceholder() {
            placeholder.delegate?.textViewDidEndEditing?(textView)
        } 
    }

    public func textViewDidBeginEditing(_ textView: UITextView) {
        if let placeholder = findPlaceholder() {
            placeholder.delegate?.textViewDidBeginEditing?(textView)
        } 
    }

    public  func textViewDidChangeSelection(_ textView: UITextView) {
        if let placeholder = findPlaceholder() {
            placeholder.delegate?.textViewDidChangeSelection?(textView)
        } 
    }

    public func textViewShouldEndEditing(_ textView: UITextView) -> Bool {
        if let placeholder = findPlaceholder() {
            guard let retval = placeholder.delegate?.textViewShouldEndEditing?(textView) else {
                return true
            }
            return retval
        }
        return true
    }

    public func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
        if let placeholder = findPlaceholder() {
            guard let retval = placeholder.delegate?.textViewShouldBeginEditing?(textView) else {
                return true
            }
            return retval
        } 
        return true
    }

    public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        if let placeholder = findPlaceholder() {
            guard let retval = placeholder.delegate?.textView?(textView, shouldChangeTextIn: range, replacementText: text) else {
                return true
            }
            return retval
        } 
        return true
    }

    public func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
        if let placeholder = findPlaceholder() {
                guard let retval = placeholder.delegate?.textView?(textView, shouldInteractWith: URL, in: characterRange, interaction:
                    interaction) else {
                        return true
            }
            return retval
        }
        return true
    }

    public func textView(_ textView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
        if let placeholder = findPlaceholder() {
            guard let retval = placeholder.delegate?.textView?(textView, shouldInteractWith: textAttachment, in: characterRange, interaction: interaction) else {
                return true
            }
            return retval
        }
        return true
    }
}

1. Как расширение важного класса iOS, такого как UITextView, важно знать, что этот код не взаимодействует с любыми текстовыми представлениями, которые не t активировать заполнитель, например экземпляры textview, которые не были инициализированы с помощью вызова addPlaceholder()

2. Текстовые представления с включенными заполнителями прозрачно становятся UITextViewDelegate для отслеживания количества символов и контроля видимости заполнителей . Если делегат передается в addPlaceholder(), этот код в цепочку (т. Е. Пересылает) делегирует обратные вызовы этому делегату.

3. Автор изучает способы проверки протокола UITextViewDelegate и автоматического прокси-сервера без необходимости жесткого кодирования каждого метода. . Это приведет к прививке кода из изменений сигнатуры метода и добавления новых методов в протокол.

person Community    schedule 08.01.2017

Другое решение

import UIKit

protocol PlaceholderTextViewDelegate: class {

    func placeholderTextViewDidChangeText(_ text: String)
    func placeholderTextViewDidEndEditing(_ text: String)
}

final class PlaceholderTextView: UITextView {

    weak var notifier: PlaceholderTextViewDelegate?
    var ignoreEnterAction: Bool = true

    var placeholder: String? {
        didSet {
            text = placeholder
            selectedRange = NSRange(location: 0, length: 0)
        }
    }

    var placeholderColor = UIColor.lightGray {
        didSet {
            if text == placeholder {
                textColor = placeholderColor
            }
        }
    }
    var normalTextColor = UIColor.lightGray

    var placeholderFont = UIFont.sfProRegular(28)

    fileprivate var placeholderLabel: UILabel?

    // MARK: - LifeCycle

    override var text: String? {
        didSet {
            if text == placeholder {
                textColor = placeholderColor
            } else {
                textColor = normalTextColor
            }
        }
    }

    init() {
        super.init(frame: CGRect.zero, textContainer: nil)
        awakeFromNib()
    }

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

    override func awakeFromNib() {
        super.awakeFromNib()

        self.delegate = self
    }
}

extension PlaceholderTextView: UITextViewDelegate {

    // MARK: - UITextViewDelegate
    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {

        if text == "" && textView.text == placeholder {
            return false
        }

        if let placeholder = placeholder,
            textView.text == placeholder,
            range.location <= placeholder.count {
            textView.text = ""
        }

        if ignoreEnterAction && text == "\n" {
            textView.resignFirstResponder()
            return false
        }
        return true
    }

    func textViewDidChange(_ textView: UITextView) {
        if let placeholder = placeholder {
            textView.text = textView.text.replacingOccurrences(of: placeholder, with: "")
        }

        if let placeholder = placeholder,
            text?.isEmpty == true {
            text = placeholder
            textColor = placeholderColor

            selectedRange = NSRange(location: 0, length: 0)
        } else {
            textColor = normalTextColor
        }

        notifier?.placeholderTextViewDidChangeText(textView.text)
    }

    func textViewDidChangeSelection(_ textView: UITextView) {
        if let placeholder = placeholder,
            text == placeholder {
            selectedRange = NSRange(location: 0, length: 0)
        }
    }

    func textViewDidEndEditing(_ textView: UITextView) {
        notifier?.placeholderTextViewDidEndEditing(textView.text)

        if let placeholder = placeholder,
            text?.isEmpty == true {
            text = placeholder
            textColor = placeholderColor
            selectedRange = NSRange(location: 0, length: 0)
        } else {
            textColor = normalTextColor
        }
    }
}

результат:

введите здесь описание изображения

person Community    schedule 18.05.2018
comment
заполнитель не меняется после ввода текста. - person Mitesh Dobareeya; 26.09.2018

TextView PlaceHolder быстро

import UIKit

@IBDesignable
open class KMPlaceholderTextView: UITextView {

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

    public let placeholderLabel: UILabel = UILabel()

    private var placeholderLabelConstraints = [NSLayoutConstraint]()

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

    @IBInspectable open var placeholderColor: UIColor = KMPlaceholderTextView.Constants.defaultiOSPlaceholderColor {
        didSet {
            placeholderLabel.textColor = placeholderColor
        }
    }

    override open var font: UIFont! {
        didSet {
            if placeholderFont == nil {
                placeholderLabel.font = font
            }
        }
    }

    open var placeholderFont: UIFont? {
        didSet {
            let font = (placeholderFont != nil) ? placeholderFont : self.font
            placeholderLabel.font = font
        }
    }

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

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

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

    override open 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() {
        #if swift(>=4.2)
        let notificationName = UITextView.textDidChangeNotification
        #else
        let notificationName = NSNotification.Name.UITextView.textDidChangeNotification
        #endif

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(textDidChange),
                                               name: notificationName,
                                               object: nil)

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

    private func updateConstraintsForPlaceholderLabel() {
        var newConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-(\(textContainerInset.left + textContainer.lineFragmentPadding))-[placeholder]",
            options: [],
            metrics: nil,
            views: ["placeholder": placeholderLabel])
        newConstraints += NSLayoutConstraint.constraints(withVisualFormat: "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.isHidden = !text.isEmpty
        self.layoutIfNeeded()
    }

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

    deinit {
        #if swift(>=4.2)
        let notificationName = UITextView.textDidChangeNotification
        #else
        let notificationName = NSNotification.Name.UITextView.textDidChangeNotification
        #endif

        NotificationCenter.default.removeObserver(self,
                                                  name: notificationName,
                                                  object: nil)
    }

}

использование

введите здесь описание изображения

person Community    schedule 21.10.2019

Если вы ищете простой способ добиться этого, попробуйте мой подход:

- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
    if ([[textView text] isEqualToString:PLACE_HOLDER_TEXT]) {
          textView.text = @"";
          textView.textColor = [UIColor blackColor];
    }

    return YES;
}

-(BOOL)textViewShouldEndEditing:(UITextView *)textView
{
    if ([[textView text] length] == 0) {
        textView.text = PLACE_HOLDER_TEXT;
        textView.textColor = [UIColor lightGrayColor];
    }
    return YES;
}

Да, это PLACE_HOLDER_TEXT NSString, содержащий ваш заполнитель

person Community    schedule 11.03.2014

Я нашел собственное решение

- (void)textViewDidBeginEditing:(UITextView *)textView
{
    if ([textView.text isEqualToString:PLACEHOLDER_TEXT])
    {
        textView.textColor = [UIColor lightGrayColor];
        dispatch_async(dispatch_get_main_queue(), ^
                       {
                           textView.selectedRange = NSMakeRange(0, 0);
                       });
    }
    else
    {
        textView.textColor = [UIColor blackColor];
    }

    [textView becomeFirstResponder];
}

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

    [textView resignFirstResponder];
}

- (BOOL)textView:(UITextView *)textView
shouldChangeTextInRange:(NSRange)range
 replacementText:(NSString *)text
{
    if (range.location == 0 && range.length == [[textView text] length] && [text isEqualToString:@""])
    {
        textView.text = PLACEHOLDER_TEXT;
        textView.textColor = [UIColor lightGrayColor];

        dispatch_async(dispatch_get_main_queue(), ^
                       {
                           textView.selectedRange = NSMakeRange(0, 0);
                       });

        return NO;
    }

    if ([textView.text isEqualToString:PLACEHOLDER_TEXT])
    {
        textView.text = @"";
        textView.textColor = [UIColor blackColor];
    }

    return YES;
}
person Community    schedule 19.10.2014

Еще один ответ:

https://github.com/gcamp/GCPlaceholderTextView

Измените класс UITextView в IB на GCPlaceholderTextView и установите свойство placeholder

person Community    schedule 20.02.2015

Swift 3.1

Попробовав все быстрые ответы, этот ответ сэкономил бы мне 3 часа исследований. Надеюсь это поможет.

  1. Убедитесь, что ваше текстовое поле (независимо от вашего собственного имени) указывает на своего делегата в раскадровке и имеет @IBOutlet с yourCustomTextField

  2. Добавьте в viewDidLoad() следующее, оно появится при загрузке представления:

Покажи мне, что выглядит как заполнитель:

yourCustomTextField = "Start typing..." 
yourCustomTextField.textColor = .lightGray
  1. Вне viewDidLoad, но внутри того же класса добавьте следующие объявления: UIViewController, UITextViewDelegate, UINavigationControllerDelegate

Этот код заставит yourCustomTextField исчезнуть при вводе textField:

func textViewDidBeginEditing (_ textView: UITextView) { 

    if (textView.text == "Start typing...") {

        textView.text = ""
        textView.textColor = .black
    }

    textView.becomeFirstResponder()
}

func textViewDidEndEditing(_ textView: UITextView) {
    if (textView.text == "") {

        textView.text = "Start typing..."
        textView.textColor = .lightGray
    }

    textView.resignFirstResponder()
}
person Community    schedule 12.04.2017

Вот код для Swift 3.1

Оригинальный код Джейсона Джорджа в первом ответе.

Не забудьте установить свой собственный класс для TextView в построителе интерфейса на UIPlaceHolderTextView, а затем установить свойства placeholder и placeHolder.

import UIKit

@IBDesignable
class UIPlaceHolderTextView: UITextView {

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

private let uiPlaceholderTextChangedAnimationDuration: Double = 0.05
private let defaultTagValue = 999

private var placeHolderLabel: UILabel?

override func awakeFromNib() {
    super.awakeFromNib()
    NotificationCenter.default.addObserver(
        self,
        selector: #selector(textChanged),
        name: NSNotification.Name.UITextViewTextDidChange,
        object: nil
    )
}

override init(frame: CGRect, textContainer: NSTextContainer?) {
    super.init(frame: frame, textContainer: textContainer)
    NotificationCenter.default.addObserver(
        self,
        selector: #selector(textChanged),
        name: NSNotification.Name.UITextViewTextDidChange,
        object: nil
    )
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    NotificationCenter.default.addObserver(
        self,
        selector: #selector(textChanged),
        name: NSNotification.Name.UITextViewTextDidChange,
        object: nil
    )
}

deinit {
    NotificationCenter.default.removeObserver(
        self,
        name: NSNotification.Name.UITextViewTextDidChange,
        object: nil
    )
}

@objc private func textChanged() {
    guard !placeholder.isEmpty else {
        return
    }
    UIView.animate(withDuration: uiPlaceholderTextChangedAnimationDuration) {
        if self.text.isEmpty {
            self.viewWithTag(self.defaultTagValue)?.alpha = CGFloat(1.0)
        }
        else {
            self.viewWithTag(self.defaultTagValue)?.alpha = CGFloat(0.0)
        }
    }
}

override var text: String! {
    didSet{
        super.text = text
        textChanged()
    }
}

override func draw(_ rect: CGRect) {
    if !placeholder.isEmpty {
        if placeHolderLabel == nil {
            placeHolderLabel = UILabel.init(frame: CGRect(x: 0, y: 8, width: bounds.size.width - 16, height: 0))
            placeHolderLabel!.lineBreakMode = .byWordWrapping
            placeHolderLabel!.numberOfLines = 0
            placeHolderLabel!.font = font
            placeHolderLabel!.backgroundColor = UIColor.clear
            placeHolderLabel!.textColor = placeholderColor
            placeHolderLabel!.alpha = 0
            placeHolderLabel!.tag = defaultTagValue
            self.addSubview(placeHolderLabel!)
        }

        placeHolderLabel!.text = placeholder
        placeHolderLabel!.sizeToFit()
        self.sendSubview(toBack: placeHolderLabel!)

        if text.isEmpty && !placeholder.isEmpty {
            viewWithTag(defaultTagValue)?.alpha = 1.0
        }
    }

    super.draw(rect)
}
}
person Community    schedule 13.07.2017

Я выполнил код из этой ссылки. Всего 7 простых шагов. Он добавляет UILabel в textView и скрывает / показывает метку, когда текст вводится или удаляется из textView с помощью метода делегата textView textViewDidChangeSelection(_ textView: UITextView). Я поместил шаги в комментариях над кодом.

// 1. make sure to include the UITextViewDelegate
class YourClass: UITextViewDelegate {

    @IBOutlet weak var textView : UITextView!

    // 2. create placeholder textLabel
    let placeHolderTextLabel: UILabel = {
        let placeholderLabel = UILabel()
        placeholderLabel.text = "Placeholder text..."
        placeholderLabel.sizeToFit()
        placeholderLabel.textColor = UIColor.lightGray
        return placeholderLabel
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        // 3. set textView delegate
        textView.delegate = self

        configurePlaceholderTextLabel()
    }


    func configurePlaceholderTextLabel() {

        // 4. add placeholder label to textView, set it's frame and font
        textView.addSubview(placeHolderTextLabel)
        placeHolderTextLabel.frame.origin = CGPoint(x: 5, y: (textView.font?.pointSize)! / 2)
        placeHolderTextLabel.font = UIFont.systemFont(ofSize: (textView.font?.pointSize)!)

        // 5. decide wether the placeHolderTextLabel is hidden or not depending on if there is or isn't text inside the textView
        placeHolderTextLabel.isHidden = !textView.text.isEmpty

    }

    // 6. implement textView delegate method to update the placeHolderTextLabel when the text is changed
    func textViewDidChangeSelection(_ textView: UITextView) {

        // 7. decide wether the placeHolderTextLabel is hidden or not depending on if there is or isn't text inside the textView when text in textView is changed
        placeHolderTextLabel.isHidden = !textView.text.isEmpty
    }

}
person Community    schedule 29.07.2018

Еще один простой ответ, используя CATextLayer.

Добавьте CATextLayer к UITextView's слою. С помощью UITextViewDelegate методов просто измените цвет CATextLayer.

func txtViewPlaceholder() {
    let textlayer = CATextLayer()

    textlayer.frame = CGRect(x: 5, y: 5, width: 200, height: 18)
    textlayer.contentsScale = UIScreen.main.scale
    textlayer.fontSize = 12
    textlayer.alignmentMode = kCAAlignmentLeft
    textlayer.string = "Enter here"
    textlayer.isWrapped = true
    textlayer.name = "placeholder"
    textlayer.backgroundColor = UIColor.white.cgColor
    textlayer.foregroundColor = UIColor.black.cgColor

    yourTxtVw.layer.insertSublayer(textlayer, at: 0)
}

func removeAddPlaceholder(remove: Bool, textView: UITextView) {
    for layers in textView.layer.sublayers! where layers.name == "placeholder" {
        
        if remove {
            (layers as! CATextLayer).foregroundColor = UIColor.white.cgColor
        } else {
            (layers as! CATextLayer).foregroundColor = UIColor.black.cgColor
        }
        
    }
}


extension YourViewController : UITextViewDelegate {

    func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
    
        removeAddPlaceholder(remove: true, textView: textView)
    
        return true
    }

    func textViewDidEndEditing(_ textView: UITextView) {
    
        if textView.text.count <= 0 {
            removeAddPlaceholder(remove: false, textView: textView)
        }
    }

}
person Community    schedule 29.06.2020

Более простой и по-прежнему учитывающий некоторый текст, введенный пользователем в какой-то момент

BOOL placeHolderTextVisible;

на viewDidLoad установите значение YES (или DidMoveToSuperview, или awakeFromNib)

затем на - (BOOL) textView: (UITextView *) textView shouldBeginEditing

    - (BOOL)textViewShouldBeginEditing:(UITextView *)textView;
{
   if (placeHolderTextVisible) {
    placeHolderTextVisible = NO;
    textView.text = @"";
   }
 return YES;
}
person Community    schedule 04.10.2012
comment
Это вообще не решение - после нажатия UITextView заполнитель исчезает и больше не появляется. - person Alex Sorokoletov; 04.07.2014
comment
Забудет ли пользователь, что он умеет печатать, если сотрет текст? - person Stephen J; 07.07.2014
comment
Видна вставка, поясняющая, что пользователь может вводить текст. - person Alex Sorokoletov; 07.07.2014

Ответ Джейсона в iOS7 будет выглядеть немного не так, исправьте это, отрегулировав смещение _placeHolderLabel:

- (void)drawRect:(CGRect)rect
{
    if( [[self placeholder] length] > 0 )
    {
        if (_placeHolderLabel == nil )
        {
            if ([[UIDevice currentDevice].systemVersion floatValue] >= 7)
                _placeHolderLabel = [[UILabel alloc] initWithFrame:CGRectMake(4,8,self.bounds.size.width - 8,0)];
            else
                _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];
}
person Community    schedule 08.11.2013

Вы можете просто установить метку в текстовом окне.

MyUITextView.h

@interface MyUITextView : UITextView {
    UILabel* _placeholderLabel;
}

@property(nonatomic, assign)NSString *placeholder;

MyUITextView.m

@implementation MyUITextView

- (id)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        // Create placeholder
        viewFrame = CGRectMake(0, 0, frame.size.width, 15);
        _placeholderLabel = [[UILabel alloc] initWithFrame:viewFrame];
        _placeholderLabel.textColor = [UIColor lightGrayColor];
        [self addSubview:_placeholderLabel];

        // Add text changed notification 
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:nil];
    }
    return self;
}

- (void)setPlaceholder:(NSString *)placeholder {
    _placeholderLabel.text = placeholder;
}

- (NSString*)placeholder {
    return _placeholderLabel.text;
}

#pragma mark UITextViewTextDidChangeNotification

- (void)textChanged:(NSNotification *)notification {
    _placeholderLabel.hidden = ([self.text lenght] == 0);
}

@end
person Community    schedule 03.07.2013

Я только что обнаружил, что начиная с iOS 10 вы можете фактически преобразовать UITextView в метод как UITextField и установить внутри метода заполнитель. Просто попробовал, и он работает без подкласса UITextView.

Это пример того, что у меня сработало:

-(void)customizeTextField:(UITextField *)textField placeholder:(NSString *)pText withColor:(UIColor *)pTextColor{

        textField.attributedPlaceholder = [[NSAttributedString alloc]
                                          initWithString:pText
                                          attributes:@{NSForegroundColorAttributeName:pTextColor}];
    }

И чтобы использовать его для UITextView, вам просто нужно передать его методу, используя приведение типа:

[self customizeTextField:(UITextField*)_myTextView placeholder:@"Placeholder" withColor:[UIColor blackColor]];

Примечание: после теста я обнаружил, что решение отлично работает и на iOS9.x, но вызывает сбой на iOS8.x.

person Community    schedule 15.09.2016

После просмотра (и опробования) большинства предлагаемых решений этой, казалось бы, очевидной - но отсутствующей - функции UITextView, я нашел наиболее подходящее решение от BobDickinson. Но мне не нравилось прибегать к целому новому подклассу [я предпочитаю добавляемые категории для таких простых функциональных дополнений], а также к перехвату методов UITextViewDelegate, что, вероятно, испортит ваш существующий код обработки UITextView. Итак, вот мой взгляд на категорию drop-in, которая будет работать с любым существующим экземпляром UITextView ...

#import <objc/runtime.h>

// Private subclass needed to override placeholderRectForBounds: to correctly position placeholder
@interface _TextField : UITextField
@property UIEdgeInsets insets;
@end
@implementation _TextField
- (CGRect)placeholderRectForBounds:(CGRect)bounds
{
    CGRect rect = [super placeholderRectForBounds:bounds];
    return UIEdgeInsetsInsetRect(rect, _insets);
}
@end

@implementation UITextView (Placeholder)

static const void *KEY;

- (void)setPlaceholder:(NSString *)placeholder
{
    _TextField *textField = objc_getAssociatedObject(self, &KEY);
    if (!textField) {
        textField = [_TextField.alloc initWithFrame:self.bounds];
        textField.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        textField.userInteractionEnabled = NO;
        textField.font = self.font;

        textField.contentVerticalAlignment = UIControlContentVerticalAlignmentTop;
        textField.insets = UIEdgeInsetsMake(self.textContainerInset.top,
                                            self.textContainerInset.left + self.textContainer.lineFragmentPadding,
                                            self.textContainerInset.bottom,
                                            self.textContainerInset.right);
        [self addSubview:textField];
        [self sendSubviewToBack:textField];

        objc_setAssociatedObject(self, &KEY, textField, OBJC_ASSOCIATION_RETAIN);

        [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(updatePlaceholder:) name:UITextViewTextDidChangeNotification object:nil];
    }
    textField.placeholder = placeholder;
}

- (NSString*)placeholder
{
    UITextField *textField = objc_getAssociatedObject(self, &KEY);
    return textField.placeholder;
}

- (void)updatePlaceholder:(NSNotification *)notification
{
    UITextField *textField = objc_getAssociatedObject(self, &KEY);
    textField.font = self.font;
    [textField setAlpha:self.text.length? 0 : 1];
}

@end

Его просто использовать, просто очевидное

UITextView *myTextView = UITextView.new;
...
myTextView.placeholder = @"enter text here";

Он работает путем добавления UITextField - в нужном месте - за вашим UITextView и использования вместо него его заполнителя (поэтому вам не нужно беспокоиться о правильном цвете и т. Д.) , затем прослушивание уведомлений всякий раз, когда ваш UITextView изменяется для отображения / скрытия этого UITextField (и, следовательно, он не мешает вашим существующим вызовам UITextViewDelegate). И здесь нет никаких магических чисел ... :-)

Objc_setAssociatedObject () / objc_getAssociatedObject () позволяет избежать создания подкласса UITextView. [К сожалению, для правильного размещения UITextField потребовалось ввести «частный» подкласс, чтобы переопределить placeholderRectForBounds:]

Адаптировано из ответа Боб Дикинсона Swift.

person Community    schedule 13.10.2016

Просто создайте @IBDesignable подкласс своего UITextView:

@IBDesignable class AttributedTextView: UITextView {

    private let placeholderLabel = UILabel()

    @IBInspectable var placeholder: String = "" {

        didSet {

            setupPlaceholderLabelIfNeeded()
            textViewDidChange()
        }
    }

    override var text: String! {

        didSet {
            textViewDidChange()
        }
    }

    //MARK: - Initialization

    override func awakeFromNib() {
        super.awakeFromNib()

        setupPlaceholderLabelIfNeeded()
        NotificationCenter.default.addObserver(self, selector: #selector(textViewDidChange), name: .UITextViewTextDidChange, object: nil)
    }

    //MARK: - Deinitialization

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    //MARK: - Internal

    func textViewDidChange() {

        placeholderLabel.isHidden = !text.isEmpty
        layoutIfNeeded()
    }

    //MARK: - Private

    private func setupPlaceholderLabelIfNeeded() {

        placeholderLabel.removeFromSuperview()
        placeholderLabel.frame = CGRect(x: 0, y: 8, width: frame.size.width, height: 0)
        placeholderLabel.textColor = UIColor.lightGray
        placeholderLabel.text = placeholder

        placeholderLabel.sizeToFit()

        insertSubview(placeholderLabel, at: 0)
    }
}

а затем просто установите заполнитель в инспекторе удостоверений:

введите здесь описание изображения

person Community    schedule 09.12.2016


В быстрой 5. Работает нормально.

class BaseTextView: UITextView {

    // MARK: - Views
    private var placeholderLabel: UIlabel!

    // MARK: - Init
    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
        setupUI()
        startupSetup()
    }

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

    deinit {
        NotificationCenter.default.removeObserver(self)
    }    
}

// MARK: - Setup UI
private extension BaseTextView {
    func setupUI() {
        addPlaceholderLabel()

        textColor = .textColor
    }

    func addPlaceholderLabel() {
        placeholderLabel = BaseLabel(frame: .zero)
        placeholderLabel.translatesAutoresizingMaskIntoConstraints = false
        insertSubview(placeholderLabel, at: 0)

        placeholderLabel.alpha = 0
        placeholderLabel.numberOfLines = 0
        placeholderLabel.backgroundColor = .clear
        placeholderLabel.textColor = .lightTextColor
        placeholderLabel.lineBreakMode = .byWordWrapping
        placeholderLabel.isUserInteractionEnabled = false
        placeholderLabel.font = UIFont.openSansSemibold.withSize(12)

        placeholderLabel.topAnchor.constraint(equalTo: topAnchor, constant: 8).isActive = true
        placeholderLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: 5).isActive = true
        placeholderLabel.rightAnchor.constraint(lessThanOrEqualTo: rightAnchor, constant: -8).isActive = true
        placeholderLabel.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor, constant: -8).isActive = true
    }
}

// MARK: - Startup
private extension BaseTextView {
    func startupSetup() {
        addObservers()
        textChanged(nil)
        font = UIFont.openSansSemibold.withSize(12)
    }    

    func addObservers() {
        NotificationCenter.default.addObserver(self, selector: #selector(textChanged(_:)), name: UITextView.textDidChangeNotification, object: nil)
    }
}

// MARK: - Actions
private extension BaseTextView {
    @objc func textChanged(_ sender: Notification?) {
        UIView.animate(withDuration: 0.2) {
            self.placeholderLabel.alpha = self.text.count == 0 ? 1 : 0
        }    
    }
}

// MARK: - Public methods
extension BaseTextView {
    public func setPlaceholder(_ placeholder: String) {
        placeholderLabel.text = placeholder
    }
}
person Community    schedule 19.09.2019

Простой класс для поддержки заполнителей с атрибутами значков в UITextView PlaceholderTextView

@IBOutlet weak var tvMessage: PlaceholderTextView!
//  TODO: - Create Icon Text Attachment
let icon: NSTextAttachment = NSTextAttachment()
icon.image = UIImage(named: "paper-plane")
let iconString = NSMutableAttributedString(attributedString: NSAttributedString(attachment: icon))

tvMessage.icon = icon

//  TODO: - Attributes
let textColor = UIColor.gray
let lightFont = UIFont(name: "Helvetica-Light", size: tvMessage.font!.pointSize)
let italicFont = UIFont(name: "Helvetica-LightOblique", size: tvMessage.font!.pointSize)

//  TODO: - Placeholder Attributed String
let message = NSAttributedString(string: " " + "Personal Message", attributes: [ NSFontAttributeName: lightFont!,   NSForegroundColorAttributeName: textColor])
iconString.append(message)
// TODO: - Italic Placeholder Part
let option = NSAttributedString(string: " " + "Optional", attributes: [ NSFontAttributeName: italicFont!, NSForegroundColorAttributeName: textColor])
iconString.append(option)

tvMessage.attributedPlaceHolder = iconString

tvMessage.layoutSubviews()

Пусто  С текстом

person Community    schedule 30.09.2016

Самый простой способ изменить цвет текста заполнителя - через построитель интерфейса раскадровки XCode. Выберите интересующий UITextField и откройте инспектор удостоверений справа. Щелкните значок плюса в пользовательских атрибутах среды выполнения и добавьте новую строку с ключевым путем как _placeholderLabel.textColor, введите как цвет и значение желаемого цвета.

person Community    schedule 26.12.2016
comment
Не имеет ничего общего с вопросом или даже UITextView. - person TruMan1; 25.12.2017

Мне удалось добавить «заполнитель» в UITextView с ЧТОМ меньше кода. Вот что я сделал:

UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(60, 800, 200, 60)];
textView.text = @"Write characters here...";
textView.textColor=[UIColor grayColor];
textView.font = [UIFont fontWithName:@"Hevlatica" size:15];
textView.delegate=self;

Я предполагаю, что это не настоящий заполнитель, потому что вам нужно удалить текст, прежде чем писать, но это может помочь, если вы хотите что-то более простое.

person Community    schedule 02.08.2013
comment
Я думаю, что этот ответ несправедливо отвергнут. Это действительно изящное и умное решение. Кроме того, переопределение drawRect - довольно дорогостоящая операция. Несмотря на то, что требуется больше кода, чтобы скрыть и, возможно, переместить текстовое представление при изменении размера, я думаю, что это стоит повышения производительности. - person barndog; 19.11.2013