ios 如何在 Swift 中的 UITextView 内添加占位符文本?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27652227/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 04:08:06  来源:igfitidea点击:

How can I add placeholder text inside of a UITextView in Swift?

iosswiftuitextviewplaceholder

提问by StevenR

I'm making an application which uses a UITextView. Now I want the Text View to have a placeholder similar to the one you can set for a Text Field. How would you accomplish this using Swift?

我正在制作一个使用UITextView. 现在我希望文本视图有一个类似于您可以为文本字段设置的占位符。您将如何使用 Swift 完成此任务?

回答by Lyndsey Scott

Updated for Swift 4

为 Swift 4 更新

UITextViewdoesn't inherently have a placeholder property so you'd have to create and manipulate one programmatically using UITextViewDelegatemethods. I recommend using either solution #1 or #2 below depending on the desired behavior.

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

Note: For either solution, add UITextViewDelegateto the class and set textView.delegate = selfto use the text view's delegate methods.

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



Solution #1- If you want the placeholder to disappear as soon as the user selects the text view:

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

First set the UITextViewto contain the placeholder text and set it to a light gray color to mimic the look of a UITextField's placeholder text. Either do so in the viewDidLoador upon the text view's creation.

首先将 设置UITextView为包含占位符文本并将其设置为浅灰色以模仿 aUITextField的占位符文本的外观。无论是在这样做viewDidLoad或者在文本视图的创建。

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

Then when the user begins to edit the text view, if the text view contains a placeholder (i.e. if its text color is light gray) clear the placeholder text and set the text color to black in order to accommodate the user's entry.

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

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

Then when the user finishes editing the text view and it's resigned as the first responder, if the text view is empty, reset its placeholder by re-adding the placeholder text and setting its color to light gray.

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

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


Solution #2- If you want the placeholder to show whenever the text view is empty, even if the text view's selected:

解决方案#2- 如果您希望占位符在文本视图为空时显示,即使文本视图被选中:

First set the placeholder in the viewDidLoad:

首先在 中设置占位符viewDidLoad

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

textView.becomeFirstResponder()

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

(Note: Since the OP wanted to have the text view selected as soon as the view loads, I incorporated text view selection into the above code. If this is not your desired behavior and you do not want the text view selected upon view load, remove the last two lines from the above code chunk.)

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

Then utilize the shouldChangeTextInRangeUITextViewDelegatemethod, like so:

然后使用该shouldChangeTextInRangeUITextViewDelegate方法,如下所示:

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
}

And also implement textViewDidChangeSelectionto prevent the user from changing the position of the cursor while the placeholder's visible. (Note: textViewDidChangeSelectionis called before the view loads so only check the text view's color if the window is visible):

并且还实现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)
        }
    }
}

回答by clearlight



Floating Placeholder

浮动占位符



It's simple, safe and reliable to position a placeholder label above a text view, set its font, color and manage placeholder visibility by tracking changes to the text view's character count.

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

Swift 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:Same, except:italicSystemFontOfSize(textView.font.pointSize), UIColor.lightGrayColor

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



回答by t4nhpt

Strongly recommend to use the KMPlaceholderTextViewlibrary. Very simple to use.

强烈推荐使用KMPlaceholderTextView库。使用起来非常简单。

回答by Juan Boero

Swift:

迅速:

Add your text view programmatically or via Interface Builder, if the last, create the outlet:

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

@IBOutlet weak var yourTextView: UITextView!

Please add the delegate (UITextViewDelegate):

请添加委托(UITextViewDelegate):

class ViewController: UIViewController, UITextViewDelegate {

In the viewDidLoad method, do add the following:

在 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

Now let me introduce the magic part, add this function:

现在让我介绍魔术部分,添加这个功能:

func textViewDidBeginEditing(_ textView: UITextView) {

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

Do note that this will execute whenever editing starts, there we will check conditions to tell the state, using the color property. Setting text to nili do not recommend. Right after that, we set the text color to desired, in this case, black.

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

Now add this function too:

现在也添加这个函数:

func textViewDidEndEditing(_ textView: UITextView) {

    if yourTextView.text == "" {

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

Let me insist, do not compare to nil, i have already tried that and it would not work. We then set the values back to placeholder style, and set the color back to placeholder color because it is a condition to check in textViewDidBeginEditing.

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

回答by Sandip Gill

Use This Extension this is the best way to set placeholder in UITextView. But make sure you have attached delegates to the TextView. You can set Place holder like this:-

使用此扩展,这是在 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.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
    }
}

回答by yesthisisjoe

I did this by using two different text views:

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

  1. One in the background that is used as a placeholder.
  2. One in the foreground (with a transparent background) that the user actually types in.
  1. 背景中用作占位符的一个。
  2. 用户实际输入的前景(具有透明背景)中的一个。

The idea is that once the user starts typing stuff in the foreground view, the placeholder in the background disappears (and reappears if the user deletes everything). So it behaves exactly like a placeholder for the single line text field.

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

Here's the code I used for it. Note that descriptionField is the field the user types in and descriptionPlaceholder is the one in the background.

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

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

回答by yesleon

I am surprised that no one mentioned NSTextStorageDelegate. UITextViewDelegate's methods will only be triggered by user interaction, but not programmatically. E.g. when you set a text view's textproperty programmatically, you'll have to set the placeholder's visibility yourself, because the delegate methods will not be called.

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

However, with NSTextStorageDelegate's textStorage(_:didProcessEditing:range:changeInLength:)method, you'll be notified of any change to the text, even if it's done programmatically. Just assign it like this:

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

textView.textStorage.delegate = self

(In UITextView, this delegate property is nilby default, so it won't affect any default behaviour.)

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

Combine it with the UILabeltechnique @clearlight demonstrates, one can easily wrap the whole UITextView's placeholderimplementation into an extension.

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

extension UITextView {

    private class PlaceholderLabel: UILabel { }

    private var placeholderLabel: PlaceholderLabel {
        if let label = subviews.compactMap( { 
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
    }
}
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( {
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
    }

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

Note that the use of a private (nested) class called PlaceholderLabel. It has no implementation at all, but it provides us a way to identify the placeholder label, which is far more 'swifty' than using the tagproperty.

请注意,使用名为PlaceholderLabel. 它根本没有实现,但它为我们提供了一种识别占位符标签的方法,这比使用tag属性要“迅速”得多。

With this approach, you can still assign the delegate of the UITextViewto someone else.

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

You don't even have to change your text views' classes. Just add the extension(s) and you will be able to assign a placeholder string to every UITextViewin your project, even in the Interface Builder.

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

I left out the implementation of a placeholderColorproperty for clarity reasons, but it can be implemented for just a few more lines with a similar computed variable to placeholder.

placeholderColor为清晰起见,我省略了属性的实现,但只需多几行即可实现,其计算变量与placeholder.

回答by TheNeil

Based on some of the great suggestions here already, I was able to put together the following lightweight, Interface-Builder-compatible subclass of UITextView, which:

基于这里的一些很好的建议,我能够将以下轻量级的、与 Interface-Builder 兼容的子类放在一起UITextView,其中:

  • Includes configurable placeholder text, styled just like that of UITextField.
  • Doesn't require any additional subviews or constraints.
  • Doesn't require any delegation or other behaviour from the ViewController.
  • Doesn't require any notifications.
  • Keeps that text fully separated from any outside classes looking at the field's textproperty.
  • 包括可配置的占位符文本,样式与UITextField.
  • 不需要任何额外的子视图或约束。
  • 不需要来自 ViewController 的任何委托或其他行为。
  • 不需要任何通知。
  • 使该文本与查看字段text属性的任何外部类完全分开。

Any improvement suggestions are welcome, especially if there's any way to pull iOS's placeholder color programatically, rather than hard-coding it.

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

Swift v5:

斯威夫特 v5:

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

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

回答by Leonardo

I tried to make code convenient from clearlight's answer.

我试图从clearlight回答中简化代码。

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

usage

用法

##代码##

回答by user2991582

SET value in view load

视图加载中的 SET 值

##代码##