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


当前回答

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

}

其他回答

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

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

迅速的回答

这是一个自定义类,用于动画占位符。

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)
    }
    
}
var placeholderLabel : UILabel!
  textviewDescription.delegate = self
    placeholderLabel = UILabel()
    placeholderLabel.text = "Add a description"

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

斯威夫特3.1

这个扩展为我工作得很好:https://github.com/devxoul/UITextView-Placeholder

下面是一个代码片段:

通过pod安装:

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

将它导入到类中

import UITextView_Placeholder

并添加占位符属性到您已经创建的UITextView

textView.placeholder = "Put some detail"

这是…… 这里是它的外观(第三个框是一个UITextView)

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

然而,使用NSTextStorageDelegate的textStorage(_:didProcessEditing:range:changeInLength:)方法,你会收到任何文本更改的通知,即使它是通过编程完成的。就像这样分配它:

textView.textStorage.delegate = self

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

将它与@clearlight演示的UILabel技术结合起来,可以轻松地将整个UITextView的占位符实现包装成一个扩展。

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分配一个占位符字符串,甚至在接口生成器中也是如此。

出于清晰的原因,我省略了placeholderColor属性的实现,但是它可以用与placeholder类似的计算变量在多几行中实现。