Ich mache eine Anwendung, die a verwendet UITextView
. Jetzt möchte ich, dass die Textansicht einen Platzhalter hat, der dem ähnlich ist, den Sie für ein Textfeld festlegen können. Wie würden Sie dies mit Swift erreichen?
Ich mache eine Anwendung, die a verwendet UITextView
. Jetzt möchte ich, dass die Textansicht einen Platzhalter hat, der dem ähnlich ist, den Sie für ein Textfeld festlegen können. Wie würden Sie dies mit Swift erreichen?
Antworten:
Aktualisiert für Swift 4
UITextView
hat von Natur aus keine Platzhalter-Eigenschaft, daher müssten Sie eine programmgesteuert mithilfe von UITextViewDelegate
Methoden erstellen und bearbeiten . Ich empfehle, je nach gewünschtem Verhalten entweder die Lösung Nr. 1 oder Nr. 2 zu verwenden.
Hinweis: Fügen Sie für beide Lösungen UITextViewDelegate
der Klasse hinzu und legen Sie fest textView.delegate = self
, dass die Delegatmethoden der Textansicht verwendet werden sollen.
Lösung 1 - Wenn der Platzhalter verschwinden soll, sobald der Benutzer die Textansicht auswählt:
Stellen Sie zuerst das so ein UITextView
, dass es den Platzhaltertext enthält, und setzen Sie es auf eine hellgraue Farbe, um das Aussehen des UITextField
Platzhaltertextes eines zu imitieren . Entweder in der viewDidLoad
oder bei der Erstellung der Textansicht.
textView.text = "Placeholder"
textView.textColor = UIColor.lightGray
Wenn der Benutzer mit der Bearbeitung der Textansicht beginnt und die Textansicht einen Platzhalter enthält (dh wenn seine Textfarbe hellgrau ist), löschen Sie den Platzhaltertext und setzen Sie die Textfarbe auf Schwarz, um die Eingabe des Benutzers zu berücksichtigen.
func textViewDidBeginEditing(_ textView: UITextView) {
if textView.textColor == UIColor.lightGray {
textView.text = nil
textView.textColor = UIColor.black
}
}
Wenn der Benutzer die Bearbeitung der Textansicht abgeschlossen hat und als Ersthelfer zurückgetreten ist und die Textansicht leer ist, setzen Sie den Platzhalter zurück, indem Sie den Platzhaltertext erneut hinzufügen und seine Farbe auf hellgrau setzen.
func textViewDidEndEditing(_ textView: UITextView) {
if textView.text.isEmpty {
textView.text = "Placeholder"
textView.textColor = UIColor.lightGray
}
}
Lösung 2 - Wenn der Platzhalter angezeigt werden soll, wenn die Textansicht leer ist, auch wenn die Textansicht ausgewählt ist:
Setzen Sie zuerst den Platzhalter in viewDidLoad
:
textView.text = "Placeholder"
textView.textColor = UIColor.lightGray
textView.becomeFirstResponder()
textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)
(Hinweis: Da das OP wollte, dass die Textansicht ausgewählt wird, sobald die Ansicht geladen wird, habe ich die Auswahl der Textansicht in den obigen Code aufgenommen. Wenn dies nicht Ihr gewünschtes Verhalten ist und Sie nicht möchten, dass die Textansicht beim Laden der Ansicht ausgewählt wird, Entfernen Sie die letzten beiden Zeilen aus dem obigen Codeblock.)
Verwenden Sie dann die shouldChangeTextInRange
UITextViewDelegate
Methode wie folgt:
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
}
Und implementieren Sie auch, um textViewDidChangeSelection
zu verhindern, dass der Benutzer die Position des Cursors ändert, während der Platzhalter sichtbar ist. (Hinweis: textViewDidChangeSelection
Wird aufgerufen, bevor die Ansicht geladen wird. Überprüfen Sie daher die Farbe der Textansicht nur, wenn das Fenster sichtbar ist.)
func textViewDidChangeSelection(_ textView: UITextView) {
if self.view.window != nil {
if textView.textColor == UIColor.lightGray {
textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)
}
}
}
yourTextField.delegate = self
. Wenn Sie dies nicht tun, funktionieren textViewDidBeginEditing
die textViewDidEndEditing
Funktionen und nicht.
Cannot convert value of type 'NSRange' (aka '_NSRange') to expected argument type 'Range<String.Index>' (aka 'Range<String.CharacterView.Index>')
.
let currentText = textView.text as NSString?
. Transformiere die let updatedText =
Linie in let updatedText = currentText?.replacingCharacters(in: range, with: text)
. Zum Schluss transformieren Sie die if updatedText.isEmpty
Linie in if (updatedText?.isEmpty)! {
. Das sollte den Trick machen!
textView.selectedTextRange
von innen func textViewDidChangeSelection(_ textView: UITextView)
verursacht eine Endlosschleife ...
Es ist einfach, sicher und zuverlässig, eine Platzhalterbezeichnung über einer Textansicht zu positionieren, Schriftart und Farbe festzulegen und die Sichtbarkeit von Platzhaltern zu verwalten, indem Änderungen an der Zeichenanzahl der Textansicht verfolgt werden.
Swift 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: Gleiche, ausgenommen italicSystemFontOfSize(textView.font.pointSize)
,UIColor.lightGrayColor
Es wird dringend empfohlen, die KMPlaceholderTextView- Bibliothek zu verwenden. Sehr einfach zu bedienen.
Schnell:
Fügen Sie Ihre Textansicht programmgesteuert oder über den Interface Builder hinzu. Wenn dies der letzte ist, erstellen Sie den Ausgang:
@IBOutlet weak var yourTextView: UITextView!
Bitte fügen Sie den Delegaten (UITextViewDelegate) hinzu:
class ViewController: UIViewController, UITextViewDelegate {
Fügen Sie in der viewDidLoad-Methode Folgendes hinzu:
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
Lassen Sie mich nun den magischen Teil vorstellen und diese Funktion hinzufügen:
func textViewDidBeginEditing(_ textView: UITextView) {
if yourTextView.textColor == UIColor.lightGray {
yourTextView.text = ""
yourTextView.textColor = UIColor.black
}
}
Beachten Sie, dass dies immer dann ausgeführt wird, wenn die Bearbeitung beginnt. Dort überprüfen wir die Bedingungen, um den Status mithilfe der Farbeigenschaft zu ermitteln. Das Setzen von Text auf nil
Ich empfehle nicht. Gleich danach setzen wir die Textfarbe auf die gewünschte, in diesem Fall Schwarz.
Fügen Sie nun auch diese Funktion hinzu:
func textViewDidEndEditing(_ textView: UITextView) {
if yourTextView.text == "" {
yourTextView.text = "Placeholder text ..."
yourTextView.textColor = UIColor.lightGray
}
}
Lassen Sie mich darauf bestehen, nicht zu vergleichen nil
, ich habe das bereits versucht und es würde nicht funktionieren. Anschließend setzen wir die Werte wieder auf den Platzhalterstil und die Farbe wieder auf die Platzhalterfarbe, da dies eine Bedingung zum Einchecken ist textViewDidBeginEditing
.
Verwenden Sie diese Erweiterung. Dies ist der beste Weg, um Platzhalter in UITextView festzulegen. Stellen Sie jedoch sicher, dass Sie Delegierte an die Textansicht angehängt haben. Sie können den Platzhalter wie folgt einstellen: -
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
}
}
Ich bin überrascht, dass niemand erwähnt hat NSTextStorageDelegate
. UITextViewDelegate
Die Methoden werden nur durch Benutzerinteraktion ausgelöst, nicht jedoch programmgesteuert. Wenn Sie beispielsweise die text
Eigenschaft einer Textansicht programmgesteuert festlegen, müssen Sie die Sichtbarkeit des Platzhalters selbst festlegen, da die Delegatmethoden nicht aufgerufen werden.
Doch mit NSTextStorageDelegate
‚s - textStorage(_:didProcessEditing:range:changeInLength:)
Methode, werden Sie auf den Text über jede Änderung informiert werden, auch wenn es programmatisch fertig ist. Weisen Sie es einfach so zu:
textView.textStorage.delegate = self
(In UITextView
ist diese Delegate-Eigenschaft nil
standardmäßig, sodass sie kein Standardverhalten beeinflusst.)
Kombinieren Sie es mit der UILabel
Technik @clearlight zeigt, kann man leicht die ganze wickeln UITextView
‚s placeholder
Implementierung in eine Verlängerung.
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
}
}
}
Beachten Sie, dass die Verwendung einer privaten (verschachtelten) Klasse aufgerufen wird PlaceholderLabel
. Es gibt überhaupt keine Implementierung, aber es bietet uns eine Möglichkeit, die Platzhalterbezeichnung zu identifizieren, die weitaus schneller ist als die Verwendung der tag
Eigenschaft.
Mit diesem Ansatz können Sie den Delegaten von weiterhin UITextView
einer anderen Person zuweisen .
Sie müssen nicht einmal die Klassen Ihrer Textansichten ändern. Fügen Sie einfach die Erweiterung (en) hinzu, und Sie können jedem UITextView
in Ihrem Projekt eine Platzhalterzeichenfolge zuweisen , auch im Interface Builder.
Ich habe die Implementierung einer placeholderColor
Eigenschaft aus Gründen der Klarheit weggelassen, aber sie kann nur für ein paar weitere Zeilen mit einer ähnlichen berechneten Variablen wie implementiert werden placeholder
.
textView.textStorage.delegate = self
Dies erfordert in einem View-Controller, dass wir diesen View-Controller mit binden NSTextStorageDelegate
. Es ist wirklich erforderlich?
NSTextStorageDelegate
, nicht der View Controller.
Ich habe dazu zwei verschiedene Textansichten verwendet:
Die Idee ist, dass der Platzhalter im Hintergrund verschwindet (und wieder angezeigt wird, wenn der Benutzer alles löscht), sobald der Benutzer mit der Eingabe von Inhalten in der Vordergrundansicht beginnt. Es verhält sich also genau wie ein Platzhalter für das einzeilige Textfeld.
Hier ist der Code, den ich dafür verwendet habe. Beachten Sie, dass descriptionField das Feld ist, in das der Benutzer eingibt, und descriptionPlaceholder das Feld im Hintergrund ist.
func textViewDidChange(descriptionField: UITextView) {
if descriptionField.text.isEmpty == false {
descriptionPlaceholder.text = ""
} else {
descriptionPlaceholder.text = descriptionPlaceholderText
}
}
Aufgrund einiger der großartigen Vorschläge hier konnte ich die folgende leichte, Interface-Builder-kompatible Unterklasse zusammenstellen, von UITextView
der:
UITextField
.text
Feldeigenschaft betrachten.Verbesserungsvorschläge sind willkommen, insbesondere wenn es eine Möglichkeit gibt, die Platzhalterfarbe von iOS programmgesteuert abzurufen, anstatt sie fest zu codieren.
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 /programming/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
}
}
SET-Wert beim Laden der Ansicht
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()
}
Ich habe versucht, den Code anhand der Antwort von clearlight zu vereinfachen .
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
}
}
Verwendungszweck
override func viewDidLoad() {
textView.delegate = self
textView.setPlaceholder()
}
func textViewDidChange(_ textView: UITextView) {
textView.checkPlaceholder()
}
Eine weitere Lösung (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)
}
}
Ergebnis
Eine einfache und schnelle Lösung, die für mich funktioniert, ist:
@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
}
}
Hier ist, was ich für diesen Job benutze.
@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)
}
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()
}
}
}
Ich weiß, dass es mehrere ähnliche Ansätze gibt, aber die Vorteile dieses Ansatzes sind:
Swift 3.2
extension EditProfileVC:UITextViewDelegate{
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
}
}
}
Wenn der Benutzer mit der Bearbeitung des Aufrufs textViewDidBeginEditing beginnt und dann überprüft, ob die Farbe von Textgrau bedeutet, dass der Benutzer nichts geschrieben hat, setzen Sie die Textansicht auf Null und ändern Sie die Farbe für die SMS des Benutzers in Schwarz.
Wenn der Benutzer die Bearbeitung von textViewDidEndEditing beendet und aufruft und prüft, ob der Benutzer nichts in die Textansicht schreibt, wird der Text als graue Farbe mit dem Text "PlaceHolder" festgelegt.
Hier ist meine Art, dieses Problem zu lösen ( Swift 4 ):
Die Idee war, eine möglichst einfache Lösung zu finden, die es ermöglicht, Platzhalter in verschiedenen Farben zu verwenden, die Größe auf Platzhaltergröße zu ändern und nicht zu überschreiben, während delegate
alle UITextView
Funktionen wie erwartet funktionieren.
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) }
}
Ich weiß nicht, warum die Leute dieses Problem so sehr komplizieren ... Es ist ziemlich einfach und unkompliziert. Hier ist eine Unterklasse von UITextView, die die angeforderte Funktionalität bereitstellt.
- (void)customInit
{
self.contentMode = UIViewContentModeRedraw;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:nil];
}
- (void)textChanged:(NSNotification *)notification
{
if (notification.object == self) {
if(self.textStorage.length != 0 || !self.textStorage.length) {
[self setNeedsDisplay];
}
}
}
#pragma mark - Setters
- (void)setPlaceholderText:(NSString *)placeholderText withFont:(UIFont *)font
{
self.placeholderText = placeholderText;
self.placeholderTextFont = font;
}
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
[[UIColor lightGrayColor] setFill];
if (self.textStorage.length != 0) {
return;
}
CGRect inset = CGRectInset(rect, 8, 8);//Default rect insets for textView
NSDictionary *attributes = @{NSFontAttributeName: self.placeholderTextFont, NSForegroundColorAttributeName: [UIColor grayColor]};
[self.placeholderText drawInRect:inset withAttributes:attributes];
}`
Dies ist meine gebrauchsfertige Lösung, wenn Sie mit mehreren Textansichten arbeiten
func textViewShouldBeginEditing(textView: UITextView) -> Bool {
// Set cursor to the beginning if placeholder is set
if textView.textColor == UIColor.lightGrayColor() {
textView.selectedTextRange = textView.textRangeFromPosition(textView.beginningOfDocument, toPosition: textView.beginningOfDocument)
}
return true
}
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
// Remove placeholder
if textView.textColor == UIColor.lightGrayColor() && text.characters.count > 0 {
textView.text = ""
textView.textColor = UIColor.blackColor()
}
if text == "\n" {
textView.resignFirstResponder()
return false
}
return true
}
func textViewDidChange(textView: UITextView) {
// Set placeholder if text is empty
if textView.text.isEmpty {
textView.text = NSLocalizedString("Hint", comment: "hint")
textView.textColor = UIColor.lightGrayColor()
textView.selectedTextRange = textView.textRangeFromPosition(textView.beginningOfDocument, toPosition: textView.beginningOfDocument)
}
}
func textViewDidChangeSelection(textView: UITextView) {
// Set cursor to the beginning if placeholder is set
let firstPosition = textView.textRangeFromPosition(textView.beginningOfDocument, toPosition: textView.beginningOfDocument)
// Do not change position recursively
if textView.textColor == UIColor.lightGrayColor() && textView.selectedTextRange != firstPosition {
textView.selectedTextRange = firstPosition
}
}
Swift 3.1
Diese Erweiterung hat bei mir gut funktioniert: https://github.com/devxoul/UITextView-Placeholder
Hier ist ein Code-Snippet:
Installieren Sie es über Pod:
pod 'UITextView+Placeholder', '~> 1.2'
Importieren Sie es in Ihre Klasse
import UITextView_Placeholder
Und fügen Sie placeholder
Ihrem bereits erstellten Objekt eine Eigenschaft hinzuUITextView
textView.placeholder = "Put some detail"
Das ist es ... Hier, wie es aussieht (dritte Box ist a UITextView
)
Im Gegensatz zu fast jeder Antwort auf dieses Thema liegt , UITextView
hat einen Platzhalter Eigenschaft. Aus Gründen, die ich nicht verstehe, wird es nur in IB als solches angezeigt:
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="placeholder" value="My Placeholder"/>
</userDefinedRuntimeAttributes>
Wenn Sie also Storyboards verwenden und ein statischer Platzhalter ausreicht, legen Sie die Eigenschaft einfach im Inspektor fest.
Sie können diese Eigenschaft auch im folgenden Code festlegen:
textView.setValue("My Placeholder", forKeyPath: "placeholder")
Seine trübe , da dies Wetter über private API zugegriffen wird , wie die Eigenschaft wird ausgesetzt.
Ich habe nicht versucht, mit dieser Methode einzureichen. Aber ich werde diesen Weg in Kürze einreichen und diese Antwort entsprechend aktualisieren.
AKTUALISIEREN:
Ich habe diesen Code in mehreren Versionen ohne Probleme von Apple ausgeliefert.
UPDATE: Dies funktioniert nur in Xcode vor 11.2
UITextField
NICHT UITextView
Bitte lesen Sie die Fragen / Antworten genauer durch.
In ios gibt es keine solche Eigenschaft, um Platzhalter direkt in TextView hinzuzufügen. Sie können stattdessen eine Beschriftung hinzufügen und die Änderung in textView ein- / ausblenden. SWIFT 2.0 und stellen Sie sicher, dass Sie das textviewdelegate implementieren
func textViewDidChange(TextView: UITextView)
{
if txtShortDescription.text == ""
{
self.lblShortDescription.hidden = false
}
else
{
self.lblShortDescription.hidden = true
}
}
Swift - Ich habe eine Klasse geschrieben, die UITextView geerbt hat, und ein UILabel als Unteransicht hinzugefügt, um als Platzhalter zu fungieren.
import UIKit
@IBDesignable
class HintedTextView: UITextView {
@IBInspectable var hintText: String = "hintText" {
didSet{
hintLabel.text = hintText
}
}
private lazy var hintLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFontOfSize(16)
label.textColor = UIColor.lightGrayColor()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setupView()
}
private func setupView() {
translatesAutoresizingMaskIntoConstraints = false
delegate = self
font = UIFont.systemFontOfSize(16)
addSubview(hintLabel)
NSLayoutConstraint.activateConstraints([
hintLabel.leftAnchor.constraintEqualToAnchor(leftAnchor, constant: 4),
hintLabel.rightAnchor.constraintEqualToAnchor(rightAnchor, constant: 8),
hintLabel.topAnchor.constraintEqualToAnchor(topAnchor, constant: 4),
hintLabel.heightAnchor.constraintEqualToConstant(30)
])
}
override func layoutSubviews() {
super.layoutSubviews()
setupView()
}
}
Ich mag die Lösung von @ nerdist. Auf dieser Grundlage habe ich eine Erweiterung erstellt für UITextView
:
import Foundation
import UIKit
extension UITextView
{
private func add(_ placeholder: UILabel) {
for view in self.subviews {
if let lbl = view as? UILabel {
if lbl.text == placeholder.text {
lbl.removeFromSuperview()
}
}
}
self.addSubview(placeholder)
}
func addPlaceholder(_ placeholder: UILabel?) {
if let ph = placeholder {
ph.numberOfLines = 0 // support for multiple lines
ph.font = UIFont.italicSystemFont(ofSize: (self.font?.pointSize)!)
ph.sizeToFit()
self.add(ph)
ph.frame.origin = CGPoint(x: 5, y: (self.font?.pointSize)! / 2)
ph.textColor = UIColor(white: 0, alpha: 0.3)
updateVisibility(ph)
}
}
func updateVisibility(_ placeHolder: UILabel?) {
if let ph = placeHolder {
ph.isHidden = !self.text.isEmpty
}
}
}
In einer ViewController-Klasse verwende ich sie beispielsweise folgendermaßen:
class MyViewController: UIViewController, UITextViewDelegate {
private var notePlaceholder: UILabel!
@IBOutlet weak var txtNote: UITextView!
...
// UIViewController
override func viewDidLoad() {
notePlaceholder = UILabel()
notePlaceholder.text = "title\nsubtitle\nmore..."
txtNote.addPlaceholder(notePlaceholder)
...
}
// UITextViewDelegate
func textViewDidChange(_ textView: UITextView) {
txtNote.updateVisbility(notePlaceholder)
...
}
Platzhalter auf UITextview!
UPDATE :
Wenn Sie den Text der Textansicht im Code ändern, denken Sie daran, die updateVisibitly-Methode aufzurufen, um Platzhalter auszublenden:
txtNote.text = "something in code"
txtNote.updateVisibility(self.notePlaceholder) // hide placeholder if text is not empty.
Um zu verhindern, dass der Platzhalter mehrmals hinzugefügt wird, wird eine private add()
Funktion hinzugefügt extension
.
In swift2.2:
public class CustomTextView: UITextView {
private struct Constants {
static let defaultiOSPlaceholderColor = UIColor(red: 0.0, green: 0.0, blue: 0.0980392, alpha: 0.22)
}
private let placeholderLabel: UILabel = UILabel()
private var placeholderLabelConstraints = [NSLayoutConstraint]()
@IBInspectable public var placeholder: String = "" {
didSet {
placeholderLabel.text = placeholder
}
}
@IBInspectable public var placeholderColor: UIColor = CustomTextView.Constants.defaultiOSPlaceholderColor {
didSet {
placeholderLabel.textColor = placeholderColor
}
}
override public var font: UIFont! {
didSet {
placeholderLabel.font = font
}
}
override public var textAlignment: NSTextAlignment {
didSet {
placeholderLabel.textAlignment = textAlignment
}
}
override public var text: String! {
didSet {
textDidChange()
}
}
override public var attributedText: NSAttributedString! {
didSet {
textDidChange()
}
}
override public var textContainerInset: UIEdgeInsets {
didSet {
updateConstraintsForPlaceholderLabel()
}
}
override public init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
commonInit()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
NSNotificationCenter.defaultCenter().addObserver(self,
selector: #selector(textDidChange),
name: UITextViewTextDidChangeNotification,
object: nil)
placeholderLabel.font = font
placeholderLabel.textColor = placeholderColor
placeholderLabel.textAlignment = textAlignment
placeholderLabel.text = placeholder
placeholderLabel.numberOfLines = 0
placeholderLabel.backgroundColor = UIColor.clearColor()
placeholderLabel.translatesAutoresizingMaskIntoConstraints = false
addSubview(placeholderLabel)
updateConstraintsForPlaceholderLabel()
}
private func updateConstraintsForPlaceholderLabel() {
var newConstraints = NSLayoutConstraint.constraintsWithVisualFormat("H:|-(\(textContainerInset.left + textContainer.lineFragmentPadding))-[placeholder]",
options: [],
metrics: nil,
views: ["placeholder": placeholderLabel])
newConstraints += NSLayoutConstraint.constraintsWithVisualFormat("V:|-(\(textContainerInset.top))-[placeholder]",
options: [],
metrics: nil,
views: ["placeholder": placeholderLabel])
newConstraints.append(NSLayoutConstraint(
item: placeholderLabel,
attribute: .Width,
relatedBy: .Equal,
toItem: self,
attribute: .Width,
multiplier: 1.0,
constant: -(textContainerInset.left + textContainerInset.right + textContainer.lineFragmentPadding * 2.0)
))
removeConstraints(placeholderLabelConstraints)
addConstraints(newConstraints)
placeholderLabelConstraints = newConstraints
}
@objc private func textDidChange() {
placeholderLabel.hidden = !text.isEmpty
}
public override func layoutSubviews() {
super.layoutSubviews()
placeholderLabel.preferredMaxLayoutWidth = textContainer.size.width - textContainer.lineFragmentPadding * 2.0
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self,
name: UITextViewTextDidChangeNotification,
object: nil)
}
}}
In swift3:
import UIKit
Klasse CustomTextView: UITextView {
private struct Constants {
static let defaultiOSPlaceholderColor = UIColor(red: 0.0, green: 0.0, blue: 0.0980392, alpha: 0.22)
}
private let placeholderLabel: UILabel = UILabel()
private var placeholderLabelConstraints = [NSLayoutConstraint]()
@IBInspectable public var placeholder: String = "" {
didSet {
placeholderLabel.text = placeholder
}
}
@IBInspectable public var placeholderColor: UIColor = CustomTextView.Constants.defaultiOSPlaceholderColor {
didSet {
placeholderLabel.textColor = placeholderColor
}
}
override public var font: UIFont! {
didSet {
placeholderLabel.font = font
}
}
override public var textAlignment: NSTextAlignment {
didSet {
placeholderLabel.textAlignment = textAlignment
}
}
override public var text: String! {
didSet {
textDidChange()
}
}
override public var attributedText: NSAttributedString! {
didSet {
textDidChange()
}
}
override public var textContainerInset: UIEdgeInsets {
didSet {
updateConstraintsForPlaceholderLabel()
}
}
override public init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
commonInit()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
NotificationCenter.default.addObserver(self,
selector: #selector(textDidChange),
name: NSNotification.Name.UITextViewTextDidChange,
object: nil)
placeholderLabel.font = font
placeholderLabel.textColor = placeholderColor
placeholderLabel.textAlignment = textAlignment
placeholderLabel.text = placeholder
placeholderLabel.numberOfLines = 0
placeholderLabel.backgroundColor = UIColor.clear
placeholderLabel.translatesAutoresizingMaskIntoConstraints = false
addSubview(placeholderLabel)
updateConstraintsForPlaceholderLabel()
}
private func updateConstraintsForPlaceholderLabel() {
var newConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-(\(textContainerInset.left + textContainer.lineFragmentPadding))-[placeholder]",
options: [],
metrics: nil,
views: ["placeholder": placeholderLabel])
newConstraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|-(\(textContainerInset.top))-[placeholder]",
options: [],
metrics: nil,
views: ["placeholder": placeholderLabel])
newConstraints.append(NSLayoutConstraint(
item: placeholderLabel,
attribute: .width,
relatedBy: .equal,
toItem: self,
attribute: .width,
multiplier: 1.0,
constant: -(textContainerInset.left + textContainerInset.right + textContainer.lineFragmentPadding * 2.0)
))
removeConstraints(placeholderLabelConstraints)
addConstraints(newConstraints)
placeholderLabelConstraints = newConstraints
}
@objc private func textDidChange() {
placeholderLabel.isHidden = !text.isEmpty
}
public override func layoutSubviews() {
super.layoutSubviews()
placeholderLabel.preferredMaxLayoutWidth = textContainer.size.width - textContainer.lineFragmentPadding * 2.0
}
deinit {
NotificationCenter.default.removeObserver(self,
name: NSNotification.Name.UITextViewTextDidChange,
object: nil)
}
}}
Ich habe schnell eine Klasse geschrieben. Sie müssen diese Klasse bei Bedarf importieren.
Ich kann wegen des guten Rufs keinen Kommentar hinzufügen. Fügen Sie in @clearlight answer einen weiteren Delegiertenbedarf hinzu.
func textViewDidBeginEditing(_ textView: UITextView) {
cell.placeholderLabel.isHidden = !textView.text.isEmpty
}
ist notwendig
weil textViewDidChange
wird nicht beim ersten mal aufgerufen
Nein, für die Textansicht ist kein Platzhalter verfügbar. Sie müssen eine Beschriftung darüber setzen, wenn der Benutzer in die Textansicht eingibt, diese dann ausblenden oder als Standardwert festlegen, wenn der Benutzer alle Werte entfernt.
func setPlaceholder(){
var placeholderLabel = UILabel()
placeholderLabel.text = "Describe your need..."
placeholderLabel.font = UIFont.init(name: "Lato-Regular", size: 15.0) ?? UIFont.boldSystemFont(ofSize: 14.0)
placeholderLabel.sizeToFit()
descriptionTextView.addSubview(placeholderLabel)
placeholderLabel.frame.origin = CGPoint(x: 5, y: (descriptionTextView.font?.pointSize)! / 2)
placeholderLabel.textColor = UIColor.lightGray
placeholderLabel.isHidden = !descriptionTextView.text.isEmpty
}
//Delegate Method.
func textViewDidChange(_ textView: UITextView) {
placeholderLabel.isHidden = !textView.text.isEmpty
}
Ich musste die Warteschlange absenden, damit mein Platzhaltertext nach Abschluss der Bearbeitung wieder angezeigt wurde.
func textViewDidBeginEditing(_ textView: UITextView) {
if textView.text == "Description" {
textView.text = nil
}
}
func textViewDidEndEditing(_ textView: UITextView) {
if textView.text.isEmpty {
DispatchQueue.main.async {
textView.text = "Description"
}
}
}
Schnell:
Fügen Sie hinzu TextView
@IBOutlet
:
@IBOutlet weak var txtViewMessage: UITextView!
Fügen Sie in der viewWillAppear
Methode Folgendes hinzu:
override func viewWillAppear(_ animated: Bool)
{
super.viewWillAppear(animated)
txtViewMessage.delegate = self // Give TextViewMessage delegate Method
txtViewMessage.text = "Place Holder Name"
txtViewMessage.textColor = UIColor.lightGray
}
Bitte fügen Sie die Delegate
Using-Erweiterung (UITextViewDelegate) hinzu:
// 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
}
}
}
Unsere Lösung vermeidet das Durcheinander mit den Eigenschaften UITextView
text
und textColor
, was praktisch ist, wenn Sie einen Zeichenzähler pflegen.
Es ist einfach:
1) Erstellen Sie UITextView
im Storyboard einen Dummy mit denselben Eigenschaften wie der Master UITextView
. Weisen Sie dem Dummy-Text Platzhaltertext zu.
2) Richten Sie den oberen, linken und rechten Rand der beiden aus UITextViews.
3) Platzieren Sie den Dummy hinter dem Master.
4) Überschreiben Sie die textViewDidChange(textView:)
Delegatenfunktion des Masters und zeigen Sie den Dummy an, wenn der Master 0 Zeichen hat. Andernfalls zeigen Sie den Master.
Dies setzt voraus, dass beide UITextViews
transparente Hintergründe haben. Wenn dies nicht der Fall ist, platzieren Sie den Dummy oben, wenn 0 Zeichen vorhanden sind, und schieben Sie ihn darunter, wenn> 0 Zeichen vorhanden sind. Sie müssen auch die Responder austauschen, um sicherzustellen, dass der Cursor nach rechts folgt UITextView
.
Swift 4, 4.2 und 5
[![@IBOutlet var detailTextView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
detailTextView.delegate = self
}
extension ContactUsViewController : UITextViewDelegate {
public func textViewDidBeginEditing(_ textView: UITextView) {
if textView.text == "Write your message here..." {
detailTextView.text = ""
detailTextView.textColor = UIColor.init(red: 0/255, green: 0/255, blue: 0/255, alpha: 0.86)
}
textView.becomeFirstResponder()
}
public func textViewDidEndEditing(_ textView: UITextView) {
if textView.text == "" {
detailTextView.text = "Write your message here..."
detailTextView.textColor = UIColor.init(red: 0/255, green: 0/255, blue: 0/255, alpha: 0.30)
}
textView.resignFirstResponder()
}
[![}][1]][1]