在 Swift 中的 UITextView 中添加占位符文本?

Posted

技术标签:

【中文标题】在 Swift 中的 UITextView 中添加占位符文本?【英文标题】:Add placeholder text inside UITextView in Swift? 【发布时间】:2015-02-23 11:26:06 【问题描述】:

如何在UITextView 中添加一个占位符,类似于在Swift 中为UITextField 设置的占位符?

【问题讨论】:

这是 ios 开发中使用 UITextView 的一个古老问题。我已经编写了像这里提到的子类:***.com/a/1704469/1403046。好处是您仍然可以拥有一个委托,以及在多个地方使用该类,而无需重新实现逻辑。 我将如何使用您的子类,同时为项目使用 swift。使用桥接文件? 你可以这样做,或者在 Swift 中重新实现它。答案中的代码比实际需要的要长。要点是显示/隐藏您在文本更改时收到通知的方法中添加的标签。 您可以使用来自 GitHub 的 UIFloatLabelTextView 示例。这个位置占位符在写的时候在上面。真的很有趣的一个! github.com/ArtSabintsev/UIFloatLabelTextView 老实说,实现这一点的最简单方法是使用自定义 textView 并在没有文本时添加在 textView 上绘制的占位符文本......到目前为止,其他答案一直是这个版本过于复杂,涉及有问题的状态管理(包括文本应该/不应该存在/不存在时的误报) 【参考方案1】:

为 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 - 如果您希望在文本视图为空时显示占位符,即使文本视图已被选中:

先在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)
        
    

【讨论】:

您好,请确保您将您的视图控制器设置为您的 textView 的委托。你可以通过创建一个从你的 textView 到你的 viewController 的出口来实现这一点。然后使用yourTextField.delegate = self。如果不这样做,textViewDidBeginEditingtextViewDidEndEditing 函数将不起作用。 代码没有编译,我遇到了Cannot convert value of type 'NSRange' (aka '_NSRange') to expected argument type 'Range<String.Index>' (aka 'Range<String.CharacterView.Index>')的错误。 我找到了解决方案,我将附上修改后的代码@iPeter。当前文本必须是 NSString 共振峰:let currentText = textView.text as NSString?。将let updatedText = 行转换为let updatedText = currentText?.replacingCharacters(in: range, with: text)。最后,将if updatedText.isEmpty 行转换为if (updatedText?.isEmpty)! 。这应该可以解决问题! @LyndseyScott 在func textViewDidChangeSelection(_ textView: UITextView) 中设置func textViewDidChangeSelection(_ textView: UITextView) 会导致无限循环... 对于如此简单的事情,(iOS) 开发人员竟需要竭尽全力,这绝对令人震惊。这是很久以前应该是 TextView 控件的一部分的基本功能,我不明白为什么 Apple 还没有解决这个问题。【参考方案2】:

我通过使用两个不同的文本视图来做到这一点:

    背景中的一个用作占位符。 在用户实际输入的前景中(具有透明背景)。

这个想法是,一旦用户开始在前景视图中输入内容,后台的占位符就会消失(如果用户删除所有内容,则会重新出现)。所以它的行为与单行文本字段的占位符完全一样。

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

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

【讨论】:

这种方式有点hacky,但它绝对是最简单的,并且可以准确地生成您想要的结果。好主意【参考方案3】:

浮动占位符


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

斯威夫特 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


【讨论】:

这种方法无疑是我发现的最简单、最不容易出错的方法。太棒了。 这是一个很好的方法,但是如果占位符文本很长,标签文本可能会超出范围.. 简单易用,并与现有行为很好地集成。无论如何,占位符消息不应该那么冗长,但是将行设置为 0 不会解决这个问题吗? 我通常更喜欢这种方法,但如果文本居中对齐会出现问题,因为光标将位于占位符的顶部而不是左侧。 @blwinters - 这将是一个非常不寻常的极端情况,不是吗?但是假设在这种情况下它是一个多行输入字段(我必须假设),您不能调整垂直偏移计算以将占位符文本从光标移开一点吗?【参考方案4】:

斯威夫特:

以编程方式或通过 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 的条件。

【讨论】:

【参考方案5】:

我不知道为什么人们将这个问题如此复杂化.... 这相当简单明了。这是提供请求功能的 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];
    `

【讨论】:

仅供参考,在有人打败我之前......这可以直接翻译成 Swift。这里没有什么复杂的。【参考方案6】:

在视图加载中设置值

    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】:

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

func textViewDidChange(TextView: UITextView)


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


【讨论】:

【参考方案8】:

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

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
    

【讨论】:

非常好!【参考方案9】:

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


【讨论】:

使用约束来定位占位符的好例子,但是占位符的理想位置正好在文本的第一个字符的正上方,因此根据文本视图的位置计算该位置字体更好,因为它适应为文本视图设置的任何字体大小。【参考方案10】:

我喜欢@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 上的占位符!

更新

如果你在代码中改变了textview的文本,记得调用updateVisibitly方法隐藏占位符:

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

为防止多次添加占位符,在extension 中添加了私有add() 函数。

【讨论】:

再次感谢您对原版的改进。这个周末我花了太多时间,但我想你会喜欢我最终想出的 EZ Placeholder 极端变体:***.com/a/41081244/2079103 (我感谢你为占位符解决方案的发展做出了贡献) 【参考方案11】:

在 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

类 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写了一个类。您需要在需要时导入此类。

【讨论】:

【参考方案12】:

我试图通过clearlight 的answer 简化代码。

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

【讨论】:

几个问题。 (1) 作为一个假设和“借用” UIView 标签属性值的扩展,有人可能会在他们自己的视图层次结构中使用相同的标签,而不知道扩展的用法,从而产生极难诊断的错误。类似的东西不属于库代码或扩展。 (2) 它仍然需要调用者声明一个委托。 Floating Placeholder 避免了黑客攻击,占用空间小,简单且完全本地化,这是一个安全的选择。【参考方案13】:

另一种解决方案(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)
    

结果

【讨论】:

【参考方案14】:

由于声誉问题,我无法添加评论。在@clearlight 答案中再添加一个代表需求。

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

需要

因为textViewDidChange 不是第一次调用

【讨论】:

【参考方案15】:

斯威夫特 3.1

这个扩展对我很有效:https://github.com/devxoul/UITextView-Placeholder

这是一个代码sn-p:

通过 pod 安装:

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

将其导入您的班级

import UITextView_Placeholder

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

textView.placeholder = "Put some detail"

就是这样... 这是它的外观(第三个框是UITextView

【讨论】:

【参考方案16】:

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

【讨论】:

【参考方案17】:

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

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.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.count > 0
        
        self.addSubview(placeholderLabel)
        self.resizePlaceholder()
        self.delegate = self
    

【讨论】:

像魅力一样工作!非常感谢你 ! :) 欢迎亲爱的,一切顺利 这是一个非常优雅的答案。这应该是投票最高的答案【参考方案18】:

上面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
    

【讨论】:

【参考方案19】:

文本视图委托方法

使用这两个委托方法并在你的类中编写 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()

【讨论】:

【参考方案20】:

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
    
	

【讨论】:

【参考方案21】:

这里有一些东西可以放入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
  

【讨论】:

【参考方案22】:

斯威夫特 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 中没有写任何内容时,文本设置为灰色并带有文本“PlaceHolder”

【讨论】:

看起来是更短、最简单的答案,代码更少【参考方案23】:

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

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"
        
    

【讨论】:

如果我希望这个值成为用户输入的内容,我应该输入什么而不是最后一行“textView.text = ”Description”?【参考方案24】:

这是我用来完成这项工作的工具。

@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: UITextView.textDidChangeNotification, 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).isActive = true
        
        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中设置占位符文本、字体大小和颜色。 IB 中不再显示“Scroll View has ambiguous scrollable content”的警告。 添加动画以显示/隐藏占位符。

【讨论】:

在哪里移除观察者? 你是什么观察者? 每一行NotificationCenter.default.addObserver必须有一个计数器行与NotificationCenter.default.removeObserver 从 iOS 9 及更高版本开始,您无需显式删除观察者:developer.apple.com/documentation/foundation/notificationcenter/…【参考方案25】:

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

但是,使用NSTextStorageDelegatetextStorage(_:didProcessEditing:range:changeInLength:) 方法,您将收到对文本的任何更改的通知,即使它是以编程方式完成的。只需像这样分配它:

textView.textStorage.delegate = self

(在UITextView中,这个委托属性默认为nil,所以它不会影响任何默认行为。)

将它与@clearlight 演示的UILabel 技术相结合,可以轻松地将整个UITextViewplaceholder 实现包装到一个扩展中。

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 属性要“迅速”得多。

使用这种方法,您仍然可以将UITextView 的代表分配给其他人。

您甚至不必更改文本视图的类。只需添加扩展名,您就可以为项目中的每个 UITextView 分配一个占位符字符串,即使在 Interface Builder 中也是如此。

为了清楚起见,我省略了 placeholderColor 属性的实现,但它可以通过与 placeholder 类似的计算变量再多几行来实现。

【讨论】:

非常优雅的解决方案。我喜欢它。 textView.textStorage.delegate = self 视图控制器中的 this 将要求我们将该视图控制器与 NSTextStorageDelegate 绑定。真的需要吗? 简单,就像一个魅力。这是必须接受的答案 @Hemang 是文本视图本身是NSTextStorageDelegate,而不是视图控制器。【参考方案26】:

斯威夫特:

添加你的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
        
    

【讨论】:

【参考方案27】:

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

@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
    

【讨论】:

【参考方案28】:

另一个解决方案可能是像我一样使用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
    

我发现这个解决方案是迄今为止最好的解决方案,因为文本将在键盘出现时被删除,而不是在用户开始输入时被删除,这可能会导致混淆。

【讨论】:

【参考方案29】:
var placeholderLabel : UILabel!
  textviewDescription.delegate = self
    placeholderLabel = UILabel()
    placeholderLabel.text = "Add a description"

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

【讨论】:

Textview 占位符在 textview 中仅使用标签作为占位符并隐藏占位符以在 textview 中输入文本【参考方案30】:

这是我解决这个问题的方法(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) 

【讨论】:

这是一个伟大而简单的解决方案。

以上是关于在 Swift 中的 UITextView 中添加占位符文本?的主要内容,如果未能解决你的问题,请参考以下文章

如何防止用户在 Swift 5 的 UITextView 中添加太多换行符?

如何在 Swift 3 中将 NSConstraints 添加到 UITextView

UITableview swift中的UITextview高度

Swift - 以编程方式居中 UITextView

在 Swift 中的 UITextView 上放置阴影

以编程方式在 UITableViewCell 下添加 UITextView 和 UIImageView Swift 4