如何在UITextView中添加占位符,类似于你可以为UITextField设置的占位符,在Swift中?
当前回答
以下是我解决这个问题的方法(Swift 4):
这个想法是做出最简单的解决方案,允许使用不同颜色的占位符,调整占位符大小,不会覆盖委托,同时保持所有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) }
}
其他回答
这是我用来完成这项工作的方法。
@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中不再显示“滚动视图有不明确的可滚动内容”的警告。 添加动画显示/隐藏占位符。
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)
}
}
}
斯威夫特
这是一个Swiftui TextView使用UIVIewRepresentable,具有占位符功能和边界颜色
struct TextView: UIViewRepresentable {
@Binding var text: String
var placeholderText: String
var textStyle: UIFont.TextStyle
func makeUIView(context: Context) -> UITextView {
let textView = UITextView()
textView.font = UIFont.preferredFont(forTextStyle: textStyle)
textView.autocapitalizationType = .sentences
textView.isSelectable = true
textView.isUserInteractionEnabled = true
textView.delegate = context.coordinator
textView.layer.borderWidth = 0.6
textView.layer.borderColor = UIColor.lightGray.cgColor
textView.layer.cornerRadius = 10
textView.text = placeholderText
textView.textColor = UIColor.lightGray
return textView
}
func updateUIView(_ uiView: UITextView, context: Context) {
uiView.text = text
uiView.font = UIFont.preferredFont(forTextStyle: textStyle)
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UITextViewDelegate {
var parent: TextView
init(_ parent: TextView) {
self.parent = parent
}
func textViewDidChange(_ textView: UITextView) {
self.parent.text = textView.text
}
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 = self.parent.placeholderText
textView.textColor = UIColor.lightGray
}
}
}
}
然后在你的视图中你可以这样使用它
TextView(text: self.$viewModel.addPostCommentText, placeholderText: "Share your story about this cash", textStyle: .body)
.padding()
.frame(height: 150)
我试着用clearlight的答案来简化代码。
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()
}
另一个解决方案是使用keyboardWillHide和keyboardWillShow通知,就像我做的那样。
首先,您需要分别处理viewWillAppear和viewWillAppear方法中的侦听和取消侦听通知(以处理内存泄漏)。
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
}
}
我发现这个解决方案是目前为止最好的,因为文本会在键盘出现时立即删除,而不是在用户开始输入时删除,这可能会导致混乱。
推荐文章
- 如何删除默认的导航栏空间在SwiftUI导航视图
- 如何在iOS中使用Swift编程segue
- Swift -整数转换为小时/分钟/秒
- 如何舍入一个双到最近的Int在迅速?
- 扁平化数组的数组在Swift
- Swift:声明一个空字典
- 在成功提交我的应用程序后,“太多符号文件”
- 从数组中随机选择一个元素
- 首先添加一个UIView,甚至是导航栏
- 我如何改变UIButton标题颜色?
- 在Swift中如何调用GCD主线程上的参数方法?
- NSLayoutConstraints是可动画的吗?
- iOS -构建失败,CocoaPods无法找到头文件
- CFNetwork SSLHandshake iOS 9失败
- swift语言中的结构与类