Заповнювач місця в UITextView


717

Моя програма використовує UITextView. Тепер я хочу, UITextViewщоб у заповнювача було схоже на те, яке ви можете встановити для UITextField.

Як це зробити?


TTTextEditor Three20 (сам за допомогою UITextField) підтримує текст заповнювача, а також зростає на висоту (він перетворюється на UITextView).
Джош Бенджамін


20
Як щодо використання категорії UITextView + заповнення? github.com/devxoul/UITextView-Placeholder
devxoul

2
Я віддаю перевагу рішенню @ devxoul, оскільки він використовує категорію, а не підклас. А також створює поле для опцій 'placeholder' (текст заповнювача та колір тексту) у інспектора IB. Тут використовуються деякі прийоми зв’язування. Який чудовий код
samthui7

Якщо ви використовуєте UITextViewрішення стає зовсім іншим. Ось пара рішень. Плаваючий заповнювач і підроблені
натовпні

Відповіді:


672

Я вніс декілька незначних змін у рішення 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

2
у деяких випадках (наприклад, сумісність iOS 5), потрібно перекрити пасту: - (недійсна) вставка: (id) відправник {[супер вставка: відправник]; [self textChanged: nil]; }
Мартін Улріх

3
Хороший матеріал! Нагадуючи про кращі практики для NSString (або будь-якого класу, який має еквівалент NSMutableXXX), властивість має бути "копіювати", а не "зберігати".
Олі

2
Як ви створюєте цей код? Я не бачу жодного тексту заповнювача, і нічого не очищається, коли я починаю вводити текст.
користувач798719

40
Це дуже, дуже погано написана реалізація. Ось надзвичайно чиста версія, яка також спостерігає за змінами диктантів: github.com/cbowns/MPTextView
cbowns

10
Не змінюйте ієрархію перегляду в drawRect.
Karmeye

634

Простий спосіб, просто створіть текст заповнення 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

і зробити батьківський клас a 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

і зробити батьківський клас a UITextViewDelegateперед тим, як включити ці методи, наприклад

class MyClass: UITextViewDelegate
{

}

1
Це чудово (я люблю просте) для 1 екрана з 1 UITextView. Причина більш складних рішень полягає в тому, що якщо у вас є більший додаток з МНОГО екранами та МНОГО UITextViews, ви не хочете робити це знову і знову. Ймовірно, ви хочете підкласи UITextView, щоб вони відповідали вашим потребам, а потім використовувати це.
ghostatron

42
Якщо хтось тут вводить текст "заповнення тексту ...", він також поводиться як текст заповнення. Також під час подання вам потрібно перевірити, чи всі ці критерії.
Anindya Sengupta

7
Текст заповнювача повинен відображатися навіть тоді, коли поле стає відповіддю, і цей метод не працює для цього.
phatmann

17
@jklp я б сказав , що «надмірно» шлях чистий і більш придатний для повторного використання ... і, схоже , це не саботаж з textатрибутом TextView , який є своїм родом nice..whereas цього метод змінює його
Cameron Askew

2
чому заклики стати FirstResponder і подати у відставкуFirstResponder методами делегату?
Адам Джонс

119

Я не був надто задоволений жодним із розміщених рішень, оскільки вони були трохи важкими. Додавання поглядів до перегляду насправді не ідеально (особливо в 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

Це набагато простіше, ніж інші, так як він не використовує підгляди (або не має витоків). Сміливо користуйтеся ним.

Оновлення 11/10/11: Тепер це документально підтверджено та підтримує використання в Interface Builder.

Оновлення 24.11.13: Вкажіть на нове репо.


Мені подобається ваше рішення, і я додав переосмислення, setTextщоб також оновити заповнення заповнення при зміні властивості тексту програмно: - (void) setText: (NSString *) рядок {[super setText: string]; [self _updateShouldDrawPlaceholder]; }
олегерет

1
Мені також подобається ваше рішення, але ви пропустили метод awakefromnib, тому ваш метод init не завжди буде викликаний. Я взяв це з одного з інших тут.
toxaq

Примітка - магічні цифри залежать від розміру шрифту. Точне позиціонування може бути обчислене з шрифту, але, мабуть, воно не варто для цієї реалізації. Правильне розташування заповнювача має бути 2px праворуч від положення курсора, коли немає тексту.
пам’ятки

Щоб вирішити завантаження від nib: ви, ймовірно, хочете додати - (id) initWithCoder: (NSCoder *) aDecoder; ініціалізатор для супроводу існуючого.
Йоріс Клюйверс

Це солодке. Однак notificationаргумент написано неправильно в останньому методі, і це занадто мала зміна для подання як редагування.
Філ Кальвін

53

Я знайшов собі дуже простий спосіб наслідувати місця, що займає місце

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

Редагувати:

Змінено, якщо висловлювання порівнюють теги, а не текст. Якщо користувач видалив їх текст, можна було також випадково видалити частину власника місця @"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;
   }
}

12
Мені дуже подобається такий підхід! Єдине, що я зробив би для редагування вище, це перенести реалізацію з методу textViewDidChange: та в метод textViewDidEndEditing:, щоб текст заповнювача повертався лише після того, як ви закінчите роботу з об'єктом.
підкова7

52

Що ви можете зробити, це налаштувати перегляд тексту з деяким початковим значенням у textвластивості та змінити textColorна [UIColor grayColor]або щось подібне. Потім, коли текст перегляду стане редагованим, очистіть текст і введіть курсор, і якщо текстове поле ще раз буде порожнім, поверніть його назад. Змініть колір на [UIColor blackColor]відповідний варіант.

Це не зовсім те саме, що функціонал заповнення місця в UITextField, але він близький.


9
Я завжди використовував lightGrayColor , який, здається, відповідає кольору тексту заповнення.
Білл

Я просто зараз це читаю, але хочу додати, що скидання кольору до чорного та скидання властивості тексту в textViewShouldBeginEditing: (UITextView *) textView працює дуже добре. Це дуже приємне і швидке рішення в порівнянні з рішеннями нижче (але все-таки елегантні рішення нижче, підкласинг uitextview. Набагато більш модульний).
Енріко Сусатіо

3
Щоправда, але це не імітує поведінку UITextField, який замінює текст тексту-заповнювача лише тоді, коли користувач щось вводить, а не коли починається редагування, і який додає заповнювач місця знову, коли другий перегляд порожній, а не коли редагування фактично закінчується.
Еш


45

Якщо комусь потрібне рішення для 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
    }
}

це нормально, але недостатньо добре. якщо користувач введе "Текст заповнювача ..." (
крайній

45

Просте рішення 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положенням, щоб правильно його встановити, але це не повинно бути занадто важким


1
Чудова відповідь, просте рішення. Я додав невелике вдосконалення, щоб анімувати лише тоді, коли альфа має змінитися: let alpha = CGFloat(textView.text.isEmpty ? 1.0 : 0.0) if alpha != lblPlaceholder.alpha { UIView.animate(withDuration: 0.3) { self.lblPlaceholder.alpha = alpha } }
Лучано Скловський

24

Я розширив відповідь 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

20

Рекомендую використовувати SZTextView.

https://github.com/glaszig/SZTextView

Додайте за замовчуванням UITextViewз, storyboardа потім змініть його власний клас на SZTextViewтакий, як нижче 👇👇👇👇

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

Тоді ви побачите дві нові опції у Attribute Inspector👇👇👇👇

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


20

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

Я знаю, що існує багато простих рішень, але мені подобається підхід підкласифікації UITextView, оскільки він багаторазовий, і мені не потрібно захаращувати класи, використовуючи його за допомогою механізмів.

Швидкий 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)
    }
}

Швидкий 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)
    }
}

Thks для того, щоб зробити швидку версію, ви можете пояснити, який сенс у тому, щоб розбудити метод wake-fromNib, який просто викликає супер?
П’єр

Він встановлює заповнювач, але не оновлюється, коли ви починаєте вводити текст.
Девід

Не маю на увазі, я поміщаю виклик сповіщень у awakeFromNib і він працює зараз.
Девід

12

ось як я це зробив:

UITextView2.h

#import <UIKit/UIKit.h>

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

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

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

@end

UITextView2.m

@implementation UITextView2

@synthesize placeholder, placeholderColor;

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

-(void)textChanged:(NSNotification*)notif {
    if ([[self placeholder] length]==0)
        return;
    if ([[self text] length]==0) {
        [[self viewWithTag:999] setAlpha:1];
    } else {
        [[self viewWithTag:999] setAlpha:0];
    }

}

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

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


@end

12

Ось спосіб простішого рішення, яке веде себе точно як заповнювач 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;
    }

}

(друга перевірка в іншому, якщо оператор - для випадку, коли нічого не введено, а користувач натискає на зворотну простір)

Просто встановіть свій клас як 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;
}

2
Річ у тім, що якщо користувач натискає десь посередині "тексту заповнення тексту", карета залишається там.
Олексій Сороколетов

10

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

або ви можете встановити його з какаоподів.


IQKeyboardManager дуже корисний і без кодів. Найкраща відповідь для мене!
NSDeveloper

1
Насправді я схвалюю і залишаю коментар з причини. Я не знаю, що IQTextView був доступний в IQKeyboardManager раніше.
NSDeveloper

У мене була проблема з делегатом. Я вилучив з класу перевищення делегата, і UITextViewDelegateпрацював просто чудово
TheoK

відповідь недооцінена
grantespo

10

Вибачте, щоб додати ще одну відповідь, але я просто зняв щось подібне, і це створило найближчий до 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);
    }
}

1
Я повинен був змінити порядок команд у першому, якщо заява if(textView.textColor == [UIColor lightGrayColor]){ textView.textColor = [UIColor blackColor]; textView.text = [textView.text substringToIndex: 1]; Інакше перший символ, що вводиться в перегляд тексту, був розміщений в кінці тексту
Flexicoder

7

Простий спосіб використовувати це в деякому рядку коду:

Візьміть одну мітку до 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;
}

7

Я змінив реалізацію Sam Soffes для роботи з 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, якщо вам подобається, що заповнювач місця розміщення добре виглядає після анімації авторозмітки тощо.


Я думаю, ви повинні додати insets.left до параметра offset x.
Karmeye

це не setFrame замість setBounds?
JakubKnejzlik

Ні! setFrame не викликається під час анімації макета, як здається.
Найлер

6

Ви також можете створити новий клас 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];
    }

}

1
Щоб отримати точну поведінку, слід намалювати власний заповнювач, змінивши команду drawRect: (намалюйте заповнювач лише у випадку, якщо! [Self isFirstResponder] && [[самотекст] довжина] == 0), і зателефонувавши setNeedsDisplay все-таки перейдіть у команду createFirstResponder та resignFirstResponder
rpetrich

6

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

Ви можете подивитися на мій підклас тут . Також включений демонстраційний проект.


6

На цю тему було багато відповідей, але ось таку версію я віддаю перевагу.

Він розширює існуючий 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, і тим, що нам (як розробникам) насправді потрібно в наших додатках. Ви пишете цей код один раз, додаєте його у свою бібліотеку файлів "helper" .m / .h, і з часом SDK насправді починає ставати менш розчарувальним.

(Я написав аналогічного помічника для додавання кнопки "ясно" до своїх UITextViews, інша річ, яка прикро існує в, UITextFieldале не в UITextView...)


Мені подобається, наскільки це чисто, але мені не подобається, як він вимагає другого UIView / UILabel (і насправді не успадковує атрибути / кольори / шрифт легко від UITextView). Хоча приємний внесок
маттсвен

6

Спочатку візьміть ярлик у .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 готовий!


6

Рекомендую використовувати pod "UITextView + заповнювач"

pod 'UITextView+Placeholder'

на свій код

#import "UITextView+Placeholder.h"

////    

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

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

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


Або ще краще, ви можете показати його ще раз, коли немає тексту: lblPlaceholder.hidden =! [TextView.text isEqualToString: @ ""];
Деспотович

Мені подобається це чисте рішення, жодна ін'єкція для перегляду тексту.
Ітачі

5

Неможливо створити заповнювач місця в 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;
 }  
}

5

Це ідеально імітує заповнювач 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
        }
    }
}

4

Ось ще один спосіб зробити це, який відтворює невеликий відступ 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];
    }
}

4

Давайте зробимо це легким

Створіть один 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;
    }

@end

Не забудьте підключити textView та UILabel до власника файлів від xib


4

Погляньте на UTPlaceholderTextView .

Це зручний підклас UITextView, який підтримує заповнювач заповнення, подібний до UITextField. Основні особливості:

  • Не використовує підперегляди
  • Не перекриває drawRect:
  • Заповнювач місць може мати довільну довжину і відображатися точно так само, як звичайний текст

4

Я прочитав усе це, але придумав дуже коротке рішення 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()
    }
}

Деякі проблеми тут. Не слід телефонувати layoutSubviews()безпосередньо. І ви не видалили спостерігача NotificationCenter.
Hlung

4

Я швидко написав клас. Ви можете імпортувати цей клас, коли це потрібно.

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

}

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


1
Це насправді одне з найчистіших рішень, які мені вдалося знайти, і я в кінцевому підсумку скористався цим. Особливо врахування вставних елементів та використання обмежень для етикетки - це приємний штрих, я не бачив багатьох (будь-яких?) Інших рішень, які приймають піклуватися про зміну меж, шрифтів, RTL тощо
Позначте
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.