簡體   English   中英

Swift 中 UITextView 中的占位符文本

[英]Placeholder text inside UITextView in Swift

我做了一些研究,發現了這篇文章: 在 Swift 中的 UITextView 中添加占位符文本?

將上述示例合並到我的代碼中,我在一個空白的 xcode UIKit 項目中有以下內容:

import UIKit

class ViewController: UIViewController {

    var sampleTextView       = UITextView()
    let placeholderText      = "Type Something"
    let placeholderTextColor = UIColor.lightGray
    let normalTextColor      = UIColor.label
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .systemGray
        
        sampleTextView.delegate     = self
        sampleTextView.text         = placeholderText
        sampleTextView.textColor    = placeholderTextColor
        sampleTextView.becomeFirstResponder()
        sampleTextView.selectedTextRange = sampleTextView.textRange(from: sampleTextView.beginningOfDocument, to: sampleTextView.beginningOfDocument)

        view.addSubview(sampleTextView)
        sampleTextView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            sampleTextView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0),
            sampleTextView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10),
            sampleTextView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10),
            sampleTextView.heightAnchor.constraint(equalToConstant: 100)
        ])
    }
}

extension ViewController: UITextViewDelegate {
    
    func textViewDidEndEditing(_ textView: UITextView) {
        if textView.text.isEmpty {
            textView.text = placeholderText
            textView.textColor = placeholderTextColor
        }
    }
    
    
    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 = placeholderText
            textView.textColor = placeholderTextColor

            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 == placeholderTextColor && !text.isEmpty {
            textView.textColor = normalTextColor
            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
    }
    
    
    func textViewDidChangeSelection(_ textView: UITextView) {
        if self.view.window != nil {
            if textView.textColor == placeholderTextColor {
                textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)
            }
        }
    }
}

但是我有兩個錯誤,對於我的生活,我無法解決:

  1. 首先是使用鍵盤工具欄上方的預測文本。 重復第一個選定的工作 - 請參見下面的屏幕截圖:

在此處輸入圖片說明

  1. 如果打字,鍵盤的 SHIFT 鍵按第一個字母的預期按下,但第二個字母也保持按下狀態 - 請參見下面的屏幕截圖:

在此處輸入圖片說明

抱歉,如果這些是基本的,但我很難過。

情侶筆記...

我認為Shift沒有被釋放,因為你從shouldChangeTextIn range返回False ......所以shouldChangeTextIn range沒有與鍵盤完全通信。

至於重復的預測文本……我遇到過類似的問題。 我的印象是文本已經插入,並且在調用shouldChangeTextIn range時設置了一個新的.selectedRange 所以代碼(如所寫)復制了它。

由於這些(和其他)復雜性,這里有一個使用CATextLayer作為“占位符”文本的示例:

class PlaceHolderTestViewController: UIViewController, UITextViewDelegate {

    var sampleTextView       = UITextView()
    
    let placeholderText      = "Type Something"
    let placeholderTextColor = UIColor.lightGray
    let normalTextColor      = UIColor.label
    
    let textLayer = CATextLayer()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .systemGray
        
        if self.navigationController != nil {
            // add a "Done" navigation bar button
            let btn = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneTap))
            self.navigationItem.rightBarButtonItem = btn
        }
        
        view.addSubview(sampleTextView)
        sampleTextView.translatesAutoresizingMaskIntoConstraints = false
        
        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            sampleTextView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40),
            sampleTextView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 10),
            sampleTextView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -10),
            sampleTextView.heightAnchor.constraint(equalToConstant: 100)
        ])

        sampleTextView.font = .systemFont(ofSize: 16.0)

        // textLayer properties
        textLayer.contentsScale = UIScreen.main.scale
        textLayer.alignmentMode = .left
        textLayer.isWrapped = true
        textLayer.foregroundColor = placeholderTextColor.cgColor
        textLayer.string = placeholderText
    
        if let fnt = sampleTextView.font {
            textLayer.fontSize = fnt.pointSize
        } else {
            textLayer.fontSize = 12.0
        }

        // insert the textLayer
        sampleTextView.layer.insertSublayer(textLayer, at: 0)

        // set delegate to self
        sampleTextView.delegate = self
    }
    
    func textViewDidChange(_ textView: UITextView) {
        textLayer.opacity = textView.text.isEmpty ? 1.0 : 0.0
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        // update textLayer frame here
        textLayer.frame = sampleTextView.bounds.insetBy(
            dx: sampleTextView.textContainerInset.left + sampleTextView.textContainer.lineFragmentPadding,
            dy: sampleTextView.textContainerInset.top
        )
    }
    
    @objc func doneTap() -> Void {
        view.endEditing(true)
    }
    
}

並且,這是一個子類化UITextView以使其更易於重用的示例 - 以及輕松地一次允許多個 textView:

class PlaceholderTextView: UITextView, UITextViewDelegate {
    
    private let textLayer = CATextLayer()
    
    public var placeholderText: String = "" {
        didSet {
            textLayer.string = placeholderText
            setNeedsLayout()
        }
    }
    public var placeholderTextColor: UIColor = .lightGray {
        didSet {
            textLayer.foregroundColor = placeholderTextColor.cgColor
            setNeedsLayout()
        }
    }
    override var font: UIFont? {
        didSet {
            if let fnt = self.font {
                textLayer.fontSize = fnt.pointSize
            } else {
                textLayer.fontSize = 12.0
            }
        }
    }
    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    private func commonInit() -> Void {
        // textLayer properties
        textLayer.contentsScale = UIScreen.main.scale
        textLayer.alignmentMode = .left
        textLayer.isWrapped = true
        textLayer.foregroundColor = placeholderTextColor.cgColor

        if let fnt = self.font {
            textLayer.fontSize = fnt.pointSize
        } else {
            textLayer.fontSize = 12.0
        }

        // insert the textLayer
        layer.insertSublayer(textLayer, at: 0)

        // set delegate to self
        delegate = self
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        textLayer.frame = bounds.insetBy(
            dx: textContainerInset.left + textContainer.lineFragmentPadding,
            dy: textContainerInset.top
        )
    }
    func textViewDidChange(_ textView: UITextView) {
        // show / hide the textLayer
        textLayer.opacity = textView.text.isEmpty ? 1.0 : 0.0
    }
}

class PlaceHolderTestViewController: UIViewController {

    let sampleTextView = PlaceholderTextView()

    let placeholderText      = "Type Something"
    let placeholderTextColor = UIColor.lightGray
    let normalTextColor      = UIColor.label

    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .systemGray
        
        if self.navigationController != nil {
            // add a "Done" navigation bar button
            let btn = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneTap))
            self.navigationItem.rightBarButtonItem = btn
        }

        view.addSubview(sampleTextView)
        sampleTextView.translatesAutoresizingMaskIntoConstraints = false

        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            sampleTextView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40),
            sampleTextView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 10),
            sampleTextView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -10),
            sampleTextView.heightAnchor.constraint(equalToConstant: 100)
        ])

        sampleTextView.font = .systemFont(ofSize: 16.0)
        sampleTextView.textColor = normalTextColor
        sampleTextView.placeholderTextColor = placeholderTextColor
        sampleTextView.placeholderText = placeholderText

    }
    
    @objc func doneTap() -> Void {
        view.endEditing(true)
    }

}

注意——這只是示例代碼,不應被視為“生產就緒”。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM