Ste*_*enR 297 placeholder uitextview ios swift
我正在创建一个使用文本视图的应用程序.现在我希望文本视图的占位符类似于您可以为文本字段设置的占位符.你会如何使用swift实现这一目标.
有谁知道如何做到这一点?
Lyn*_*ott 591
针对Swift 4进行了更新
UITextView
本身并不具有占位符属性,因此您必须使用UITextViewDelegate
方法以编程方式创建和操作.我建议使用下面的解决方案#1或#2,具体取决于所需的行为.
注意:对于任一解决方案,请添加UITextViewDelegate
到类并设置textView.delegate = self
为使用文本视图的委托方法.
解决方案#1 - 如果您希望占位符在用户选择文本视图后立即消失:
首先设置UITextView
包含占位符文本并将其设置为浅灰色以模仿UITextField
占位符文本的外观.要么viewDidLoad
在文本视图的创建中,要么在文本视图的创建中执行此操作.
textView.text = "Placeholder"
textView.textColor = UIColor.lightGray
Run Code Online (Sandbox Code Playgroud)
然后,当用户开始编辑文本视图时,如果文本视图包含占位符(即,如果其文本颜色为浅灰色),则清除占位符文本并将文本颜色设置为黑色以适应用户的输入.
func textViewDidBeginEditing(_ textView: UITextView) {
if textView.textColor == UIColor.lightGray {
textView.text = nil
textView.textColor = UIColor.black
}
}
Run Code Online (Sandbox Code Playgroud)
然后,当用户完成编辑文本视图并将其作为第一个响应者重新签名时,如果文本视图为空,则通过重新添加占位符文本并将其颜色设置为浅灰色来重置其占位符.
func textViewDidEndEditing(_ textView: UITextView) {
if textView.text.isEmpty {
textView.text = "Placeholder"
textView.textColor = UIColor.lightGray
}
}
Run Code Online (Sandbox Code Playgroud)
解决方案#2 - 如果您希望占位符在文本视图为空时显示,即使选择了文本视图:
首先在以下位置设置占位符viewDidLoad
:
textView.text = "Placeholder"
textView.textColor = UIColor.lightGray
textView.becomeFirstResponder()
textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)
Run Code Online (Sandbox Code Playgroud)
(注意:由于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
}
Run Code Online (Sandbox Code Playgroud)
并且还实现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)
}
}
}
Run Code Online (Sandbox Code Playgroud)
cle*_*ght 214
通过跟踪文本视图字符数的更改,将占位符标签放置在文本视图上方,设置其字体,颜色和管理占位符可见性是简单,安全和可靠的.
斯威夫特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
}
}
Run Code Online (Sandbox Code Playgroud)
斯威夫特2:一样的,除了:italicSystemFontOfSize(textView.font.pointSize)
,UIColor.lightGrayColor
Jua*_*ero 22
迅速:
以编程方式或通过Interface Builder添加文本视图,如果是最后一个,则创建插座:
@IBOutlet weak var yourTextView: UITextView!
Run Code Online (Sandbox Code Playgroud)
请添加委托(UITextViewDelegate):
class ViewController: UIViewController, UITextViewDelegate {
Run Code Online (Sandbox Code Playgroud)
在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
Run Code Online (Sandbox Code Playgroud)
现在让我介绍一下神奇的部分,添加这个功能:
func textViewDidBeginEditing(_ textView: UITextView) {
if yourTextView.textColor == UIColor.lightGray {
yourTextView.text = ""
yourTextView.textColor = UIColor.black
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,这将在编辑开始时执行,我们将使用color属性检查条件以告知状态.nil
不建议将文字设置为i.在此之后,我们将文本颜色设置为所需的颜色,在本例中为黑色.
现在添加此功能:
func textViewDidEndEditing(_ textView: UITextView) {
if yourTextView.text == "" {
yourTextView.text = "Placeholder text ..."
yourTextView.textColor = UIColor.lightGray
}
}
Run Code Online (Sandbox Code Playgroud)
让我坚持,不要比较nil
,我已经尝试过,它不会起作用.然后,我们将值设置回占位符样式,并将颜色设置回占位符颜色,因为这是一个要检入的条件textViewDidBeginEditing
.
San*_*ill 15
使用此扩展这是在UITextView中设置占位符的最佳方法.但请确保您已将代理附加到TextView.您可以像这样设置Place holder: -
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
}
}
Run Code Online (Sandbox Code Playgroud)
yes*_*joe 14
我通过使用两个不同的文本视图来做到这一点
这个想法是,一旦用户开始在前景视图中键入内容,后台中的占位符就会消失(如果用户删除了所有内容,则会重新出现).因此它的行为与单行文本字段的占位符完全相同.
这是我用来的代码.请注意,descriptionField是用户键入的字段,descriptionPlaceholder是后台的字段.
func textViewDidChange(descriptionField: UITextView) {
if descriptionField.text.isEmpty == false {
descriptionPlaceholder.text = ""
} else {
descriptionPlaceholder.text = descriptionPlaceholderText
}
}
Run Code Online (Sandbox Code Playgroud)
Tru*_*iya 11
迅速:
添加您的TextView
@IBOutlet
:
@IBOutlet weak var txtViewMessage: UITextView!
Run Code Online (Sandbox Code Playgroud)
在该viewWillAppear
方法中,添加以下内容:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
txtViewMessage.delegate = self // Give TextViewMessage delegate Method
txtViewMessage.text = "Place Holder Name"
txtViewMessage.textColor = UIColor.lightGray
}
Run Code Online (Sandbox Code Playgroud)
请添加Delegate
使用扩展(UITextViewDelegate):
// MARK: - UITextViewDelegate
extension ViewController: UITextViewDelegate {
func textViewDidBeginEditing(_ textView: UITextView) {
if !txtViewMessage.text!.isEmpty && txtViewMessage.text! == "Place Holder Name" {
txtViewMessage.text = ""
txtViewMessage.textColor = UIColor.black
}
}
func textViewDidEndEditing(_ textView: UITextView) {
if txtViewMessage.text.isEmpty {
txtViewMessage.text = "Place Holder Name"
txtViewMessage.textColor = UIColor.lightGray
}
}
}
Run Code Online (Sandbox Code Playgroud)
我很惊讶没有人提及NSTextStorageDelegate
。UITextViewDelegate
的方法只会由用户互动触发,而不会以编程方式触发。例如,当您以text
编程方式设置文本视图的属性时,必须自行设置占位符的可见性,因为将不会调用委托方法。
但是,使用NSTextStorageDelegate
的textStorage(_:didProcessEditing:range:changeInLength:)
方法,即使文本已通过编程方式进行了更改,也会通知您。像这样分配它:
textView.textStorage.delegate = self
Run Code Online (Sandbox Code Playgroud)
(在中UITextView
,nil
默认情况下,此委托属性是默认值,因此它不会影响任何默认行为。)
与结合起来UILabel
@clearlight表明,人们可以很容易地包裹整个技术UITextView
的placeholder
实现为一个扩展。
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,使用称为的私有(嵌套)类PlaceholderLabel
。它根本没有实现,但是它为我们提供了一种识别占位符标签的方法,它比使用该tag
属性要“花哨得多” 。
使用这种方法,您仍然可以将的委托分配UITextView
给其他人。
您甚至不必更改文本视图的类。只需添加扩展,就可以为UITextView
项目中的每个对象(甚至在Interface Builder中)分配一个占位符字符串。
placeholderColor
为了清楚起见,我省略了属性的实现,但是可以再执行几行,并使用与相似的计算变量placeholder
。
小智 8
我试图从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
}
}
Run Code Online (Sandbox Code Playgroud)
用法
override func viewDidLoad() {
textView.delegate = self
textView.setPlaceholder()
}
func textViewDidChange(_ textView: UITextView) {
textView.checkPlaceholder()
}
Run Code Online (Sandbox Code Playgroud)
这是我用来完成这项工作的方法。
@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()
}
}
}
Run Code Online (Sandbox Code Playgroud)
我知道有几种与此类似的方法,但这种方法的好处在于它可以:
基于已经在此处提出的一些重要建议,我能够将以下轻量级,与Interface-Builder兼容的子类放在一起UITextView
:
UITextField
。text
属性的所有外部类完全分开。欢迎提出任何改进建议,尤其是如果有任何方法可以通过编程方式拉动iOS的占位符颜色,而不是对其进行硬编码的话。
Swift v5:
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 /sf/ask/2174042251/
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
}
}
Run Code Online (Sandbox Code Playgroud)
小智 6
视图加载中的SET值
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()
}
Run Code Online (Sandbox Code Playgroud)
小智 6
如果我们在项目中使用 pod,我们可以很容易地实现 textview PlaceHolder,IQKeyboardManagerSwift
只需遵循 4 个步骤
IQTextView
给我们的TextView
班级。IQKeyboardManagerSwift
在我们的控制器页面中导入textView
最后但并非最不重要的一点是,如果您愿意,请在控制器页面上创建 的出口:)textView
通过情节提要检查一些占位符文本还有一个解决方案(Swift 3):
import UIKit
protocol PlaceholderTextViewDelegate {
func placeholderTextViewDidChangeText(_ text:String)
func placeholderTextViewDidEndEditing(_ text:String)
}
final class PlaceholderTextView: UITextView {
var notifier:PlaceholderTextViewDelegate?
var placeholder: String? {
didSet {
placeholderLabel?.text = placeholder
}
}
var placeholderColor = UIColor.lightGray
var placeholderFont = UIFont.appMainFontForSize(14.0) {
didSet {
placeholderLabel?.font = placeholderFont
}
}
fileprivate var placeholderLabel: UILabel?
// MARK: - LifeCycle
init() {
super.init(frame: CGRect.zero, textContainer: nil)
awakeFromNib()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
self.delegate = self
NotificationCenter.default.addObserver(self, selector: #selector(PlaceholderTextView.textDidChangeHandler(notification:)), name: .UITextViewTextDidChange, object: nil)
placeholderLabel = UILabel()
placeholderLabel?.textColor = placeholderColor
placeholderLabel?.text = placeholder
placeholderLabel?.textAlignment = .left
placeholderLabel?.numberOfLines = 0
}
override func layoutSubviews() {
super.layoutSubviews()
placeholderLabel?.font = placeholderFont
var height:CGFloat = placeholderFont.lineHeight
if let data = placeholderLabel?.text {
let expectedDefaultWidth:CGFloat = bounds.size.width
let fontSize:CGFloat = placeholderFont.pointSize
let textView = UITextView()
textView.text = data
textView.font = UIFont.appMainFontForSize(fontSize)
let sizeForTextView = textView.sizeThatFits(CGSize(width: expectedDefaultWidth,
height: CGFloat.greatestFiniteMagnitude))
let expectedTextViewHeight = sizeForTextView.height
if expectedTextViewHeight > height {
height = expectedTextViewHeight
}
}
placeholderLabel?.frame = CGRect(x: 5, y: 0, width: bounds.size.width - 16, height: height)
if text.isEmpty {
addSubview(placeholderLabel!)
bringSubview(toFront: placeholderLabel!)
} else {
placeholderLabel?.removeFromSuperview()
}
}
func textDidChangeHandler(notification: Notification) {
layoutSubviews()
}
}
extension PlaceholderTextView : UITextViewDelegate {
// MARK: - UITextViewDelegate
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if(text == "\n") {
textView.resignFirstResponder()
return false
}
return true
}
func textViewDidChange(_ textView: UITextView) {
notifier?.placeholderTextViewDidChangeText(textView.text)
}
func textViewDidEndEditing(_ textView: UITextView) {
notifier?.placeholderTextViewDidEndEditing(textView.text)
}
}
Run Code Online (Sandbox Code Playgroud)
结果
对我有用的一个简单快速的解决方案是:
@IBDesignable
class PlaceHolderTextView: UITextView {
@IBInspectable var placeholder: String = "" {
didSet{
updatePlaceHolder()
}
}
@IBInspectable var placeholderColor: UIColor = UIColor.gray {
didSet {
updatePlaceHolder()
}
}
private var originalTextColor = UIColor.darkText
private var originalText: String = ""
private func updatePlaceHolder() {
if self.text == "" || self.text == placeholder {
self.text = placeholder
self.textColor = placeholderColor
if let color = self.textColor {
self.originalTextColor = color
}
self.originalText = ""
} else {
self.textColor = self.originalTextColor
self.originalText = self.text
}
}
override func becomeFirstResponder() -> Bool {
let result = super.becomeFirstResponder()
self.text = self.originalText
self.textColor = self.originalTextColor
return result
}
override func resignFirstResponder() -> Bool {
let result = super.resignFirstResponder()
updatePlaceHolder()
return result
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
218402 次 |
最近记录: |