我的应用程序使用UITextView。现在我想让UITextView有一个占位符,类似于你可以为UITextField设置的占位符。

如何做到这一点?


当前回答

我修改了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;
}

记住在可能改变字体和其他可能影响它们的东西的方法中设置_placeholderattributes = nil。如果没有问题的话,您可能还想跳过属性字典的“惰性”创建。

编辑:

如果你想让占位符在自动布局动画之后看起来更好,记得在setBounds的重写版本中调用setNeedsDisplay。

其他回答

抱歉添加了另一个答案,但我只是拉出了这样的东西,这创建了最接近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);
    }
}

还有一个简单的答案,使用CATextLayer。

添加CATextLayer到UITextView的层。 使用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)
        }
    }

}

我对发布的任何解决方案都不太满意,因为它们有点重。向视图中添加视图并不理想(尤其是在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:它现在是文档化的,并支持在接口生成器中使用。

更新11/24/13:指向新的回购。

下面是swift 3.1的代码

原始代码由杰森乔治在第一个答案。

不要忘记设置你的自定义类的TextView在接口生成器UIPlaceHolderTextView,然后设置占位符和占位符属性。

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

更简单,并且仍然考虑到用户在某些时候输入的一些文本

BOOL placeHolderTextVisible;

在viewDidLoad上,设置它为YES(或DidMoveToSuperview,或awakeFromNib)

然后,在 - (BOOL) textView:(UITextView*)textView shouldBeginEditing

    - (BOOL)textViewShouldBeginEditing:(UITextView *)textView;
{
   if (placeHolderTextVisible) {
    placeHolderTextVisible = NO;
    textView.text = @"";
   }
 return YES;
}