Bagaimana saya bisa menambahkan teks placeholder di dalam UITextView di Swift?

316

Saya membuat aplikasi yang menggunakan UITextView. Sekarang saya ingin Tampilan Teks memiliki placeholder mirip dengan yang Anda bisa atur untuk Bidang Teks. Bagaimana Anda melakukannya dengan menggunakan Swift?

StevenR
sumber
Ini adalah masalah lama dalam pengembangan iOS dengan UITextView. Saya telah menulis subclass seperti yang disebutkan di sini: stackoverflow.com/a/1704469/1403046 . Keuntungannya adalah Anda masih dapat memiliki delegasi, serta menggunakan kelas di banyak tempat tanpa harus mengimplementasikan kembali logika.
cjwirth
Bagaimana saya akan menggunakan subclass Anda, saat menggunakan swift untuk proyek. Menggunakan file jembatan?
StevenR
Anda bisa melakukan itu, atau menerapkannya kembali di Swift. Kode dalam jawabannya lebih panjang dari yang seharusnya. Poin utamanya adalah untuk menunjukkan / menyembunyikan label yang Anda tambahkan dalam metode yang Anda beri tahu ketika teksnya berubah.
cjwirth
Anda dapat menggunakan sampel UIFloatLabelTextView dari GitHub. Penempatan posisi ini di atas saat menulis. Sangat menarik! github.com/ArtSabintsev/UIFloatLabelTextView
Dubey
2
Jujur, cara termudah untuk mencapai ini adalah memiliki textView khusus dan hanya menambahkan teks placeholder yang ditarik ke textView ketika tidak ada teks yang hadir .... Jawaban lain sejauh ini telah menjadi versi rumit yang terlalu rumit dari ini yang melibatkan masalah manajemen negara (termasuk positif palsu ketika teks seharusnya / tidak seharusnya tidak / tidak ada)
TheCodingArt

Jawaban:

649

Diperbarui untuk Swift 4

UITextViewsecara inheren tidak memiliki properti placeholder sehingga Anda harus membuat dan memanipulasi satu program menggunakan UITextViewDelegatemetode. Saya sarankan menggunakan salah satu solusi # 1 atau # 2 di bawah ini tergantung pada perilaku yang diinginkan.

Catatan: Untuk solusi mana pun, tambahkan UITextViewDelegateke kelas dan atur textView.delegate = selfuntuk menggunakan metode delegasi tampilan teks.


Solusi # 1 - Jika Anda ingin placeholder menghilang segera setelah pengguna memilih tampilan teks:

Pertama, atur UITextViewagar berisi teks placeholder dan setel ke warna abu-abu terang untuk meniru tampilan UITextFieldteks placeholder. Entah melakukannya di viewDidLoadatau setelah penciptaan tampilan teks.

textView.text = "Placeholder"
textView.textColor = UIColor.lightGray

Kemudian ketika pengguna mulai mengedit tampilan teks, jika tampilan teks berisi placeholder (yaitu jika warna teksnya abu-abu terang) hapus teks placeholder dan atur warna teks menjadi hitam untuk mengakomodasi entri pengguna.

func textViewDidBeginEditing(_ textView: UITextView) {
    if textView.textColor == UIColor.lightGray {
        textView.text = nil
        textView.textColor = UIColor.black
    }
}

Kemudian ketika pengguna selesai mengedit tampilan teks dan mengundurkan diri sebagai responden pertama, jika tampilan teks kosong, reset placeholder-nya dengan menambahkan kembali teks placeholder dan mengatur warnanya ke abu-abu terang.

func textViewDidEndEditing(_ textView: UITextView) {
    if textView.text.isEmpty {
        textView.text = "Placeholder"
        textView.textColor = UIColor.lightGray
    }
}

Solusi # 2 - Jika Anda ingin placeholder ditampilkan kapan pun tampilan teks kosong, meskipun tampilan teks dipilih:

Pertama-tama atur placeholder di viewDidLoad:

textView.text = "Placeholder"
textView.textColor = UIColor.lightGray

textView.becomeFirstResponder()

textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)

(Catatan: Karena OP ingin agar tampilan teks dipilih segera setelah tampilan dimuat, saya memasukkan pemilihan tampilan teks ke dalam kode di atas. Jika ini bukan perilaku yang Anda inginkan dan Anda tidak ingin tampilan teks dipilih pada tampilan beban, hapus dua baris terakhir dari potongan kode di atas.)

Kemudian gunakan shouldChangeTextInRange UITextViewDelegatemetode ini, seperti:

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
}

Dan juga implementasikan textViewDidChangeSelectionuntuk mencegah pengguna mengubah posisi kursor sementara placeholder terlihat. (Catatan: textViewDidChangeSelectiondipanggil sebelum tampilan dimuat sehingga hanya memeriksa warna tampilan teks jika jendela terlihat):

func textViewDidChangeSelection(_ textView: UITextView) {
    if self.view.window != nil {
        if textView.textColor == UIColor.lightGray {
            textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)
        }
    }
}
Lyndsey Scott
sumber
5
Hai, pastikan Anda mengatur pengontrol tampilan Anda sebagai delegasi untuk textView Anda . Anda dapat mencapai ini dengan membuat outlet dari textView Anda ke viewController Anda. Lalu gunakan yourTextField.delegate = self. Jika Anda tidak melakukan ini, textViewDidBeginEditingdan textViewDidEndEditingfungsi tidak akan bekerja.
Ujjwal-Nadhani
2
Kode tidak dikompilasi, saya mengalami kesalahan sebagai Cannot convert value of type 'NSRange' (aka '_NSRange') to expected argument type 'Range<String.Index>' (aka 'Range<String.CharacterView.Index>').
iPeter
4
Saya telah menemukan solusinya dan saya akan melampirkan kode revisi @iPeter. Teks saat ini harus dalam NSString forman: let currentText = textView.text as NSString?. Ubah let updatedText =garis menjadi let updatedText = currentText?.replacingCharacters(in: range, with: text). Akhirnya, ubah if updatedText.isEmptygaris menjadi if (updatedText?.isEmpty)! {. Itu harus melakukan trik!
Baylor Mitchell
1
@ TàTruhoada Saya telah memperbarui solusi untuk menangani skenario salin dan tempel
Lyndsey Scott
3
Pengaturan @LyndseyScott textView.selectedTextRangedari dalam func textViewDidChangeSelection(_ textView: UITextView)menyebabkan loop tak terbatas ...
MikeG
229

Placeholder Terapung


Sederhana, aman, dan andal untuk menempatkan label placeholder di atas tampilan teks, mengatur font, warna, dan mengelola visibilitas placeholder dengan melacak perubahan pada jumlah karakter tampilan teks.

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: Sama, kecuali: italicSystemFontOfSize(textView.font.pointSize),UIColor.lightGrayColor


cahaya terang
sumber
19
Metode ini tidak diragukan lagi adalah yang paling sederhana dan paling rawan kesalahan yang saya temukan. Menakjubkan.
David
6
Ini adalah metode yang baik tetapi teks label dapat keluar dari batas jika teks placeholder sangat panjang ..
Aks
5
Mudah digunakan dan terintegrasi dengan baik dengan perilaku yang ada. Bagaimanapun, pesan placeholder seharusnya tidak terlalu bertele-tele, tetapi tidakkah menetapkan garis untuk 0 menangani masalah itu?
Tommie C.
2
Saya umumnya lebih suka metode ini, meskipun bermasalah jika teksnya sejajar tengah karena kursor akan dipusatkan di atas placeholder alih-alih ke kiri itu.
Blwinters
1
@blwinters - Itu akan menjadi kasus sudut yang sangat tidak biasa bukan? Tetapi dengan asumsi itu adalah bidang input multi-line dalam kasus itu (saya harus berasumsi itu), tidak bisakah Anda menyesuaikan perhitungan offset vertikal untuk memindahkan teks placeholder sedikit dari kursor sedikit?
clearlight
35

Sangat merekomendasikan untuk menggunakan pustaka KMPlaceholderTextView . Sangat mudah digunakan.

t4nhpt
sumber
1
Sejauh ini, ini adalah solusi termudah (tidak melibatkan penambahan label UIL atau melakukan hal-hal delegasi khusus). Bekerja di luar kotak.
HixField
Itu harus bekerja. Penulis mengatakan bahwa ia mendukung Swift 3.0+
t4nhpt
27

Cepat:

Tambahkan tampilan teks Anda secara terprogram atau melalui Interface Builder, jika yang terakhir, buat outlet:

@IBOutlet weak var yourTextView: UITextView!

Silakan tambahkan delegasi (UITextViewDelegate):

class ViewController: UIViewController, UITextViewDelegate {

Dalam metode viewDidLoad, jangan tambahkan yang berikut ini:

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

Sekarang izinkan saya memperkenalkan bagian ajaib, tambahkan fungsi ini:

func textViewDidBeginEditing(_ textView: UITextView) {

    if yourTextView.textColor == UIColor.lightGray {
        yourTextView.text = ""
        yourTextView.textColor = UIColor.black
    }
}

Perhatikan bahwa ini akan dijalankan setiap kali pengeditan dimulai, di sana kami akan memeriksa kondisi untuk memberi tahu negara bagian, menggunakan properti warna. Menyetel teks ke nilsaya tidak merekomendasikan. Tepat setelah itu, kami mengatur warna teks yang diinginkan, dalam hal ini, hitam.

Sekarang tambahkan fungsi ini juga:

func textViewDidEndEditing(_ textView: UITextView) {

    if yourTextView.text == "" {

        yourTextView.text = "Placeholder text ..."
        yourTextView.textColor = UIColor.lightGray
    }
}

Biarkan saya bersikeras, jangan dibandingkan dengan nil, saya sudah mencobanya dan itu tidak akan berhasil. Kami kemudian mengatur nilai kembali ke gaya placeholder, dan mengatur warna kembali ke warna placeholder karena itu adalah kondisi untuk check-in textViewDidBeginEditing.

Juan Boero
sumber
16

Gunakan Ekstensi Ini ini adalah cara terbaik untuk mengatur placeholder di UITextView. Tetapi pastikan Anda telah melampirkan delegasi ke TextView. Anda dapat mengatur Place holder seperti ini: -

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
    }
}
Sandip Gill
sumber
1
Bekerja seperti pesona !! Terima kasih banyak ! :)
Nahid Raihan
Selamat Datang Dear All the best
Sandip Gill
15

Saya terkejut tidak ada yang menyebutkan NSTextStorageDelegate. UITextViewDelegateMetode hanya akan dipicu oleh interaksi pengguna, tetapi tidak secara terprogram. Misalnya ketika Anda menyetel textproperti tampilan teks secara terprogram, Anda harus mengatur visibilitas placeholder sendiri, karena metode delegasi tidak akan dipanggil.

Namun, dengan NSTextStorageDelegate's textStorage(_:didProcessEditing:range:changeInLength:)metode, Anda akan diberitahu setiap perubahan ke teks, bahkan jika itu dilakukan pemrograman. Tetapkan saja seperti ini:

textView.textStorage.delegate = self

(Dalam UITextView, properti delegasi ini nilsecara default, sehingga tidak akan memengaruhi perilaku default apa pun.)

Menggabungkannya dengan UILabelteknik @clearlight menunjukkan, orang dapat dengan mudah membungkus seluruh UITextView's placeholderpelaksanaan menjadi perpanjangan.

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
        }
    }

}

Perhatikan bahwa penggunaan kelas privat (bersarang) disebut PlaceholderLabel. Ini tidak memiliki implementasi sama sekali, tetapi memberikan kami cara untuk mengidentifikasi label placeholder, yang jauh lebih 'cepat' daripada menggunakan tagproperti.

Dengan pendekatan ini, Anda masih dapat menetapkan delegasi UITextViewuntuk orang lain.

Anda bahkan tidak perlu mengubah kelas tampilan teks Anda. Cukup tambahkan ekstensi dan Anda akan dapat menetapkan string placeholder ke setiap UITextViewdi proyek Anda, bahkan di Pembuat Antarmuka.

Saya meninggalkan implementasi placeholderColorproperti untuk alasan kejelasan, tetapi dapat diimplementasikan hanya untuk beberapa baris dengan variabel yang dikomputasi serupa placeholder.

yesleon
sumber
Solusi yang sangat elegan. Aku menyukainya.
Dave Batton
textView.textStorage.delegate = selfini dalam view-controller akan mengharuskan kita untuk mengikat view-controller dengan NSTextStorageDelegate. Itu benar-benar membutuhkan?
Hemang
Sederhana dan berfungsi seperti pesona. Ini harus diterima jawaban
Qaiser Abbas
@Hemang itu tampilan teks itu sendiri NSTextStorageDelegate, bukan view controller.
yesleon
14

Saya melakukan ini dengan menggunakan dua tampilan teks yang berbeda:

  1. Satu di latar belakang yang digunakan sebagai pengganti.
  2. Satu di latar depan (dengan latar belakang transparan) yang sebenarnya diketik pengguna.

Idenya adalah setelah pengguna mulai mengetikkan hal-hal di tampilan latar depan, placeholder di latar belakang menghilang (dan muncul kembali jika pengguna menghapus semuanya). Jadi berperilaku persis seperti placeholder untuk bidang teks baris tunggal.

Ini kode yang saya gunakan untuk itu. Perhatikan bahwa descriptionField adalah bidang yang diketik pengguna dan descriptionPlaceholder adalah yang ada di latar belakang.

func textViewDidChange(descriptionField: UITextView) {
    if descriptionField.text.isEmpty == false {
        descriptionPlaceholder.text = ""
    } else {
        descriptionPlaceholder.text = descriptionPlaceholderText
    }
}
yesthisisjoe
sumber
1
Cara ini agak sedikit macet tetapi jauh dan termudah dan menghasilkan persis hasil yang Anda inginkan. Ide bagus
William T.
9

Berdasarkan beberapa saran hebat di sini, saya dapat mengumpulkan subkelas ringan yang kompatibel dengan Interface-Builder UITextView, yang:

  • Termasuk teks placeholder yang dapat dikonfigurasi, ditata persis seperti itu UITextField.
  • Tidak memerlukan subview atau kendala tambahan.
  • Tidak memerlukan delegasi atau perilaku lain dari ViewController.
  • Tidak memerlukan pemberitahuan apa pun.
  • Biarkan teks itu sepenuhnya terpisah dari kelas luar mana pun yang melihat textproperti bidang .

Setiap saran perbaikan dipersilahkan, terutama jika ada cara untuk menarik warna placeholder iOS secara terprogram, daripada mengkodekannya.

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
    }
}
TheNeil
sumber
6

SET nilai dalam tampilan beban

    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()
}
pengguna2991582
sumber
6

Saya mencoba untuk membuat kode nyaman dari Clearlight 's jawaban .

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
    }

}

pemakaian

override func viewDidLoad() {
    textView.delegate = self
    textView.setPlaceholder()
}

func textViewDidChange(_ textView: UITextView) {
    textView.checkPlaceholder()
}
Leonardo
sumber
Beberapa masalah. (1) Sebagai ekstensi yang mengasumsikan dan 'meminjam' nilai properti tag UIView, ada risiko seseorang mungkin menggunakan tag yang sama dalam hierarki tampilan mereka sendiri, tidak mengetahui penggunaan ekstensi, menciptakan bug yang sangat sulit didiagnosis. Hal-hal seperti itu tidak termasuk dalam kode perpustakaan atau ekstensi. (2) Masih membutuhkan penelepon untuk menyatakan delegasi. The Mengambang Placeholder menghindari hacks, memiliki jejak kecil, sederhana dan sepenuhnya lokal, yang membuatnya menjadi taruhan yang aman.
clearlight
5

Satu lagi solusi (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)
    }
}

hasil

masukkan deskripsi gambar di sini

gbk
sumber
4

Solusi sederhana dan cepat yang berfungsi untuk saya adalah:

@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
    }
}
Sepand Y.
sumber
4

Inilah yang saya gunakan untuk pekerjaan ini dilakukan.

@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()
        }
    }

}

Saya tahu ada beberapa pendekatan yang mirip dengan ini tetapi manfaat dari yang satu ini adalah:

  • Dapat mengatur teks placeholder, ukuran font dan warna dalam IB .
  • Tidak lagi menampilkan peringatan " Tampilan Gulir memiliki konten yang dapat digulir ambigu " di IB.
  • Tambahkan animasi untuk menampilkan / menyembunyikan placeholder.
Pei
sumber
3

Cepat 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
        }
    }
}

Pertama ketika pengguna mulai mengedit panggilan textViewDidBeginEditing dan kemudian memeriksa apakah warna teks abu-abu berarti pengguna tidak menulis apa-apa kemudian ditetapkan sebagai textview nil dan ubah warna menjadi hitam untuk texting pengguna.

Ketika pengguna akhir mengedit textViewDidEndEditing adalah panggilan dan periksa apakah pengguna tidak menulis apa pun di tampilan teks maka teks ditetapkan sebagai warna abu-abu dengan teks "PlaceHolder"

VD Purohit
sumber
3

Inilah cara saya memecahkan masalah ini ( Swift 4 ):

Idenya adalah untuk membuat solusi sesederhana mungkin yang memungkinkan untuk menggunakan placeholder dengan warna berbeda, mengubah ukuran menjadi placeholder, tidak akan menimpa delegatesementara itu menjaga semua UITextViewfungsi berfungsi seperti yang diharapkan.

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) }
}
beberapa baris kode
sumber
Ini adalah solusi yang bagus dan sederhana.
JaredH
2

Saya tidak tahu mengapa orang terlalu memperumit masalah ini .... Ini cukup mudah dan sederhana. Berikut adalah subkelas dari UITextView yang menyediakan fungsionalitas yang diminta.

- (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];
    }`
TheCodingArt
sumber
FYI, sebelum seseorang mengalahkan saya untuk itu ... ini cukup langsung untuk diterjemahkan ke Swift. Tidak ada yang rumit di sini.
TheCodingArt
2

Ini adalah solusi saya siap pakai jika Anda bekerja dengan beberapa tampilan teks

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
    }
}
hadiah
sumber
Itu sangat bagus!
KevinVuD
2

Cepat 3.1

Ekstensi ini bekerja dengan baik untuk saya: https://github.com/devxoul/UITextView-Placeholder

Berikut ini cuplikan kode:

Instal melalui pod:

pod 'UITextView+Placeholder', '~> 1.2'

Impor ke kelas Anda

import UITextView_Placeholder

Dan tambahkan placeholderproperti ke yang sudah Anda buatUITextView

textView.placeholder = "Put some detail"

Itu ... Begini tampilannya (Kotak ketiga adalah a UITextView) masukkan deskripsi gambar di sini

Vaibhav Saran
sumber
2

Bertentangan dengan hampir setiap jawaban pada posting ini, UITextView memang memiliki properti placeholder. Untuk alasan di luar pemahaman saya, itu hanya terekspos di IB, seperti:

<userDefinedRuntimeAttributes>
  <userDefinedRuntimeAttribute type="string" keyPath="placeholder" value="My Placeholder"/>
</userDefinedRuntimeAttributes>

Jadi jika Anda menggunakan storyboard dan placeholder statis sudah cukup, cukup atur properti pada inspektur.

Anda juga dapat mengatur properti ini dalam kode seperti ini:

textView.setValue("My Placeholder", forKeyPath: "placeholder")

Its berawan untuk cuaca ini diakses melalui API pribadi, seperti properti yang terkena.

Saya belum mencoba mengirim dengan metode ini. Tetapi saya akan mengirimkan cara ini segera dan akan memperbarui jawaban ini sesuai.

MEMPERBARUI:

Saya telah mengirimkan kode ini dalam beberapa rilis tanpa masalah dari Apple.

PEMBARUAN: Ini hanya akan berfungsi di Xcode pra 11.2

Alex Chase
sumber
di Swift 5 Anda dapat menulis myTextView, placeholder = "masukkan warna mata Anda"
user462990
@ user462990 Bisakah Anda memberikan tautan dokumentasi untuk ini? Saya tidak percaya ini akurat.
Alex Chase
maaf, tidak menemukan dox tetapi sangat mudah untuk menguji ... mis. "alert.addTextField {(textField3) di textField3.placeholder = language.JBLocalize (frasa:" yourName ")}. jbLocalize adalah manajer terjemahan yang mengembalikan sebuah String
user462990
4
@ user462990 Anda merujuk UITextFieldTIDAK, UITextView harap baca pertanyaan / tanggapan lebih hati-hati.
Alex Chase
3
Solusi hebat, tetapi tidak berhasil untuk saya. Entah bagaimana itu selalu macet. Bisakah Anda menentukan versi penyebaran target, cepat dan xCode yang Anda gunakan?
T. Pasichnyk
1

Tidak ada properti seperti itu di ios untuk menambahkan placeholder langsung di TextView melainkan Anda dapat menambahkan label dan menampilkan / menyembunyikan perubahan di textView. SWIFT 2.0 dan pastikan untuk mengimplementasikan delegasi textview

func textViewDidChange(TextView: UITextView)
{

 if  txtShortDescription.text == ""
    {
        self.lblShortDescription.hidden = false
    }
    else
    {
        self.lblShortDescription.hidden = true
    }

}
Himanshu
sumber
1

Swift - Saya menulis kelas yang mewarisi UITextView dan saya menambahkan UILabel sebagai subview untuk bertindak sebagai pengganti.

  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()
     }

}
nabil muthanna
sumber
Contoh bagus menggunakan batasan untuk memposisikan placeholder, namun posisi ideal placeholder tepat di atas di mana karakter pertama teks akan pergi, jadi menghitung posisi itu berdasarkan font tampilan teks lebih baik untuk itu karena itu menyesuaikan untuk ukuran font apa pun yang diatur untuk tampilan teks.
clearlight
1

Saya suka solusi @ nerdist. Berdasarkan itu, saya membuat ekstensi ke 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
    }
  }
}

Di kelas ViewController, misalnya, ini adalah bagaimana saya menggunakannya:

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)
    ...
  }

Placeholder di UITextview!

masukkan deskripsi gambar di sini

PEMBARUAN :

Jika Anda mengubah teks textview dalam kode, ingatlah untuk memanggil metode updateVisibitly untuk menyembunyikan placeholder:

txtNote.text = "something in code"
txtNote.updateVisibility(self.notePlaceholder) // hide placeholder if text is not empty.

Untuk mencegah placeholder ditambahkan lebih dari sekali, add()fungsi pribadi ditambahkan extension.

David.Chu.ca
sumber
Sekali lagi terima kasih atas peningkatan Anda ke aslinya. Saya menghabiskan terlalu banyak waktu pada akhir pekan ini, tetapi saya pikir Anda akan menikmati varian ekstrim EZ Placeholder yang akhirnya saya dapatkan: stackoverflow.com/a/41081244/2079103 (Saya memberi Anda kredit karena berkontribusi pada evolusi solusi placeholder)
clearlight
1

Di 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)
}

}

Dalam swift3:

import UIKit

kelas 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)
}

}

Saya menulis kelas dengan cepat. Anda perlu mengimpor kelas ini kapan pun diperlukan.

Sujatha Girijala
sumber
Harap sebutkan untuk apa versi Swift ini (tidak berfungsi untuk Swift 3)
clearlight
1

Saya tidak dapat menambahkan komentar karena reputasi. tambahkan satu lagi kebutuhan delegasi dalam jawaban @clearlight.

func textViewDidBeginEditing(_ textView: UITextView) { 
        cell.placeholderLabel.isHidden = !textView.text.isEmpty
}

adalah kebutuhan

karena textViewDidChangetidak disebut pertama kali

Seungyoun Yi
sumber
1

tidak ada placeholder yang tersedia untuk tampilan teks. Anda harus meletakkan label di atasnya ketika pengguna masuk ke tampilan teks lalu menyembunyikannya atau mengaturnya dengan nilai default saat pengguna memasukkan menghapus semua nilai.


sumber
1

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
    }
	

Saurabh Sharma
sumber
1

Saya harus mengirim antrean agar teks placeholder saya muncul kembali setelah pengeditan selesai.

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"
        }
    }
}
Taylor A. Leach
sumber
Apa yang harus saya ketik alih-alih baris terakhir ”textView.text =” Deskripsi ”jika saya ingin nilai ini menjadi apa yang diketik pengguna?
ISS
1

Cepat:

Tambahkan Anda TextView @IBOutlet:

@IBOutlet weak var txtViewMessage: UITextView!

Dalam viewWillAppearmetode ini, tambahkan yang berikut ini:

override func viewWillAppear(_ animated: Bool)
{
    super.viewWillAppear(animated)

    txtViewMessage.delegate = self    // Give TextViewMessage delegate Method

    txtViewMessage.text = "Place Holder Name"
    txtViewMessage.textColor = UIColor.lightGray

}

Silakan tambahkan Delegateekstensi Menggunakan (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
        }
    }
}
Trupesh Vaghasiya
sumber
1

Solusi kami menghindari penghancuran dengan UITextView textdan textColorproperti, yang berguna jika Anda mempertahankan penghitung karakter.

Itu mudah:

1) Buat boneka UITextViewdi Storyboard dengan properti yang sama dengan masternya UITextView. Tetapkan teks placeholder ke teks dummy.

2) Sejajarkan tepi atas, kiri, dan kanan keduanya UITextViews.

3) Tempatkan boneka di belakang master.

4) Mengesampingkan textViewDidChange(textView:)fungsi delegasi master, dan menunjukkan dummy jika master memiliki 0 karakter. Jika tidak, tunjukkan master.

Ini mengasumsikan keduanya UITextViewsmemiliki latar belakang transparan. Jika tidak, letakkan boneka di atas ketika ada 0 karakter, dan dorong di bawahnya ketika ada> 0 karakter. Anda juga harus menukar responden untuk memastikan kursor mengikuti yang benar UITextView.

Crashalot
sumber
1

Swift 4, 4.2 dan 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]
Akbar Khan
sumber