我做了一些研究,发现了这篇文章:Add placeholder text inside UITextView in Swift?
在我的代码中加入上面的例子,我在一个空白的 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)
}
}
}
}
但是我有两个错误,我一生都无法解决:
抱歉,如果这些是基本的,但我很难过。
答案 0 :(得分:0)
情侣笔记...
我认为 Shift 没有被释放,因为您从 False
返回 shouldChangeTextIn range
...所以 textView 没有与键盘完全通信。
至于重复的预测文本...我遇到过类似的问题。我的印象是文本已经插入,并且在调用 .selectedRange
时设置了一个新的 shouldChangeTextIn range
。所以代码(如所写)复制了它。
由于这些(和其他)复杂性,以下是使用 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)
}
}
注意——这是示例代码,不应被视为“生产就绪”。