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

如何做到这一点?


当前回答

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

或者你可以添加标签在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];

并设置

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

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

其他回答

你能做的是在文本属性中设置文本视图的初始值,并将textColor更改为[UIColor grayColor]或类似的东西。然后,每当文本视图变为可编辑时,清除文本并显示游标,如果文本字段再次为空,则将占位符文本放回。根据需要将颜色更改为[UIColor blackColor]。

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

下面是“SAMTextView”ObjC代码的Swift端口,作为对这个问题的第一批回答之一。我在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)
    }
}

还有一个简单的答案,使用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)
        }
    }

}

在尝试了一些建议的方法后,我写了一个更清晰的实现,并将其发布到Github上。拉请求和问题是欢迎的。

与其他方法相比,这里提出了一些关键的改进:

不会在drawRect:中分配一个UILabel。(请不要这样做。) 不将文本视图的当前文本与所需的占位符进行比较以交换颜色。 当听写处于活动状态时隐藏占位符(如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。