UITextView中的占位符

708 cocoa-touch objective-c placeholder uitextview ios

我的应用程序使用了UITextView.现在我希望UITextView有一个类似于你可以设置的占位符UITextField.

这该怎么做?

Jas*_*rge 671

我对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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

  • 这是一个非常非常糟糕的实现.这是一个非常清洁的版本,也可以监视听写的变化:https://github.com/cbowns/MPTextView (38认同)
  • 不要修改drawRect中的视图层次结构. (9认同)
  • 好东西!提醒NSString(或任何具有NSMutableXXX等效类的类)的最佳实践,属性应该是"复制"而不是"保留". (3认同)
  • 在某些情况下(特别是iOS 5兼容性),它需要覆盖粘贴: - (void)paste:(id)sender {[super paste:sender]; [self textChanged:nil]; } (2认同)
  • 你如何实例化这段代码?我没有看到任何占位符文本,当我开始输入时没有任何清除. (2认同)
  • 我不明白为什么苹果公司没有向UITextView添加一个占位符属性。我不明白 (2认同)

小智 627

简单的方法,只需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];
}
Run Code Online (Sandbox Code Playgroud)

只记得myUITextView在创作时设置确切的文字,例如

UITextView *myUITextView = [[UITextView alloc] init];
myUITextView.delegate = self;
myUITextView.text = @"placeholder text here...";
myUITextView.textColor = [UIColor lightGrayColor]; //optional
Run Code Online (Sandbox Code Playgroud)

UITextViewDelegate在包括这些方法之前使父类成为例如

@interface MyClass () <UITextViewDelegate>
@end
Run Code Online (Sandbox Code Playgroud)

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()
}
Run Code Online (Sandbox Code Playgroud)

只记得myUITextView在创作时设置确切的文字,例如

 let myUITextView = UITextView.init()
 myUITextView.delegate = self
 myUITextView.text = "placeholder text here..."
 myUITextView.textColor = .lightGray
Run Code Online (Sandbox Code Playgroud)

UITextViewDelegate在包括这些方法之前使父类成为例如

class MyClass: UITextViewDelegate
{

}
Run Code Online (Sandbox Code Playgroud)

  • 如果有人在文本框中键入"占位符文本...",它的行为就像占位符文本一样.此外,在提交过程中,您需要检查所有这些标准. (40认同)
  • @jklp我认为"过度设计"的方式更清晰,更易于使用......看起来它不会篡改textview的`text`属性,这有点不错......就这种方法而言修改它 (17认同)
  • 即使字段成为响应者,占位符文本也应该显示,并且此方法不适用于此. (7认同)
  • 为什么在委托方法中调用becomeFirstResponder和resignFirstResponder? (2认同)

Sam*_*fes 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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

它比其他的简单得多,因为它不使用子视图(或有泄漏).随意使用它.

更新11/10/11:现在已记录并支持在Interface Builder中使用.

2013年11月24日更新:指向新的回购.


Tim*_*Tim 52

你可以做的是建立在对一些初始值的文本视图text属性,并更改textColor[UIColor grayColor]或类似的东西.然后,只要文本视图变得可编辑,清除文本并显示光标,如果文本字段再次为空,请将占位符文本放回原位.根据需要更改颜色[UIColor blackColor].

它与UITextField中的占位符功能不完全相同,但它很接近.

  • 我一直使用_lightGrayColor_,它似乎与占位符文本的颜色相匹配. (9认同)
  • 是的,但它并不模仿UITextField的行为,UITextField仅在用户输入内容时替换其占位符文本,而不是在编辑开始时替换占位符,并且在视图为空的情况下再次添加占位符,而不是在编辑实际完成时. (3认同)

vde*_*vos 51

我发现自己很容易模仿一个占位符

  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;
}
Run Code Online (Sandbox Code Playgroud)

当键盘返回并且[textView length] == 0时,也可以重置占位符文本

编辑:

只是为了让最后一部分更清晰 - 这里是你如何设置占位符文本:

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

  • 我非常喜欢这种方法!我要对上面的编辑做的唯一事情是将实现移出textViewDidChange:方法并移入textViewDidEndEditing:方法,这样占位符文本只有在你完成对象的处理后才会返回. (11认同)

Moh*_*ala 46

您可以设置标签上UITextView

[UITextView addSubView:lblPlaceHoldaer];
Run Code Online (Sandbox Code Playgroud)

并将其隐藏在TextViewdidChange方法上.

这是简单易行的方法.


der*_*ida 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
    }
}
Run Code Online (Sandbox Code Playgroud)


bud*_*ino 45

简单的Swift 3解决方案

加入UITextViewDelegate你的班级

yourTextView.delegate = self

placeholderLabel在里面创建并定位它yourTextView

现在只是动画placeholderLabel.alphatextViewDidChange:

  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
      }
    }
  }
Run Code Online (Sandbox Code Playgroud)

你可能不得不玩placeholderLabel位置设置正确,但这不应该太难


Mat*_*att 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;
}
Run Code Online (Sandbox Code Playgroud)

只记得在创建时使用确切的文本设置myUITextView,例如

UITextView *myUITextView = [[UITextView alloc] init];
myUITextView.delegate = self;
myUITextView.text = @"What's happening?";
myUITextView.textColor = [UIColor lightGrayColor]; //optional
Run Code Online (Sandbox Code Playgroud)

并在包含这些方法之前使父类成为UITextView委托

@interface MyClass () <UITextViewDelegate>
@end
Run Code Online (Sandbox Code Playgroud)


Vai*_*ran 20

我建议使用SZTextView.

https://github.com/glaszig/SZTextView

添加默认UITextViewstoryboard,然后改变它的定制类SZTextView下面喜欢

在此输入图像描述

然后你会看到两个新选项 Attribute Inspector

在此输入图像描述


cle*_*ght 19

下面是一个Swift端口的"SAMTextView"ObjC代码,作为对该问题的第一批回复之一.我在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)
    }
}
Run Code Online (Sandbox Code Playgroud)

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)
    }
}
Run Code Online (Sandbox Code Playgroud)


bcd*_*bcd 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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)


ani*_*ing 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;
    }

}
Run Code Online (Sandbox Code Playgroud)

(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;
}
Run Code Online (Sandbox Code Playgroud)

  • 问题是,如果用户点击“占位符文本”中间的某处,则插入符号将停留在该位置。 (2认同)

Sha*_*ali 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);
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 9

您好,您可以使用IQKeyboard Manager中提供的IQTextView,它使用简单,只需将textview的set类集成到IQTextView,您可以使用其属性设置您想要的颜色的占位符标签.您可以从IQKeyboardManager下载库

或者你可以从cocoapods安装它.

  • 实际上,我为此投票并发表评论。我不知道 IQTextView 之前在 IQKeyboardManager 中可用。 (2认同)

Sac*_*hin 7

在某些代码行中使用它的简单方法:

将一个标签带到.nib中的UITextView,将此标签连接到您的代码,之后.

- (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;
}
Run Code Online (Sandbox Code Playgroud)


Nai*_*ler 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;
}
Run Code Online (Sandbox Code Playgroud)

请记住设置_placeholderAttribues = nil可能会更改可能影响它们的字体和其他符号的方法.您可能还想跳过属性字典的"懒惰"制作,如果这不会给您带来麻烦.

编辑:

如果您希望占位符在自动布局动画之后看起来很好,请记住在重写的setBounds版本中调用setNeedsDisplay.


Ama*_*mer 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;
    }

}
Run Code Online (Sandbox Code Playgroud)

在您的委托中,添加以下内容:

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

}
Run Code Online (Sandbox Code Playgroud)


gca*_*amp 6

我创建了自己的'UITextView'子类版本.我喜欢Sam Soffes使用通知的想法,但我不喜欢drawRect:overwrite.对我来说似乎有点矫枉过正.我想我做了一个非常干净的实现.

你可以在这里查看我的子类.还包括一个演示项目.


Mik*_*ill 6

这个帖子有很多答案,但这是我喜欢的版本.

扩展了现有的UITextView类,因此很容易重复使用,并且它不会拦截像textViewDidChange(如果它们已经在其他地方拦截这些事件而破坏用户代码)这样​​的事件.

使用我的代码(如下所示),您可以轻松地为这样的任何一个添加占位符UITextViews:

self.textViewComments.placeholder = @"(Enter some comments here.)";
Run Code Online (Sandbox Code Playgroud)

当您设置此新占位符值时,它会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
Run Code Online (Sandbox Code Playgroud)

...以及包含以下内容的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
Run Code Online (Sandbox Code Playgroud)

是的,这是很多代码,但是一旦你将它添加到你的项目并包含.h文件......

#import "UITextViewHelper.h"
Run Code Online (Sandbox Code Playgroud)

...你可以很容易地使用占位符UITextViews.

虽然有一个问题.

如果你这样做:

self.textViewComments.placeholder = @"(Enter some comments here.)";
self.textViewComments.text = @"Ooooh, hello there";
Run Code Online (Sandbox Code Playgroud)

...占位符将显示文本的顶部.设置text值时,不会调用任何常规通知,因此我无法确定如何调用我的函数来决定是否显示/隐藏占位符.

解决方案是设置textValue而不是text:

self.textViewComments.placeholder = @"(Enter some comments here.)";
self.textViewComments.textValue = @"Ooooh, hello there";
Run Code Online (Sandbox Code Playgroud)

或者,您可以设置text值,然后调用checkIfNeedToDisplayPlaceholder.

self.textViewComments.text = @"Ooooh, hello there";
[self.textViewComments checkIfNeedToDisplayPlaceholder];
Run Code Online (Sandbox Code Playgroud)

我喜欢这样的解决方案,因为它们"填补了Apple提供给我们的东西之间的空白",以及我们(作为开发人员)在我们的应用程序中实际需要的东西.您编写此代码一次,将其添加到"helper".m/.h文件库中,随着时间的推移,SDK实际上开始变得不那么令人沮丧了.

(我写了一个类似的帮助器,为我的UITextViews添加了一个"清除"按钮,另一件令人讨厌的东西,UITextField但不是UITextView......)


小智 6

首先在.h文件中加一个标签.

我在这里

UILabel * lbl;
Run Code Online (Sandbox Code Playgroud)

然后在viewDidLoad下的.m中声明它

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];
Run Code Online (Sandbox Code Playgroud)

textview是我的TextView.

现在宣布

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

 if (![textView hasText]){

    lbl.hidden = NO;

 }
 else{
    lbl.hidden = YES;
 }

}
Run Code Online (Sandbox Code Playgroud)

您的Textview占位符已准备就绪!


The*_*mer 6

我推荐使用pod'UITextView + Placeholder'

pod 'UITextView+Placeholder'
Run Code Online (Sandbox Code Playgroud)

在你的代码上

#import "UITextView+Placeholder.h"

////    

UITextView *textView = [[UITextView alloc] init];
textView.placeholder = @"How are you?";
textView.placeholderColor = [UIColor lightGrayColor];
Run Code Online (Sandbox Code Playgroud)


The*_*eil 6

基于这里的一些很好的建议,我能够将以下轻量级的、与 Interface-Builder 兼容的子类放在一起UITextView,其中:

  • 包括可配置的占位符文本,样式与UITextField.
  • 不需要任何额外的子视图或约束。
  • 不需要来自 ViewController 的任何委托或其他行为。
  • 不需要任何通知。
  • 使该文本与查看字段text属性的任何外部类完全分开。

欢迎提出改进建议。

编辑 1:如果以编程方式设置实际文本,则更新以重置占位符格式。

编辑 2:现在可以以编程方式检索占位符文本颜色。

斯威夫特 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
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 感谢您的反馈@AfnanAhmad。我现在进行了一些编辑,以更好地支持以编程方式设置的文本。最好不要创建设计为私有的公共组件。 (2认同)

vat*_*tti 5

    - (void)textViewDidChange:(UITextView *)textView
{
    placeholderLabel.hidden = YES;
}
Run Code Online (Sandbox Code Playgroud)

在textview上添加标签.


PJR*_*PJR 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];
    }
    }
Run Code Online (Sandbox Code Playgroud)

或者您可以在textview中添加标签

       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];
Run Code Online (Sandbox Code Playgroud)

并设定

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

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


Ami*_*itP 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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)