文本视图(UITextView)占位符Swift

时间:2014-12-26 02:12:05

标签: ios swift uitextview placeholder

我正在制作一个使用UITextView的应用程序。现在我希望文本视图的占位符类似于您可以为文本字段设置的占位符。你会如何使用swift实现这一目标。

有谁知道怎么做?

45 个答案:

答案 0 :(得分:529)

更新了Swift 4

UITextView本身并不具有占位符属性,因此您必须使用UITextViewDelegate方法以编程方式创建和操作。我建议使用下面的解决方案#1或#2,具体取决于所需的行为。

注意:对于任一解决方案,请向该类添加UITextViewDelegate并设置textView.delegate = self以使用文本视图的委托方法。


解决方案#1 - 如果您希望占位符在用户选择文本视图后立即消失:

首先将UITextView设置为包含占位符文本,并将其设置为浅灰色,以模仿UITextField占位符文本的外观。可以在viewDidLoad或文本视图的创建中执行此操作。

textView.text = "Placeholder"
textView.textColor = UIColor.lightGray

然后,当用户开始编辑文本视图时,如果文本视图包含占位符(即,如果其文本颜色为浅灰色),请清除占位符文本并将文本颜色设置为黑色以容纳用户的条目。

func textViewDidBeginEditing(_ textView: UITextView) {
    if textView.textColor == UIColor.lightGray {
        textView.text = nil
        textView.textColor = UIColor.black
    }
}

然后,当用户完成编辑文本视图并将其作为第一响应者重新签名时,如果文本视图为空,则通过重新添加占位符文本并将其颜色设置为浅灰色来重置其占位符。

func textViewDidEndEditing(_ textView: UITextView) {
    if textView.text.isEmpty {
        textView.text = "Placeholder"
        textView.textColor = UIColor.lightGray
    }
}

解决方案#2 - 如果您希望占位符在文本视图为空时显示,即使选择了文本视图: < / p>

首先在viewDidLoad

中设置占位符
textView.text = "Placeholder"
textView.textColor = UIColor.lightGray

textView.becomeFirstResponder()

textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)

(注意:由于OP希望在视图加载后立即选择文本视图,因此我将文本视图选择合并到上面的代码中。如果这不是您想要的行为,并且您不希望选择文本视图查看加载,从上面的代码块中删除最后两行。)

然后使用shouldChangeTextInRange UITextViewDelegate方法,如下所示:

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

    // Combine the textView text and the replacement text to
    // create the updated text string
    let currentText:String = textView.text
    let updatedText = (currentText as NSString).replacingCharacters(in: range, with: text)

    // If updated text view will be empty, add the placeholder
    // and set the cursor to the beginning of the text view
    if updatedText.isEmpty {

        textView.text = "Placeholder"
        textView.textColor = UIColor.lightGray

        textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)
    }

    // Else if the text view's placeholder is showing and the
    // length of the replacement string is greater than 0, set 
    // the text color to black then set its text to the
    // replacement string
     else if textView.textColor == UIColor.lightGray && !text.isEmpty {
        textView.textColor = UIColor.black
        textView.text = text
    }

    // For every other case, the text should change with the usual
    // behavior...
    else {
        return true
    }

    // ...otherwise return false since the updates have already
    // been made
    return false
}

并且还实现textViewDidChangeSelection以防止用户在占位符可见时更改光标的位置。 (注意:在加载视图之前调用textViewDidChangeSelection,因此只有在窗口可见时才检查文本视图的颜色):

func textViewDidChangeSelection(_ textView: UITextView) {
    if self.view.window != nil {
        if textView.textColor == UIColor.lightGray {
            textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)
        }
    }
}

答案 1 :(得分:202)


浮动占位符


在文本视图上方放置占位符标签,设置字体,颜色并通过跟踪文本视图字符数的更改来管理占位符可见性,这是​​简单,安全和可靠的。

斯威夫特3:

class NotesViewController : UIViewController, UITextViewDelegate {

    @IBOutlet var textView : UITextView!
    var placeholderLabel : UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        textView.delegate = self
        placeholderLabel = UILabel()
        placeholderLabel.text = "Enter some text..."
        placeholderLabel.font = UIFont.italicSystemFont(ofSize: (textView.font?.pointSize)!)
        placeholderLabel.sizeToFit()
        textView.addSubview(placeholderLabel)
        placeholderLabel.frame.origin = CGPoint(x: 5, y: (textView.font?.pointSize)! / 2)
        placeholderLabel.textColor = UIColor.lightGray
        placeholderLabel.isHidden = !textView.text.isEmpty
    }

    func textViewDidChange(_ textView: UITextView) {
        placeholderLabel.isHidden = !textView.text.isEmpty
    }
}

Swift 2:相同,但:italicSystemFontOfSize(textView.font.pointSize)UIColor.lightGrayColor


答案 2 :(得分:27)

强烈建议您使用KMPlaceholderTextView库。使用起来非常简单。

答案 3 :(得分:21)

<强>夫特:

以编程方式或通过Interface Builder添加文本视图,如果是最后一个,则创建插座:

@IBOutlet weak var yourTextView: UITextView!

请添加委托(UITextViewDelegate):

class ViewController: UIViewController, UITextViewDelegate {

在viewDidLoad方法中,添加以下内容:

override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

    yourTextView.delegate = self
    yourTextView.text = "Placeholder text goes right here..."
    yourTextView.textColor = UIColor.lightGray

现在让我介绍一下神奇的部分,添加这个功能:

func textViewDidBeginEditing(_ textView: UITextView) {

    if yourTextView.textColor == UIColor.lightGray {
        yourTextView.text = ""
        yourTextView.textColor = UIColor.black
    }
}

请注意,这将在编辑开始时执行,我们将使用color属性检查条件以告知状态。 将文本设置为nil我不建议。在此之后,我们将文本颜色设置为所需的颜色,在本例中为黑色。

现在也添加此功能:

func textViewDidEndEditing(_ textView: UITextView) {

    if yourTextView.text == "" {

        yourTextView.text = "Placeholder text ..."
        yourTextView.textColor = UIColor.lightGray
    }
}

让我坚持,不要与nil比较,我已经尝试过了,但是它不会起作用。然后,我们将值设置回占位符样式,并将颜色设置回占位符颜色,因为这是签入textViewDidBeginEditing的条件。

答案 4 :(得分:14)

我是通过使用两种不同的文本视图来完成的:

  1. 用作占位符的后台中的一个。
  2. 用户实际输入的前景中的一个(具有透明背景)。
  3. 这个想法是,一旦用户开始在前景视图中输入内容,背景中的占位符就会消失(如果用户删除了所有内容,则会重新出现)。因此它的行为与单行文本字段的占位符完全相同。

    这是我用过的代码。请注意,descriptionField是用户键入的字段,descriptionPlaceholder是后台的字段。

    func textViewDidChange(descriptionField: UITextView) {
        if descriptionField.text.isEmpty == false {
            descriptionPlaceholder.text = ""
        } else {
            descriptionPlaceholder.text = descriptionPlaceholderText
        }
    }
    

答案 5 :(得分:12)

使用此扩展名这是在UITextView中设置占位符的最佳方法。 但请确保您已将代理附加到TextView。您可以像这样设置Place holder: -

yourTextView.placeholder = "Placeholder" 

extension UITextView :UITextViewDelegate
{

    /// Resize the placeholder when the UITextView bounds change
    override open var bounds: CGRect {
        didSet {
            self.resizePlaceholder()
        }
    }

    /// The UITextView placeholder text
    public var placeholder: String? {
        get {
            var placeholderText: String?

            if let placeholderLabel = self.viewWithTag(100) as? UILabel {
                placeholderText = placeholderLabel.text
            }

            return placeholderText
        }
        set {
            if let placeholderLabel = self.viewWithTag(100) as! UILabel? {
                placeholderLabel.text = newValue
                placeholderLabel.sizeToFit()
            } else {
                self.addPlaceholder(newValue!)
            }
        }
    }

    /// When the UITextView did change, show or hide the label based on if the UITextView is empty or not
    ///
    /// - Parameter textView: The UITextView that got updated
    public func textViewDidChange(_ textView: UITextView) {
        if let placeholderLabel = self.viewWithTag(100) as? UILabel {
            placeholderLabel.isHidden = self.text.characters.count > 0
        }
    }

    /// Resize the placeholder UILabel to make sure it's in the same position as the UITextView text
    private func resizePlaceholder() {
        if let placeholderLabel = self.viewWithTag(100) as! UILabel? {
            let labelX = self.textContainer.lineFragmentPadding
            let labelY = self.textContainerInset.top - 2
            let labelWidth = self.frame.width - (labelX * 2)
            let labelHeight = placeholderLabel.frame.height

            placeholderLabel.frame = CGRect(x: labelX, y: labelY, width: labelWidth, height: labelHeight)
        }
    }

    /// Adds a placeholder UILabel to this UITextView
    private func addPlaceholder(_ placeholderText: String) {
        let placeholderLabel = UILabel()

        placeholderLabel.text = placeholderText
        placeholderLabel.sizeToFit()

        placeholderLabel.font = self.font
        placeholderLabel.textColor = UIColor.lightGray
        placeholderLabel.tag = 100

        placeholderLabel.isHidden = self.text.characters.count > 0

        self.addSubview(placeholderLabel)
        self.resizePlaceholder()
        self.delegate = self
    }
}

答案 6 :(得分:6)

视图加载中的SET值

    txtVw!.autocorrectionType = UITextAutocorrectionType.No
    txtVw!.text = "Write your Placeholder"
    txtVw!.textColor = UIColor.lightGrayColor()



func textViewDidBeginEditing(textView: UITextView) {
    if (txtVw?.text == "Write your Placeholder")

    {
        txtVw!.text = nil
        txtVw!.textColor = UIColor.blackColor()
    }
}

func textViewDidEndEditing(textView: UITextView) {
    if txtVw!.text.isEmpty
    {
        txtVw!.text = "Write your Placeholder"
        txtVw!.textColor = UIColor.lightGrayColor()
    }
    textView.resignFirstResponder()
}

答案 7 :(得分:5)

另一个解决方案(Swift 3):

import UIKit

protocol PlaceholderTextViewDelegate {
    func placeholderTextViewDidChangeText(_ text:String)
    func placeholderTextViewDidEndEditing(_ text:String)
}

final class PlaceholderTextView: UITextView {

    var notifier:PlaceholderTextViewDelegate?

    var placeholder: String? {
        didSet {
            placeholderLabel?.text = placeholder
        }
    }
    var placeholderColor = UIColor.lightGray
    var placeholderFont = UIFont.appMainFontForSize(14.0) {
        didSet {
            placeholderLabel?.font = placeholderFont
        }
    }

    fileprivate var placeholderLabel: UILabel?

    // MARK: - LifeCycle

    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
        NotificationCenter.default.addObserver(self, selector: #selector(PlaceholderTextView.textDidChangeHandler(notification:)), name: .UITextViewTextDidChange, object: nil)

        placeholderLabel = UILabel()
        placeholderLabel?.textColor = placeholderColor
        placeholderLabel?.text = placeholder
        placeholderLabel?.textAlignment = .left
        placeholderLabel?.numberOfLines = 0
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        placeholderLabel?.font = placeholderFont

        var height:CGFloat = placeholderFont.lineHeight
        if let data = placeholderLabel?.text {

            let expectedDefaultWidth:CGFloat = bounds.size.width
            let fontSize:CGFloat = placeholderFont.pointSize

            let textView = UITextView()
            textView.text = data
            textView.font = UIFont.appMainFontForSize(fontSize)
            let sizeForTextView = textView.sizeThatFits(CGSize(width: expectedDefaultWidth,
                                                               height: CGFloat.greatestFiniteMagnitude))
            let expectedTextViewHeight = sizeForTextView.height

            if expectedTextViewHeight > height {
                height = expectedTextViewHeight
            }
        }

        placeholderLabel?.frame = CGRect(x: 5, y: 0, width: bounds.size.width - 16, height: height)

        if text.isEmpty {
            addSubview(placeholderLabel!)
            bringSubview(toFront: placeholderLabel!)
        } else {
            placeholderLabel?.removeFromSuperview()
        }
    }

    func textDidChangeHandler(notification: Notification) {
        layoutSubviews()
    }

}

extension PlaceholderTextView : UITextViewDelegate {
    // MARK: - UITextViewDelegate
    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        if(text == "\n") {
            textView.resignFirstResponder()
            return false
        }
        return true
    }

    func textViewDidChange(_ textView: UITextView) {
        notifier?.placeholderTextViewDidChangeText(textView.text)
    }

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

结果

enter image description here

答案 8 :(得分:4)

一个对我有用的简单快速的解决方案是:

@IBDesignable
class PlaceHolderTextView: UITextView {

    @IBInspectable var placeholder: String = "" {
         didSet{
             updatePlaceHolder()
        }
    }

    @IBInspectable var placeholderColor: UIColor = UIColor.gray {
        didSet {
            updatePlaceHolder()
        }
    }

    private var originalTextColor = UIColor.darkText
    private var originalText: String = ""

    private func updatePlaceHolder() {

        if self.text == "" || self.text == placeholder  {

            self.text = placeholder
            self.textColor = placeholderColor
            if let color = self.textColor {

                self.originalTextColor = color
            }
            self.originalText = ""
        } else {
            self.textColor = self.originalTextColor
            self.originalText = self.text
        }

    }

    override func becomeFirstResponder() -> Bool {
        let result = super.becomeFirstResponder()
        self.text = self.originalText
        self.textColor = self.originalTextColor
        return result
    }
    override func resignFirstResponder() -> Bool {
        let result = super.resignFirstResponder()
        updatePlaceHolder()

        return result
    }
}

答案 9 :(得分:3)

Swift 3.2

extension EditProfileVC:UITextViewDelegate{

    func textViewDidBeginEditing(_ textView: UITextView) {
        if textView.textColor == UIColor.lightGray {
            textView.text = nil
            textView.textColor = UIColor.black
       }
    }
    func textViewDidEndEditing(_ textView: UITextView) {
        if textView.text.isEmpty {
            textView.text = "Placeholder"
            textView.textColor = UIColor.lightGray
        }
    }
}

首先,当用户开始编辑textViewDidBeginEditing调用,然后检查文本灰色的颜色是否意味着用户没有写任何内容然后设置为textview nil并将颜色更改为黑色以便用户发短信。

当用户结束编辑textViewDidEndEditing时,如果用户没有在textview中写任何内容,那么文本设置为灰色,带有文字&#34; PlaceHolder&#34;

答案 10 :(得分:3)

我很惊讶没有人提到NSTextStorageDelegateUITextViewDelegate的方法只会由用户互动触发,但不会以编程方式触发。例如。当您以编程方式设置文本视图的text属性时,您必须自己设置占位符的可见性,因为不会调用委托方法。

但是,使用NSTextStorageDelegate textStorage(_:didProcessEditing:range:changeInLength:)方法,即使以编程方式完成,您也会收到有关文本更改的通知。只需像这样分配:

textView.textStorage.delegate = self

(在UITextView中,此委托属性默认为nil,因此不会影响任何默认行为。)

将它与UILabel技术@clearlight演示结合起来,可以轻松地将整个UITextView的{​​{1}}实现包装到扩展中。

placeholder

请注意,使用名为extension UITextView { private class PlaceholderLabel: UILabel { } private var placeholderLabel: PlaceholderLabel { if let label = subviews.compactMap( { $0 as? PlaceholderLabel }).first { return label } else { let label = PlaceholderLabel(frame: .zero) label.font = font addSubview(label) return label } } @IBInspectable var placeholder: String { get { return subviews.compactMap( { $0 as? PlaceholderLabel }).first?.text ?? "" } set { let placeholderLabel = self.placeholderLabel placeholderLabel.text = newValue placeholderLabel.numberOfLines = 0 let width = frame.width - textContainer.lineFragmentPadding * 2 let size = placeholderLabel.sizeThatFits(CGSize(width: width, height: .greatestFiniteMagnitude)) placeholderLabel.frame.size.height = size.height placeholderLabel.frame.size.width = width placeholderLabel.frame.origin = CGPoint(x: textContainer.lineFragmentPadding, y: textContainerInset.top) textStorage.delegate = self } } } extension UITextView: NSTextStorageDelegate { public func textStorage(_ textStorage: NSTextStorage, didProcessEditing editedMask: NSTextStorageEditActions, range editedRange: NSRange, changeInLength delta: Int) { if editedMask.contains(.editedCharacters) { placeholderLabel.isHidden = !text.isEmpty } } } 的私有(嵌套)类。它根本没有实现,但它为我们提供了一种识别占位符标签的方法,它更加“快速”。而不是使用PlaceholderLabel属性。

通过这种方法,您仍然可以将tag的代理人分配给其他人。

您甚至不必更改文字观看次数&#39;类。只需添加扩展名,您就可以为项目中的每个UITextView分配占位符字符串,即使在Interface Builder中也是如此。

出于清晰的原因,我省略了UITextView属性的实现,但是它可以仅使用类似的计算变量到placeholderColor的几行来实现。

答案 11 :(得分:3)

我尝试从clearlightanswer开始使代码更方便。

extension UITextView{

    func setPlaceholder() {

        let placeholderLabel = UILabel()
        placeholderLabel.text = "Enter some text..."
        placeholderLabel.font = UIFont.italicSystemFont(ofSize: (self.font?.pointSize)!)
        placeholderLabel.sizeToFit()
        placeholderLabel.tag = 222
        placeholderLabel.frame.origin = CGPoint(x: 5, y: (self.font?.pointSize)! / 2)
        placeholderLabel.textColor = UIColor.lightGray
        placeholderLabel.isHidden = !self.text.isEmpty

        self.addSubview(placeholderLabel)
    }

    func checkPlaceholder() {
        let placeholderLabel = self.viewWithTag(222) as! UILabel
        placeholderLabel.isHidden = !self.text.isEmpty
    }

}

使用

override func viewDidLoad() {
    textView.delegate = self
    textView.setPlaceholder()
}

func textViewDidChange(_ textView: UITextView) {
    textView.checkPlaceholder()
}

答案 12 :(得分:3)

我不知道为什么人们过多地使这个问题复杂化......这很简单直接。这是UITextView的子类,提供所请求的功能。

- (void)customInit
{
    self.contentMode = UIViewContentModeRedraw;
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:nil];
}

    - (void)textChanged:(NSNotification *)notification
    {
        if (notification.object == self) {
            if(self.textStorage.length != 0 || !self.textStorage.length) {
                [self setNeedsDisplay];
            }
        }
    }


    #pragma mark - Setters

    - (void)setPlaceholderText:(NSString *)placeholderText withFont:(UIFont *)font
    {
        self.placeholderText = placeholderText;
        self.placeholderTextFont = font;

    }



    - (void)drawRect:(CGRect)rect
    {
        [super drawRect:rect];
        [[UIColor lightGrayColor] setFill];

        if (self.textStorage.length != 0) {
            return;
        }

        CGRect inset = CGRectInset(rect, 8, 8);//Default rect insets for textView
        NSDictionary *attributes =  @{NSFontAttributeName: self.placeholderTextFont, NSForegroundColorAttributeName: [UIColor grayColor]};
        [self.placeholderText drawInRect:inset withAttributes:attributes];
    }`

答案 13 :(得分:2)

无需添加任何第三方库。只需使用下面的代码...

class SubmitReviewVC : UIViewController, UITextViewDelegate {

@IBOutlet var txtMessage : UITextView!
var lblPlaceHolder : UILabel!

override func viewDidLoad() {
    super.viewDidLoad()

    txtMessage.delegate = self
    lblPlaceHolder = UILabel()
    lblPlaceHolder.text = "Enter message..."
    lblPlaceHolder.font = UIFont.systemFont(ofSize: txtMessage.font!.pointSize)
    lblPlaceHolder.sizeToFit()
    txtMessage.addSubview(lblPlaceHolder)
    lblPlaceHolder.frame.origin = CGPoint(x: 5, y: (txtMessage.font?.pointSize)! / 2)
    lblPlaceHolder.textColor = UIColor.lightGray
    lblPlaceHolder.isHidden = !txtMessage.text.isEmpty
}

func textViewDidChange(_ textView: UITextView) {
    lblPlaceHolder.isHidden = !textView.text.isEmpty
}

}

答案 14 :(得分:2)

Swift 3.1

此扩展适用于我:https://github.com/devxoul/UITextView-Placeholder

以下是代码段:

通过pod安装它:

pod 'UITextView+Placeholder', '~> 1.2'

将其导入您的班级

import UITextView_Placeholder

并将placeholder属性添加到您已创建的UITextView

textView.placeholder = "Put some detail"

多数民众赞成...... 在这里看起来如何(第三个框是UITextViewenter image description here

答案 15 :(得分:2)

Swift 5.2

独立类

如果您想要一个可以在任何地方使用的类,请使用它

import UIKit
class PlaceHolderTextView:UITextView, UITextViewDelegate{
var placeholderText = "placeholderText"

override func willMove(toSuperview newSuperview: UIView?) {
    textColor = .lightText
    delegate = self
}

func textViewDidBeginEditing(_ textView: UITextView) {
    if textView.text == placeholderText{
        placeholderText = textView.text
        textView.text = ""
        textView.textColor = .darkText
    }
}

func textViewDidEndEditing(_ textView: UITextView) {
    if textView.text == ""{
        textView.text = placeholderText
        textColor = .lightText
    }
}    
}

这里的关键是willMove(toSuperView:)函数,因为它允许您在将视图添加到另一个视图的层次结构之前对其进行设置(类似于ViewControllers中的viewDidLoad / viewWillAppear)

答案 16 :(得分:2)

这是我解决此问题的方法( Swift 4 ):

该想法是使最简单的解决方案成为可能,该解决方案允许使用不同颜色的占位符,将大小调整为占位符的大小,不会覆盖delegate,同时保持所有UITextView函数正常工作。

import UIKit

class PlaceholderTextView: UITextView {
    var placeholderColor: UIColor = .lightGray
    var defaultTextColor: UIColor = .black

    private var isShowingPlaceholder = false {
        didSet {
            if isShowingPlaceholder {
                text = placeholder
                textColor = placeholderColor
            } else {
                textColor = defaultTextColor
            }
        }
    }

    var placeholder: String? {
        didSet {
            isShowingPlaceholder = !hasText
        }
    }

    @objc private func textViewDidBeginEditing(notification: Notification) {
        textColor = defaultTextColor
        if isShowingPlaceholder { text = nil }
    }

    @objc private func textViewDidEndEditing(notification: Notification) {
        isShowingPlaceholder = !hasText
    }

    // MARK: - Construction -
    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
        setup()
    }

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

    private func setup() {
        NotificationCenter.default.addObserver(self, selector: #selector(textViewDidBeginEditing(notification:)), name: UITextView.textDidBeginEditingNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(textViewDidEndEditing(notification:)), name: UITextView.textDidEndEditingNotification, object: nil)
    }

    // MARK: - Destruction -
    deinit { NotificationCenter.default.removeObserver(self) }
}

答案 17 :(得分:2)

以下是我用于完成这项工作的内容。

@IBDesignable class UIPlaceholderTextView: UITextView {

    var placeholderLabel: UILabel?

    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
        sharedInit()
    }

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

    override func prepareForInterfaceBuilder() {
        sharedInit()
    }

    func sharedInit() {
        refreshPlaceholder()
        NotificationCenter.default.addObserver(self, selector: #selector(textChanged), name: .UITextViewTextDidChange, object: nil)
    }

    @IBInspectable var placeholder: String? {
        didSet {
            refreshPlaceholder()
        }
    }

    @IBInspectable var placeholderColor: UIColor? = .darkGray {
        didSet {
            refreshPlaceholder()
        }
    }

    @IBInspectable var placeholderFontSize: CGFloat = 14 {
        didSet {
            refreshPlaceholder()
        }
    }

    func refreshPlaceholder() {
        if placeholderLabel == nil {
            placeholderLabel = UILabel()
            let contentView = self.subviews.first ?? self

            contentView.addSubview(placeholderLabel!)
            placeholderLabel?.translatesAutoresizingMaskIntoConstraints = false

            placeholderLabel?.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: textContainerInset.left + 4).isActive = true
            placeholderLabel?.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: textContainerInset.right + 4).isActive = true
            placeholderLabel?.topAnchor.constraint(equalTo: contentView.topAnchor, constant: textContainerInset.top).isActive = true
            placeholderLabel?.bottomAnchor.constraint(lessThanOrEqualTo: contentView.bottomAnchor, constant: textContainerInset.bottom)
        }
        placeholderLabel?.text = placeholder
        placeholderLabel?.textColor = placeholderColor
        placeholderLabel?.font = UIFont.systemFont(ofSize: placeholderFontSize)
    }

    @objc func textChanged() {
        if self.placeholder?.isEmpty ?? true {
            return
        }

        UIView.animate(withDuration: 0.25) {
            if self.text.isEmpty {
                self.placeholderLabel?.alpha = 1.0
            } else {
                self.placeholderLabel?.alpha = 0.0
            }
        }
    }

    override var text: String! {
        didSet {
            textChanged()
        }
    }

}

我知道有几种与此类似的方法,但这一方面的好处是:

  • 可以在 IB
  • 中设置占位符文字,字体大小和颜色
  • 不再显示警告&#34; 滚动视图具有不明确的可滚动内容&#34;在IB。
  • 添加动画以显示/隐藏占位符。

答案 18 :(得分:1)

我喜欢@ nerdist的解决方案。基于此,我创建了UITextView的扩展名:

import Foundation
import UIKit

extension UITextView
{
  private func add(_ placeholder: UILabel) {
    for view in self.subviews {
        if let lbl = view as? UILabel  {
            if lbl.text == placeholder.text {
                lbl.removeFromSuperview()
            }
        }
    }
    self.addSubview(placeholder)
  }

  func addPlaceholder(_ placeholder: UILabel?) {
    if let ph = placeholder {
      ph.numberOfLines = 0  // support for multiple lines
      ph.font = UIFont.italicSystemFont(ofSize: (self.font?.pointSize)!)
      ph.sizeToFit()
      self.add(ph)
      ph.frame.origin = CGPoint(x: 5, y: (self.font?.pointSize)! / 2)
      ph.textColor = UIColor(white: 0, alpha: 0.3)
      updateVisibility(ph)
    }
  }

  func updateVisibility(_ placeHolder: UILabel?) {
    if let ph = placeHolder {
      ph.isHidden = !self.text.isEmpty
    }
  }
}

例如,在ViewController类中,这就是我使用它的方式:

class MyViewController: UIViewController, UITextViewDelegate {
  private var notePlaceholder: UILabel!
  @IBOutlet weak var txtNote: UITextView!
  ...
  // UIViewController
  override func viewDidLoad() {
    notePlaceholder = UILabel()
    notePlaceholder.text = "title\nsubtitle\nmore..."
    txtNote.addPlaceholder(notePlaceholder)
    ...
  }

  // UITextViewDelegate
  func textViewDidChange(_ textView: UITextView) {
    txtNote.updateVisbility(notePlaceholder)
    ...
  }

UITextview上的占位符!

enter image description here

<强>更新

如果您在代码中更改textview的文本,请记得调用updateVisibitly方法来隐藏占位符:

txtNote.text = "something in code"
txtNote.updateVisibility(self.notePlaceholder) // hide placeholder if text is not empty.

要防止多次添加占位符,我会在add()中添加一个私人extension功能。

答案 19 :(得分:1)

如果我们在项目中使用 pod IQKeyboardManagerSwift,我们可以很容易地实现 textview PlaceHolder 只需遵循 4 个步骤

  1. 我们必须将 IQTextView 类分配给我们的 TextView 类。
  2. 我们必须在控制器页面中导入 IQKeyboardManagerSwift
  3. 最后但并非最不重要的一点是在控制器页面上制作 textView 的出口 // 如果你愿意:)
  4. 通过可检查的故事板为 textView 提供一些占位符文本

答案 20 :(得分:1)

快速答案

这是为占位符设置动画的自定义类。

class CustomTextView: UITextView {

    //   MARK: - public

    public var placeHolderText: String? = "Enter Reason.."

    public lazy var placeHolderLabel: UILabel! = {
        let placeHolderLabel = UILabel(frame: .zero)
        placeHolderLabel.numberOfLines = 0
        placeHolderLabel.backgroundColor = .clear
        placeHolderLabel.alpha = 0.5
        return placeHolderLabel
    }()

    //   MARK: - Init

    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
        enableNotifications()
    }

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

    func setup() {
        placeHolderLabel.frame = CGRect(x: 8, y: 8, width: self.bounds.size.width - 16, height: 15)
        placeHolderLabel.sizeToFit()
    }

    //   MARK: - Cycle

    override func awakeFromNib() {
        super.awakeFromNib()

        textContainerInset = UIEdgeInsets(top: 8, left: 5, bottom: 8, right: 8)
        returnKeyType = .done
        addSubview(placeHolderLabel)
        placeHolderLabel.frame = CGRect(x: 8, y: 8, width: self.bounds.size.width - 16, height: 15)
        placeHolderLabel.textColor = textColor
        placeHolderLabel.font = font
        placeHolderLabel.text = placeHolderText
        bringSubviewToFront(placeHolderLabel)
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        setup()
    }

    //   MARK: - Notifications

    private func enableNotifications() {
        NotificationCenter.default.addObserver(self, selector: #selector(textDidChangeNotification(_:)), name: UITextView.textDidChangeNotification , object: nil)
    }

    @objc func textDidChangeNotification(_ notify: Notification) {
        guard self == notify.object as? UITextView else { return }
        guard placeHolderText != nil else { return }

        UIView.animate(withDuration: 0.25, animations: {
            self.placeHolderLabel.alpha = (self.text.count == 0) ? 0.5 : 0
        }, completion: nil)
    }

}

答案 21 :(得分:1)

基于这里已经提出的一些重要建议,我能够将UITextView的以下轻量级,与Interface-Builder兼容的子类放在一起,

  • 包括可配置的占位符文本,其样式类似于UITextField
  • 不需要任何其他子视图或约束。
  • 不需要ViewController的任何委派或其他行为。
  • 不需要任何通知。
  • 使该文本与其他所有与该字段的text属性无关的外部类完全分开。

欢迎提出任何改进建议,尤其是如果有任何方法可以通过编程方式拉动iOS的占位符颜色,而不是对其进行硬编码的话。

Swift 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 { super.text = newValue }
    }
    @IBInspectable var placeholderText: String = ""
    @IBInspectable var placeholderTextColor: UIColor = UIColor(red: 0.78, green: 0.78, blue: 0.80, alpha: 1.0) // Standard iOS placeholder color (#C7C7CD). See https://stackoverflow.com/questions/31057746/whats-the-default-color-for-placeholder-text-in-uitextfield
    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 func becomeFirstResponder() -> Bool {
        // If the current text is the placeholder, remove it
        if showingPlaceholder {
            text = nil
            textColor = nil // Put the text back to the default, unmodified color
            showingPlaceholder = false
        }
        return super.becomeFirstResponder()
    }

    override func resignFirstResponder() -> Bool {
        // If there's no text, put the placeholder back
        if text.isEmpty {
            showPlaceholderText()
        }
        return super.resignFirstResponder()
    }

    private func showPlaceholderText() {
        showingPlaceholder = true
        textColor = placeholderTextColor
        text = placeholderText
    }
}

答案 22 :(得分:1)

与此帖子的几乎所有答案相反,UITextView 确实具有占位符属性。由于超出我的理解范围的原因,它仅在IB中公开,如下所示:

<userDefinedRuntimeAttributes>
  <userDefinedRuntimeAttribute type="string" keyPath="placeholder" value="My Placeholder"/>
</userDefinedRuntimeAttributes>

因此,如果您使用情节提要板并且静态占位符就足够了,只需在检查器上设置属性。

您还可以在如下代码中设置此属性:

textView.setValue("My Placeholder", forKeyPath: "placeholder")

由于是否公开了属性 ,因此对是否可以访问私有API感到困惑。

我没有尝试使用此方法提交。但是我很快就会以这种方式提交,并将相应地更新此答案。

答案 23 :(得分:1)

我们的解决方案避免了program option2 implicit none character(:), allocatable :: txt call sub1(txt) print *, "main ", len(txt), txt ! prints: main 7 message contains subroutine sub1(txt) character(:), allocatable, optional :: txt character(:), allocatable :: txt_ if(present(txt)) then ! txt_ isn't optional, so the bug doesn't fire call sub2(txt_) txt = txt_ end if print *, "sub1 ", len(txt), txt ! prints: sub1 7 message end subroutine sub2(txt) character(:), allocatable, optional :: txt print *, present(txt) if(present(txt)) txt = "message" print *, "sub2 ", len(txt), txt ! prints: sub2 7 message end end program option3a ! using a derived type with allocatable character length component. implicit none type :: string character(:), allocatable :: chars end type type(string) :: txt call sub1(txt) print *, "main ", len(txt%chars), txt%chars ! prints: main 7 message contains subroutine sub1(txt) type(string), optional :: txt call sub2(txt) print *, "sub1 ", len(txt%chars), txt%chars ! prints: sub1 7 message end subroutine sub2(txt) type(string), optional :: txt if(present(txt)) txt = string("message") print *, "sub2 ", len(txt%chars), txt%chars ! prints: sub2 7 message end end program option3b ! using a parameterized derived type, you can practically mimic the intrinsic ! character type behavior, with the possibility to add custom behavior. ! but its still raw in gfortran. implicit none type :: string(len) integer, len :: len character(len) :: chars end type type(string(:)), allocatable :: txt call sub1(txt) print *, "main ", txt%len, txt ! prints: main 7 7 message (a lil bug of gfortran) contains subroutine sub1(txt) type(string(:)), allocatable, optional :: txt call sub2(txt) print *, "sub1 ", txt%len, txt ! prints: main 7 7 message end subroutine sub2(txt) type(string(:)), allocatable, optional :: txt ! the following fails with gfortran, however it's valid syntax ! if(present(txt)) txt = string(7)("message") allocate(string(7) :: txt) if(present(txt)) txt%chars = "message" print *, "sub2 ", txt%len, txt ! prints: main 7 7 message end end UITextView属性的混乱,如果您要维护字符计数器,这将非常方便。

很简单:

1)在情节提要中创建一个虚拟text,其属性与主textColor相同。将占位符文本分配给虚拟文本。

2)对齐两个UITextView

的上边缘,左边缘和右边缘

3)将假人放在母版后面。

4)覆盖主机的UITextView委托函数,如果主机具有0个字符,则显示虚拟对象。否则,请向主人展示。

这假设两个UITextViews.都有透明的背景。如果没有,则在有0个字符时将虚拟对象放在顶部,而在有> 0个字符时将其推到下方。您还必须交换响应者,以确保光标位于正确的textViewDidChange(textView:)之后。

答案 24 :(得分:1)

在ios中没有这样的属性可以直接在TextView中添加占位符,而是可以在textView中添加标签并显示/隐藏更改。 SWIFT 2.0并确保实现textviewdelegate

func textViewDidChange(TextView: UITextView)
{

 if  txtShortDescription.text == ""
    {
        self.lblShortDescription.hidden = false
    }
    else
    {
        self.lblShortDescription.hidden = true
    }

}

答案 25 :(得分:1)

Swift - 我写了一个继承了UITextView的类,我添加了一个UILabel作为子视图来充当占位符。

  import UIKit
  @IBDesignable
  class HintedTextView: UITextView {

      @IBInspectable var hintText: String = "hintText" {
          didSet{
              hintLabel.text = hintText
          }
      }

      private lazy var hintLabel: UILabel = {
          let label = UILabel()
          label.font = UIFont.systemFontOfSize(16)
          label.textColor = UIColor.lightGrayColor()
          label.translatesAutoresizingMaskIntoConstraints = false
          return label
      }()


      override init(frame: CGRect, textContainer: NSTextContainer?) {
          super.init(frame: frame, textContainer: textContainer)
          setupView()
      }

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

      override func prepareForInterfaceBuilder() {
         super.prepareForInterfaceBuilder()
         setupView()
      }

      private func setupView() {

        translatesAutoresizingMaskIntoConstraints = false
        delegate = self
        font = UIFont.systemFontOfSize(16)

        addSubview(hintLabel)

        NSLayoutConstraint.activateConstraints([

           hintLabel.leftAnchor.constraintEqualToAnchor(leftAnchor, constant: 4),
           hintLabel.rightAnchor.constraintEqualToAnchor(rightAnchor, constant: 8),
           hintLabel.topAnchor.constraintEqualToAnchor(topAnchor, constant: 4),
           hintLabel.heightAnchor.constraintEqualToConstant(30)
         ])
        }

      override func layoutSubviews() {
        super.layoutSubviews()
        setupView()
     }

}

答案 26 :(得分:1)

在swift2.2中:

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

}

在swift3中:

import UIKit

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() {
    NotificationCenter.default.addObserver(self,
                                                     selector: #selector(textDidChange),
                                                     name: NSNotification.Name.UITextViewTextDidChange,
                                                     object: nil)

    placeholderLabel.font = font
    placeholderLabel.textColor = placeholderColor
    placeholderLabel.textAlignment = textAlignment
    placeholderLabel.text = placeholder
    placeholderLabel.numberOfLines = 0
    placeholderLabel.backgroundColor = UIColor.clear
    placeholderLabel.translatesAutoresizingMaskIntoConstraints = false
    addSubview(placeholderLabel)
    updateConstraintsForPlaceholderLabel()
}

private func updateConstraintsForPlaceholderLabel() {
    var newConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-(\(textContainerInset.left + textContainer.lineFragmentPadding))-[placeholder]",
        options: [],
        metrics: nil,
        views: ["placeholder": placeholderLabel])
    newConstraints += NSLayoutConstraint.constraints(withVisualFormat: "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.isHidden = !text.isEmpty
}

public override func layoutSubviews() {
    super.layoutSubviews()
    placeholderLabel.preferredMaxLayoutWidth = textContainer.size.width - textContainer.lineFragmentPadding * 2.0
}

deinit {
    NotificationCenter.default.removeObserver(self,
                                                        name: NSNotification.Name.UITextViewTextDidChange,
                                                        object: nil)
}

}

我在swift写了一堂课。您需要在需要时导入此类。

答案 27 :(得分:1)

由于声誉,我无法添加评论。在@clearlight回答中添加一个委托需求。

func textViewDidBeginEditing(_ textView: UITextView) { 
        cell.placeholderLabel.isHidden = !textView.text.isEmpty
}

需要

因为第一次没有调用textViewDidChange

答案 28 :(得分:1)

没有任何占位符可用于textview。当用户输入textview时,你必须在它上面放置标签,然后在用户输入删除所有值时将其隐藏或设置为默认值。

答案 29 :(得分:1)

编辑完成后,我不得不调度队列以使占位符文本重新出现。

func textViewDidBeginEditing(_ textView: UITextView) {

    if textView.text == "Description" {
        textView.text = nil
    }
}

func textViewDidEndEditing(_ textView: UITextView) {

    if textView.text.isEmpty {
        DispatchQueue.main.async {
            textView.text = "Description"
        }
    }
}

答案 30 :(得分:1)

func setPlaceholder(){
var placeholderLabel = UILabel()
        placeholderLabel.text = "Describe your need..."
        placeholderLabel.font = UIFont.init(name: "Lato-Regular", size: 15.0) ?? UIFont.boldSystemFont(ofSize: 14.0)
        placeholderLabel.sizeToFit()
        descriptionTextView.addSubview(placeholderLabel)
        placeholderLabel.frame.origin = CGPoint(x: 5, y: (descriptionTextView.font?.pointSize)! / 2)
        placeholderLabel.textColor = UIColor.lightGray
        placeholderLabel.isHidden = !descriptionTextView.text.isEmpty
}



//Delegate Method.

func textViewDidChange(_ textView: UITextView) {
        placeholderLabel.isHidden = !textView.text.isEmpty
    }
	

答案 31 :(得分:1)

如果您正在处理多个文本视图,这是我准备好使用的解决方案

func textViewShouldBeginEditing(textView: UITextView) -> Bool {        
    // Set cursor to the beginning if placeholder is set
    if textView.textColor == UIColor.lightGrayColor() {
        textView.selectedTextRange = textView.textRangeFromPosition(textView.beginningOfDocument, toPosition: textView.beginningOfDocument)
    }

    return true
}

func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
    // Remove placeholder
    if textView.textColor == UIColor.lightGrayColor() && text.characters.count > 0 {
        textView.text = ""
        textView.textColor = UIColor.blackColor()
    }

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

    return true
}

func textViewDidChange(textView: UITextView) {
    // Set placeholder if text is empty
    if textView.text.isEmpty {
        textView.text = NSLocalizedString("Hint", comment: "hint")
        textView.textColor = UIColor.lightGrayColor()
        textView.selectedTextRange = textView.textRangeFromPosition(textView.beginningOfDocument, toPosition: textView.beginningOfDocument)
    }
}

func textViewDidChangeSelection(textView: UITextView) {
    // Set cursor to the beginning if placeholder is set
    let firstPosition = textView.textRangeFromPosition(textView.beginningOfDocument, toPosition: textView.beginningOfDocument)

    // Do not change position recursively
    if textView.textColor == UIColor.lightGrayColor() && textView.selectedTextRange != firstPosition {
        textView.selectedTextRange = firstPosition
    }
}

答案 32 :(得分:0)

这是可以放入UIStackView的内容,它将使用内部高度约束来调整大小。可能需要进行调整以满足特定要求。

import UIKit

public protocol PlaceholderTextViewDelegate: class {
  func placeholderTextViewTextChanged(_ textView: PlaceholderTextView, text: String)
}

public class PlaceholderTextView: UIView {

  public weak var delegate: PlaceholderTextViewDelegate?
  private var heightConstraint: NSLayoutConstraint?

  public override init(frame: CGRect) {
    self.allowsNewLines = true

    super.init(frame: frame)

    self.heightConstraint = self.heightAnchor.constraint(equalToConstant: 0)
    self.heightConstraint?.isActive = true

    self.addSubview(self.placeholderTextView)
    self.addSubview(self.textView)

    self.pinToCorners(self.placeholderTextView)
    self.pinToCorners(self.textView)

    self.updateHeight()
  }

  public override func didMoveToSuperview() {
    super.didMoveToSuperview()

    self.updateHeight()
  }

  private func pinToCorners(_ view: UIView) {
    NSLayoutConstraint.activate([
      view.leadingAnchor.constraint(equalTo: self.leadingAnchor),
      view.trailingAnchor.constraint(equalTo: self.trailingAnchor),
      view.topAnchor.constraint(equalTo: self.topAnchor),
      view.bottomAnchor.constraint(equalTo: self.bottomAnchor)
    ])
  }

  // Accessors
  public var text: String? {
    didSet {
      self.textView.text = text
      self.textViewDidChange(self.textView)
      self.updateHeight()
    }
  }

  public var textColor: UIColor? {
    didSet {
      self.textView.textColor = textColor
      self.updateHeight()
    }
  }

  public var font: UIFont? {
    didSet {
      self.textView.font = font
      self.placeholderTextView.font = font
      self.updateHeight()
    }
  }

  public override var tintColor: UIColor? {
    didSet {
      self.textView.tintColor = tintColor
      self.placeholderTextView.tintColor = tintColor
    }
  }

  public var placeholderText: String? {
    didSet {
      self.placeholderTextView.text = placeholderText
      self.updateHeight()
    }
  }

  public var placeholderTextColor: UIColor? {
    didSet {
      self.placeholderTextView.textColor = placeholderTextColor
      self.updateHeight()
    }
  }

  public var allowsNewLines: Bool

  public required init?(coder _: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

  private lazy var textView: UITextView = self.newTextView()
  private lazy var placeholderTextView: UITextView = self.newTextView()

  private func newTextView() -> UITextView {
    let textView = UITextView()
    textView.translatesAutoresizingMaskIntoConstraints = false
    textView.isScrollEnabled = false
    textView.delegate = self
    textView.backgroundColor = .clear
    return textView
  }

  private func updateHeight() {
    let maxSize = CGSize(width: self.frame.size.width, height: .greatestFiniteMagnitude)

    let textViewSize = self.textView.sizeThatFits(maxSize)
    let placeholderSize = self.placeholderTextView.sizeThatFits(maxSize)

    let maxHeight = ceil(CGFloat.maximum(textViewSize.height, placeholderSize.height))

    self.heightConstraint?.constant = maxHeight
  }
}

extension PlaceholderTextView: UITextViewDelegate {
  public func textViewDidChangeSelection(_: UITextView) {
    self.placeholderTextView.alpha = self.textView.text.isEmpty ? 1 : 0
    self.updateHeight()
  }

  public func textViewDidChange(_: UITextView) {
    self.delegate?.placeholderTextViewTextChanged(self, text: self.textView.text)
  }

  public func textView(_: UITextView, shouldChangeTextIn _: NSRange,
                       replacementText text: String) -> Bool {
    let containsNewLines = text.rangeOfCharacter(from: .newlines)?.isEmpty == .some(false)
    guard !containsNewLines || self.allowsNewLines else { return false }

    return true
  }
}

答案 33 :(得分:0)

文字视图代理方法

使用这两个委托方法,并在类

中编写UITextViewDelegate
func textViewDidBeginEditing(_ textView: UITextView) {
    if (commentsTextView.text == "Type Your Comments")
    {
        commentsTextView.text = nil
        commentsTextView.textColor = UIColor.darkGray
    }
}

func textViewDidEndEditing(_ textView: UITextView) {
    if commentsTextView.text.isEmpty
    {
        commentsTextView.text = "Type Your Comments"
        commentsTextView.textColor = UIColor.darkGray
    }
    textView.resignFirstResponder()
}

答案 34 :(得分:0)

clearlight上面的答案的协议版本,因为协议很棒。随心所欲地流行吧。扣篮!

extension UITextViewPlaceholder where Self: UIViewController {

    // Use this in ViewController's ViewDidLoad method.
    func addPlaceholder(text: String, toTextView: UITextView, font: UIFont? = nil) {
        placeholderLabel = UILabel()
        placeholderLabel.text = text
        placeholderLabel.font = font ?? UIFont.italicSystemFont(ofSize: (toTextView.font?.pointSize)!)
        placeholderLabel.sizeToFit()
        toTextView.addSubview(placeholderLabel)
        placeholderLabel.frame.origin = CGPoint(x: 5, y: (toTextView.font?.pointSize)! / 2)
        placeholderLabel.textColor = UIColor.lightGray
        placeholderLabel.isHidden = !toTextView.text.isEmpty
    }

    // Use this function in the ViewController's textViewDidChange delegate method.
    func textViewWithPlaceholderDidChange(_ textView: UITextView) {
        placeholderLabel.isHidden = !textView.text.isEmpty
    }
}

答案 35 :(得分:0)

快速4、4.2和5

[![@IBOutlet var detailTextView: UITextView!

override func viewDidLoad() {
        super.viewDidLoad()
        detailTextView.delegate = self
}

extension ContactUsViewController : UITextViewDelegate {

    public func textViewDidBeginEditing(_ textView: UITextView) {
        if textView.text == "Write your message here..." {
            detailTextView.text = ""
            detailTextView.textColor = UIColor.init(red: 0/255, green: 0/255, blue: 0/255, alpha: 0.86)

        }
        textView.becomeFirstResponder()
    }

    public func textViewDidEndEditing(_ textView: UITextView) {

        if textView.text == "" {
            detailTextView.text = "Write your message here..."
            detailTextView.textColor = UIColor.init(red: 0/255, green: 0/255, blue: 0/255, alpha: 0.30)
        }
        textView.resignFirstResponder()
    }
[![}][1]][1]

答案 36 :(得分:0)

var placeholderLabel : UILabel!
  textviewDescription.delegate = self
    placeholderLabel = UILabel()
    placeholderLabel.text = "Add a description"

   func textViewDidChange(_ textView: UITextView) {
             placeholderLabel.isHidden = !textviewDescription.text.isEmpty
    }

答案 37 :(得分:0)

我知道这是一个古老的问题,但想分享一下我认为是将UITextView扩展为具有placeholderText和placeholderColor字段的有用方法。基本上,您将UITextView转换为UITextField,然后设置attributedPlaceholder字段。 PlaceholderText和placeholderColor是IBInspectable字段,因此它们的值可以在IB中设置,并且行为与UITextField占位符功能完全相同。

UITextView + Extend.h

#import <UIKit/UIKit.h>

@interface UITextView (Extend)

@property (nonatomic) IBInspectable NSString *placeholderText;
@property (nonatomic) IBInspectable UIColor *placeholderColor;

@end

UITextView + Extend.m

#import "UITextView+Extend.h"
#import "objc/runtime.h"

@implementation UITextView (Extend)

- (void)setPlaceholderText:(NSString *)placeholderText
{
    objc_setAssociatedObject(self, @selector(placeholderText), placeholderText, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [self updatePlaceholderText];
}

- (NSString*)placeholderText
{
    return objc_getAssociatedObject(self, @selector(placeholderText));
}

- (void)setPlaceholderColor:(UIColor *)placeholderColor
{
    objc_setAssociatedObject(self, @selector(placeholderColor), placeholderColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [self updatePlaceholderText];
}

- (UIColor*)placeholderColor
{
    return objc_getAssociatedObject(self, @selector(placeholderColor));
}

- (void)updatePlaceholderText
{
    NSString *text = self.placeholderText;
    UIColor *color = self.placeholderColor;
    if(text && color)
    {
        UITextField *textField = (UITextField*)self;
        textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:text attributes:@{NSForegroundColorAttributeName:color}];
    }
}

@end

答案 38 :(得分:0)

我有一个简单的UITextView版本,带有占位符。主要思想是:

  • 如果用户开始编辑并且可见占位符,则隐藏占位符
  • 如果用户结束编辑并且文本视图的text为空,则显示占位符。
class PlaceholderTextView: UITextView {

    var placeholder = "" {
        didSet {
            if isPlaceholderVisible {
                showPlaceholder()
            }
        }
    }

    var isPlaceholderVisible = true {
        didSet {
            isPlaceholderVisible ? showPlaceholder() : hidePlaceholder()
        }
    }

    init() {
        super.init(frame: .zero, textContainer: nil)
        delegate = self
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func showPlaceholder() {
        text = placeholder
        // Set other things like color of text for placeholder, ...
    }

    private func hidePlaceholder() {
        text = ""
        // Set other things like color of text for normal input, ...
    }

}

extension PlaceholderTextView: UITextViewDelegate {
    func textViewDidBeginEditing(_ textView: UITextView) {
        if isPlaceholderVisible {
            isPlaceholderVisible = false
        }
    }

    func textViewDidEndEditing(_ textView: UITextView) {
        if text.isEmpty {
            isPlaceholderVisible = true
        }
    }
}

答案 39 :(得分:0)

我相信这是一个非常干净的解决方案。它将在实际文本视图下面添加一个虚拟文本视图,并根据实际文本视图中的文本显示或隐藏它:

import Foundation
import UIKit

class TextViewWithPlaceholder: UITextView {

    private var placeholderTextView: UITextView = UITextView()

    var placeholder: String? {
        didSet {
            placeholderTextView.text = placeholder
        }
    }

    override var text: String! {
        didSet {
            placeholderTextView.isHidden = text.isEmpty == false
        }
    }

    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
        commonInit()
    }

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

    private func commonInit() {
        applyCommonTextViewAttributes(to: self)
        configureMainTextView()
        addPlaceholderTextView()
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(textDidChange),
                                               name: UITextView.textDidChangeNotification,
                                               object: nil)
    }

    func addPlaceholderTextView() {
        applyCommonTextViewAttributes(to: placeholderTextView)
        configurePlaceholderTextView()
        insertSubview(placeholderTextView, at: 0)
    }

    private func applyCommonTextViewAttributes(to textView: UITextView) {
        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.textContainer.lineFragmentPadding = 0
        textView.textContainerInset = UIEdgeInsets(top: 10,
                                                   left: 10,
                                                   bottom: 10,
                                                   right: 10)
    }

    private func configureMainTextView() {
        // Do any configuration of the actual text view here
    }

    private func configurePlaceholderTextView() {
        placeholderTextView.text = placeholder
        placeholderTextView.font = font
        placeholderTextView.textColor = UIColor.lightGray
        placeholderTextView.frame = bounds
        placeholderTextView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        placeholderTextView.frame = bounds
    }

    @objc func textDidChange() {
        placeholderTextView.isHidden = !text.isEmpty
    }

}

答案 40 :(得分:0)

另一种解决方案可能是像我一样使用keyboardWillHide和keyboardWillShow通知。

首先,您需要处理监听,并分别监听viewWillAppearviewWillAppear方法中的通知(以处理内存泄漏)。

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    setupKeyboardNotificationListeners(enable: true)
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    setupKeyboardNotificationListeners(enable: false)
}

然后使用该方法来处理监听/取消监听通知:

private func setupKeyboardNotificationListeners(enable: Bool) {
        if enable {
            NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
            NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
        } else {
            NotificationCenter.default.removeObserver(self)
        }
    }

然后在keyboardWillHide和keyboardWillShow两种方法中,您都将处理文本的占位符和颜色更改。

@objc func keyboardWillShow(notification: NSNotification) {
    if self.textView.text == self.placeholder {
        self.textView.text = ""
        self.textView.textColor = .black
    }
}

@objc func keyboardWillHide(notification: NSNotification) {
    if self.textView.text.isEmpty {
        self.textView.text = self.placeholder
        self.textView.textColor = .lightGrey
    }
}

我发现该解决方案是迄今为止最好的解决方案,因为该文本将在键盘出现后立即删除,而不是在用户开始输入时被删除,这可能会引起混乱。

答案 41 :(得分:0)

这就是我所做的。倾向于代码清晰和简单。我需要添加一个textView,它将在我的应用程序上获得一些其他注释。保存后,可以创建或修改这些附加注释。见下文。 HTH。 :)

class NotesTextView: UITextView {

    var placeholder = "" {
        didSet {
            showPlaceholder()
        }
    }
    
    // if the text is the placeholder, then assign a color fitting for a
    // placeholder text, else, assign it your color of choice.
    override var text: String! {
        didSet {
            textColor = text == placeholder ? .tertiaryLabel : .systemBlue
        }
    }
    
    
    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
        
        delegate = self
        //config your font and translateAutoResizingMaskIntoConstraints here
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func showPlaceholder() {
        text = placeholder
    }
    
    private func hidePlaceholder() {
        text = ""
    }
}

extension NotesTextView: UITextViewDelegate {
    func textViewDidBeginEditing(_ textView: UITextView) {
        if text == placeholder {
            hidePlaceholder()
        }
    }
    
    func textViewDidEndEditing(_ textView: UITextView) {
        if text.isEmpty {
            showPlaceholder()
        }
    }
}

答案 42 :(得分:0)

SWIFTUI

这是使用UIVIewRepresentable制作的Swiftui TextView,具有占位符功能和边框颜色

struct TextView: UIViewRepresentable {

@Binding var text: String
var placeholderText: String
var textStyle: UIFont.TextStyle

func makeUIView(context: Context) -> UITextView {
    let textView = UITextView()

    textView.font = UIFont.preferredFont(forTextStyle: textStyle)
    textView.autocapitalizationType = .sentences
    textView.isSelectable = true
    textView.isUserInteractionEnabled = true
    textView.delegate = context.coordinator
    textView.layer.borderWidth = 0.6
    textView.layer.borderColor = UIColor.lightGray.cgColor
    textView.layer.cornerRadius = 10
    textView.text = placeholderText
    textView.textColor = UIColor.lightGray
    return textView
}

func updateUIView(_ uiView: UITextView, context: Context) {
    uiView.text = text
    uiView.font = UIFont.preferredFont(forTextStyle: textStyle)
}

func makeCoordinator() -> Coordinator {
    Coordinator(self)
}
 
class Coordinator: NSObject, UITextViewDelegate {
    var parent: TextView
 
    init(_ parent: TextView) {
        self.parent = parent
    }
 
    func textViewDidChange(_ textView: UITextView) {
        self.parent.text = textView.text
    }
    
    func textViewDidBeginEditing(_ textView: UITextView) {
        if textView.textColor == UIColor.lightGray {
            textView.text = nil
            textView.textColor = UIColor.black
        }
    }
    
    func textViewDidEndEditing(_ textView: UITextView) {
        if textView.text.isEmpty {
            textView.text = self.parent.placeholderText
            textView.textColor = UIColor.lightGray
        }
    }
}

}

然后在视图中可以像这样使用它

TextView(text: self.$viewModel.addPostCommentText, placeholderText: "Share your story about this cash", textStyle: .body)
                .padding()
                .frame(height: 150)

答案 43 :(得分:0)

清理代码尽可能简单!

我建议在这种情况下使用光路吊舱,以使代码尽可能简单和整洁。 有很多吊舱提供了占位符选项,而无需编写任何代码。

其中一些:

答案 44 :(得分:0)

<强>夫特:

添加您的TextView @IBOutlet

@IBOutlet weak var txtViewMessage: UITextView!

viewWillAppear方法中,请添加以下内容:

override func viewWillAppear(_ animated: Bool)
{
    super.viewWillAppear(animated)

    txtViewMessage.delegate = self    // Give TextViewMessage delegate Method

    txtViewMessage.text = "Place Holder Name"
    txtViewMessage.textColor = UIColor.lightGray

}

请添加Delegate使用扩展程序(UITextViewDelegate):

// MARK: - UITextViewDelegate
extension ViewController: UITextViewDelegate
{

    func textViewDidBeginEditing(_ textView: UITextView)
    {

        if !txtViewMessage.text!.isEmpty && txtViewMessage.text! == "Place Holder Name"
        {
            txtViewMessage.text = ""
            txtViewMessage.textColor = UIColor.black
        }
    }

    func textViewDidEndEditing(_ textView: UITextView)
    {

        if txtViewMessage.text.isEmpty
        {
            txtViewMessage.text = "Place Holder Name"
            txtViewMessage.textColor = UIColor.lightGray
        }
    }
}