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

如何做到这一点?


当前回答

我建议使用pod 'UITextView+Placeholder'

pod 'UITextView+Placeholder'

在代码中

#import "UITextView+Placeholder.h"

////    

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

其他回答

另一个解决方案

import UIKit

protocol PlaceholderTextViewDelegate: class {

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

final class PlaceholderTextView: UITextView {

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

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

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

    var placeholderFont = UIFont.sfProRegular(28)

    fileprivate var placeholderLabel: UILabel?

    // MARK: - LifeCycle

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

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

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

    override func awakeFromNib() {
        super.awakeFromNib()

        self.delegate = self
    }
}

extension PlaceholderTextView: UITextViewDelegate {

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

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

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

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

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

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

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

        notifier?.placeholderTextViewDidChangeText(textView.text)
    }

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

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

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

结果:

这是我的版本的UITextView与占位符支持。斯威夫特4.2 https://gist.github.com/hlung/c5dda3a0c2087e5ae6c1fce8822c4713

一个支持占位符文本的UITextView子类。它使用另一个 UILabel显示占位符,当文本为空时显示。

基于这里已经给出的一些很好的建议,我能够将以下轻量级的、与接口生成器兼容的UITextView子类组合在一起,它是:

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

欢迎提出改进建议。

编辑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
    }
}

我用swift写了一个类。您可以在需要时导入这个类。

import UIKit

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

}

我找到了一个很容易模仿占位符的方法

在NIB或代码设置你的textView的textColor为lightGrayColor(大多数时候) 确保你的textView的委托链接到文件的所有者和实现UITextViewDelegate在你的头文件 将文本视图的默认文本设置为(例如:"Foobar placeholder") 实现:(BOOL) textViewShouldBeginEditing:(UITextView *)textView

编辑:

Changed if statements to compare tags rather than text. If the user deleted their text it was possible to also accidentally delete a portion of the place holder @"Foobar placeholder".This meant if the user re-entered the textView the following delegate method, -(BOOL) textViewShouldBeginEditing:(UITextView *) textView, it would not work as expected. I tried comparing by the color of the text in the if statement but found that light grey color set in interface builder is not the same as light grey color set in code with [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;
   }
}