Swift의 UITextView 내에 자리 표시 자 텍스트를 추가하려면 어떻게합니까?


316

을 사용하는 응용 프로그램을 만들고 UITextView있습니다. 이제 텍스트보기에 텍스트 필드에 설정할 수있는 것과 비슷한 자리 표시자가 필요합니다. Swift를 사용하여 어떻게 이것을 달성 하시겠습니까?


이것은 UITextView를 사용한 iOS 개발의 오래된 문제입니다. 여기에 언급 된 것과 같은 하위 클래스를 작성했습니다 : stackoverflow.com/a/1704469/1403046 . 장점은 로직을 다시 구현하지 않고도 클래스를 여러 위치에서 사용할 수있을뿐만 아니라 델리게이트를 계속 사용할 수 있다는 것입니다.
cjwirth 2014

프로젝트에 신속성을 사용하면서 하위 클래스를 어떻게 사용합니까? 브릿지 파일을 사용하십니까?
StevenR

그렇게하거나 Swift에서 다시 구현할 수 있습니다. 답변의 코드가 실제보다 길다. 요점은 텍스트가 변경 될 때 알림을받는 방법에 추가 한 레이블을 표시하거나 숨기는 것입니다.
cjwirth 2014

GitHub에서 UIFloatLabelTextView 샘플을 사용할 수 있습니다. 쓰는 동안이 자리 표시자가 맨 위에옵니다. 정말 흥미로운 것! github.com/ArtSabintsev/UIFloatLabelTextView
Jayprakash Dubey

2
솔직히, 이것을 달성하는 가장 쉬운 방법은 사용자 정의 textView를 가지고 텍스트가 없을 때 textView에 그려진 자리 표시 자 텍스트를 추가하는 것입니다. 상태 관리 (텍스트가 존재하지 않아야
하거나

답변:


649

스위프트 4 업데이트

UITextView본질적으로 자리 표시 자 속성이 없으므로 UITextViewDelegate메서드를 사용하여 프로그래밍 방식으로 속성을 만들고 조작해야 합니다. 원하는 동작에 따라 아래의 솔루션 # 1 또는 # 2를 사용하는 것이 좋습니다.

참고 : 두 솔루션 중 하나 UITextViewDelegate에 대해 클래스에 추가 textView.delegate = self하고 텍스트보기의 대리자 메서드를 사용하도록 설정 하십시오.


해결 방법 # 1- 사용자가 텍스트보기를 선택하자마자 자리 표시자가 사라지 길 원하는 경우 :

먼저 UITextView자리 표시 자 텍스트를 포함 하도록를 설정하고 자리 표시 자 텍스트의 모양을 모방하기 위해 밝은 회색으로 설정합니다 UITextField. 중 하나에 그렇게 viewDidLoad하거나 텍스트 뷰의 생성시.

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

그런 다음 사용자가 텍스트보기를 편집하기 시작할 때 텍스트보기에 자리 표시자가 포함 된 경우 (예 : 텍스트 색상이 밝은 회색 인 경우) 자리 표시 자 텍스트를 지우고 사용자 입력을 수용 할 수 있도록 텍스트 색상을 검은 색으로 설정하십시오.

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

그런 다음 사용자가 텍스트보기 편집을 마치고 첫 번째 응답자로 사임 한 경우, 텍스트보기가 비어 있으면 자리 표시 자 텍스트를 다시 추가하고 색상을 연한 회색으로 설정하여 자리 표시자를 재설정하십시오.

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

해결 방법 # 2- 텍스트보기가 선택되어 있어도 텍스트보기가 비어있을 때마다 자리 표시자가 표시되도록하려면 :

먼저 다음에 자리 표시자를 설정하십시오 viewDidLoad.

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

textView.becomeFirstResponder()

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

(참고 : OP는보기가로드되는 즉시 텍스트보기를 선택하기를 원했기 때문에 위의 코드에 텍스트보기 선택을 통합했습니다. 원하는 동작이 아니고보기를로드 할 때 텍스트보기를 선택하지 않으려면, 위의 코드 청크에서 마지막 두 줄을 제거하십시오.)

그런 다음 shouldChangeTextInRange UITextViewDelegate방법을 활용하십시오 .

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {

    // Combine the textView text and the replacement text to
    // create the updated text string
    let currentText:String = textView.text
    let updatedText = (currentText as NSString).replacingCharacters(in: range, with: text)

    // If updated text view will be empty, add the placeholder
    // and set the cursor to the beginning of the text view
    if updatedText.isEmpty {

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

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

    // Else if the text view's placeholder is showing and the
    // length of the replacement string is greater than 0, set 
    // the text color to black then set its text to the
    // replacement string
     else if textView.textColor == UIColor.lightGray && !text.isEmpty {
        textView.textColor = UIColor.black
        textView.text = text
    }

    // For every other case, the text should change with the usual
    // behavior...
    else {
        return true
    }

    // ...otherwise return false since the updates have already
    // been made
    return false
}

또한 textViewDidChangeSelection자리 표시자가 표시되는 동안 사용자가 커서 위치를 변경하지 못하도록 구현 합니다. (참고 : textViewDidChangeSelection뷰가로드되기 전에 호출되므로 창이 표시되는 경우에만 텍스트 뷰의 색상을 확인하십시오) :

func textViewDidChangeSelection(_ textView: UITextView) {
    if self.view.window != nil {
        if textView.textColor == UIColor.lightGray {
            textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)
        }
    }
}

5
안녕하세요, 뷰 컨트롤러를 textView의 대리자로 설정 했는지 확인하십시오 . textView에서 viewController로 콘센트를 생성하여이를 달성 할 수 있습니다. 그런 다음을 사용하십시오 yourTextField.delegate = self. 이 작업을 수행하지 않으면 textViewDidBeginEditingtextViewDidEndEditing기능이 작동하지 않습니다.
Ujjwal-Nadhani

2
코드가 컴파일되지 않고로 오류가 발생했습니다 Cannot convert value of type 'NSRange' (aka '_NSRange') to expected argument type 'Range<String.Index>' (aka 'Range<String.CharacterView.Index>').
iPeter 2016 년

4
해결책을 찾았으며 수정 된 코드 @iPeter를 첨부 할 것입니다. 현재 텍스트는 NSString 형식이어야합니다 let currentText = textView.text as NSString?. let updatedText =줄을로 바꿉니다 let updatedText = currentText?.replacingCharacters(in: range, with: text). 마지막으로 if updatedText.isEmpty줄을로 바꿉니다 if (updatedText?.isEmpty)! {. 그 트릭을해야합니다!
Baylor Mitchell

1
@ TàTruhoada 저는 복사 및 붙여 넣기 시나리오를 처리하는 솔루션을 업데이트했습니다
Lyndsey Scott

3
안에서 @LyndseyScott 설정 textView.selectedTextRangefunc textViewDidChangeSelection(_ textView: UITextView)무한 루프를 발생 ...
MikeG

229

부동 자리 표시 자


텍스트보기의 문자 수에 대한 변경 사항을 추적하여 자리 표시 자 레이블을 텍스트보기 위에 배치하고 글꼴, 색상을 설정하고 자리 표시 자 가시성을 관리하는 것은 간단하고 안전하며 신뢰할 수 있습니다.

스위프트 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
    }
}

스위프트 2 : 동일은 제외 : italicSystemFontOfSize(textView.font.pointSize),UIColor.lightGrayColor



19
이 방법은 필자가 찾은 가장 간단하고 오류가 발생하기 쉬운 것입니다. 굉장한.
David

6
좋은 방법이지만 자리 표시 자 텍스트가 너무 길면 레이블 텍스트가 경계를 벗어날 수 있습니다.
Aks

5
사용이 간편하고 기존 동작과 잘 통합됩니다. 자리 표시 자 메시지가 너무 간결해서는 안되지만 행을 0으로 설정하여 해당 문제를 처리하지 않습니까?
Tommie C.

2
커서가 왼쪽 대신 자리 표시 자의 맨 위에 위치하기 때문에 텍스트가 가운데 정렬되면 문제가 있지만 일반적 으로이 방법을 선호합니다.
blwinters

1
@blwinters-정말 특이한 코너 케이스가 아닐까요? 그러나이 경우 여러 줄 입력 필드라고 가정하면 (위치를 가정해야 함) 자리 표시 자 텍스트를 커서에서 약간 멀리 이동시키기 위해 수직 오프셋 계산을 조정할 수 없었습니까?
clearlight

35

KMPlaceholderTextView 라이브러리 를 사용하는 것이 좋습니다 . 사용하기 매우 간단합니다.


1
이것은 가장 쉬운 솔루션입니다 (UILabels 추가 또는 특수 대리자 작업은 포함하지 않음). 상자 밖으로 작동합니다.
HixField

작동해야합니다. 저자는 스위프트 3.0 이상을 지원했다
t4nhpt

27

빠른:

프로그래밍 방식으로 또는 Interface Builder를 통해 텍스트 뷰를 추가하고 마지막 경우 콘센트를 만듭니다.

@IBOutlet weak var yourTextView: UITextView!

델리게이트 (UITextViewDelegate)를 추가하십시오 :

class ViewController: UIViewController, UITextViewDelegate {

viewDidLoad 메소드에서 다음을 추가하십시오.

override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

    yourTextView.delegate = self
    yourTextView.text = "Placeholder text goes right here..."
    yourTextView.textColor = UIColor.lightGray

이제 마법 부분을 소개 하고이 기능을 추가하십시오.

func textViewDidBeginEditing(_ textView: UITextView) {

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

편집이 시작될 때마다 실행되며 color 속성을 사용하여 상태를 알려주는 조건을 확인합니다. 텍스트를 nili로 설정 하지 않는 것이 좋습니다. 그 직후 텍스트 색상을 원하는 경우 (이 경우 검은 색)로 설정합니다.

이제이 기능도 추가하십시오.

func textViewDidEndEditing(_ textView: UITextView) {

    if yourTextView.text == "" {

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

주장하지 말고 비교하지 마십시오 nil. 나는 이미 그것을 시도했지만 작동하지 않을 것입니다. 그런 다음 값을 플레이스 홀더 스타일로 다시 설정하고 체크인하기위한 조건이므로 색상을 플레이스 홀더 색상으로 다시 설정합니다 textViewDidBeginEditing.


16

이 확장명을 사용하면 UITextView에서 자리 표시자를 설정하는 가장 좋은 방법입니다. 그러나 TextView에 대리자를 연결했는지 확인하십시오. 다음과 같이 자리 표시자를 설정할 수 있습니다.

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

1
매력처럼 일했습니다! 정말 고마워 ! :)
Nahid Raihan

환영합니다 모든 최고
Sandip Gill

15

아무도 언급하지 않은 것이 놀랍습니다 NSTextStorageDelegate. UITextViewDelegate의 메소드는 사용자 상호 작용에 의해서만 트리거되지만 프로그래밍 방식으로는 발생하지 않습니다. 예를 들어 텍스트 뷰의 text속성을 프로그래밍 방식으로 설정하면 대리자 메서드가 호출되지 않으므로 자리 표시 자의 가시성을 직접 설정해야합니다.

그러나 NSTextStorageDelegatetextStorage(_:didProcessEditing:range:changeInLength:)방법을 사용하면 프로그래밍 방식으로 완료된 경우에도 텍스트 변경 사항에 대한 알림을받습니다. 다음과 같이 지정하십시오.

textView.textStorage.delegate = self

(에서는 UITextView,이 위양 호텔입니다 nil그것이 어떤 기본 동작에 영향을주지 않도록, 기본적으로.)

와 결합 UILabel, 하나는 쉽게 전체 포장 할 수 있습니다 보여줍니다 @clearlight 기술 UITextViewplaceholder확장으로 구현.

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

}

라는 개인 (중첩) 클래스를 사용합니다 PlaceholderLabel. 전혀 구현되지 않았지만 자리 표시 자 레이블을 식별하는 방법을 제공합니다.이 레이블은 tag속성을 사용하는 것보다 훨씬 빠릅니다 .

이 방법을 사용하면의 대리인 UITextView을 다른 사람에게 할당 할 수 있습니다 .

텍스트 뷰의 클래스를 변경할 필요조차 없습니다. 확장명 만 추가하면 UITextViewInterface Builder에서도 프로젝트의 모든 개체에 자리 표시 자 문자열을 할당 할 수 있습니다 .

placeholderColor명확성 을 위해 속성 구현 을 생략했지만와 비슷한 계산 변수를 사용하여 몇 줄 더 구현할 수 있습니다 placeholder.


매우 우아한 솔루션. 나는 그것을 좋아한다.
Dave Batton

textView.textStorage.delegate = self이것은 뷰 컨트롤러에서 뷰 컨트롤러를와 바인딩해야합니다 NSTextStorageDelegate. 정말 필요한가요?
Hemang

간단하고 매력처럼 작동합니다. 이 답변에 동의해야합니다
Qaiser Abbas

@ Hemang 그것은 NSTextStorageDelegate뷰 컨트롤러가 아닌 텍스트 뷰 자체 입니다.
yesleon

14

두 가지 텍스트보기를 사용 하여이 작업을 수행했습니다.

  1. 플레이스 홀더로 사용되는 백그라운드에서 하나입니다.
  2. 사용자가 실제로 입력하는 전경 (투명한 배경) 중 하나.

일단 사용자가 전경에서 물건을 입력하기 시작하면 배경의 자리 표시자가 사라지고 사용자가 모든 것을 삭제하면 다시 나타납니다. 따라서 한 줄 텍스트 필드의 자리 표시 자처럼 동작합니다.

여기 내가 사용한 코드가 있습니다. descriptionField는 사용자가 입력하는 필드이고 descriptionPlaceholder는 백그라운드에있는 필드입니다.

func textViewDidChange(descriptionField: UITextView) {
    if descriptionField.text.isEmpty == false {
        descriptionPlaceholder.text = ""
    } else {
        descriptionPlaceholder.text = descriptionPlaceholderText
    }
}

1
이 방법은 약간 해 키지 만 가장 멀리 떨어져 있으며 원하는 결과를 정확하게 생성합니다. 좋은 생각
William T.

9

여기에있는 훌륭한 제안 중 일부를 기반으로 다음과 같은 경량의 Interface-Builder 호환 하위 클래스를 구성 할 수있었습니다 UITextView.

  • 구성 가능한 플레이스 홀더 텍스트를 포함하며의 스타일과 동일합니다 UITextField.
  • 추가적인 서브 뷰나 제약이 필요하지 않습니다.
  • ViewController에서 위임이나 다른 동작이 필요하지 않습니다.
  • 알림이 필요하지 않습니다.
  • 해당 필드의 text속성을 보고 텍스트를 외부 클래스와 완전히 분리 합니다.

특히 iOS의 자리 표시 자 색상을 하드 코딩하는 대신 프로그래밍 방식으로 가져올 수있는 방법이 있다면 개선 제안을 환영합니다.

스위프트 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
    }
}

6

뷰로드의 설정 값

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

6

나는 clearlight답변 으로부터 코드를 편리하게 만들려고 노력했다 .

extension UITextView{

    func setPlaceholder() {

        let placeholderLabel = UILabel()
        placeholderLabel.text = "Enter some text..."
        placeholderLabel.font = UIFont.italicSystemFont(ofSize: (self.font?.pointSize)!)
        placeholderLabel.sizeToFit()
        placeholderLabel.tag = 222
        placeholderLabel.frame.origin = CGPoint(x: 5, y: (self.font?.pointSize)! / 2)
        placeholderLabel.textColor = UIColor.lightGray
        placeholderLabel.isHidden = !self.text.isEmpty

        self.addSubview(placeholderLabel)
    }

    func checkPlaceholder() {
        let placeholderLabel = self.viewWithTag(222) as! UILabel
        placeholderLabel.isHidden = !self.text.isEmpty
    }

}

용법

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

func textViewDidChange(_ textView: UITextView) {
    textView.checkPlaceholder()
}

몇 가지 문제. (1) UIView 태그 속성 값을 가정하고 '빌려 오는'확장으로서, 누군가는 확장의 사용법을 모르고 자신의 뷰 계층에서 동일한 태그를 사용하여 버그를 진단하기가 매우 어려울 위험이 있습니다. 이와 같은 것은 라이브러리 코드 또는 확장에 속하지 않습니다. (2) 여전히 발신자는 대리인을 선언해야합니다. 부동 자리 표시 자 , 해킹을 방지 작은 풋 프린트를 가지고, 그것은 안전한 내기 만드는 간단하고 전체 지역입니다.
clearlight

5

하나 더 해결책 (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)
    }
}

결과

여기에 이미지 설명을 입력하십시오


4

나를 위해 간단하고 빠른 해결책은 다음과 같습니다.

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

4

이 일을 위해 내가 사용하고있는 것이 있습니다.

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

}

나는 이것과 비슷한 몇 가지 접근법이 있지만 이것의 이점은 다음과 같습니다.

  • IB 에서 자리 표시 자 텍스트, 글꼴 크기 및 색상을 설정할 수 있습니다.
  • IB에서 더 이상 " 스크롤보기에 스크롤 가능한 내용이 모호합니다 "라는 경고가 표시되지 않습니다 .
  • 자리 표시 자의 표시 / 숨기기를위한 애니메이션 을 추가 합니다 .

3

스위프트 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
        }
    }
}

사용자가 textViewDidBeginEditing 호출을 편집하기 시작한 다음 텍스트 회색의 색상이 사용자가 아무것도 쓰지 않았 음을 의미하는지 확인한 다음 textview nil로 설정하고 사용자 문자 메시지의 색상을 검은 색으로 변경하십시오.

사용자가 textViewDidEndEditing 편집을 호출하고 사용자가 textview에 아무것도 쓰지 않았는지 확인하면 텍스트 "PlaceHolder"와 함께 회색으로 설정된 텍스트


3

이 문제를 해결하는 내 방법은 다음과 같습니다 ( Swift 4 ).

아이디어는 다른 색상의 자리 표시자를 사용하고 자리 표시 자 크기로 크기를 조정 delegate하고 모든 UITextView기능이 예상대로 작동하도록 유지 하는 동안 가능한 가장 간단한 솔루션을 만드는 것 입니다.

import UIKit

class PlaceholderTextView: UITextView {
    var placeholderColor: UIColor = .lightGray
    var defaultTextColor: UIColor = .black

    private var isShowingPlaceholder = false {
        didSet {
            if isShowingPlaceholder {
                text = placeholder
                textColor = placeholderColor
            } else {
                textColor = defaultTextColor
            }
        }
    }

    var placeholder: String? {
        didSet {
            isShowingPlaceholder = !hasText
        }
    }

    @objc private func textViewDidBeginEditing(notification: Notification) {
        textColor = defaultTextColor
        if isShowingPlaceholder { text = nil }
    }

    @objc private func textViewDidEndEditing(notification: Notification) {
        isShowingPlaceholder = !hasText
    }

    // MARK: - Construction -
    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    private func setup() {
        NotificationCenter.default.addObserver(self, selector: #selector(textViewDidBeginEditing(notification:)), name: UITextView.textDidBeginEditingNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(textViewDidEndEditing(notification:)), name: UITextView.textDidEndEditingNotification, object: nil)
    }

    // MARK: - Destruction -
    deinit { NotificationCenter.default.removeObserver(self) }
}

이것은 훌륭하고 간단한 해결책입니다.
JaredH

2

사람들이 왜이 문제를 지나치게 복잡하게하는지 모르겠습니다 .. 매우 간단하고 간단합니다. 요청 된 기능을 제공하는 UITextView의 서브 클래스입니다.

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

2

여러 텍스트보기로 작업하는 경우 솔루션을 사용할 준비가되었습니다.

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

매우 좋습니다!
KevinVuD

2

스위프트 3.1

이 확장은 나를 위해 잘 작동했습니다 : https://github.com/devxoul/UITextView-Placeholder

다음은 코드 스 니펫입니다.

포드를 통해 설치하십시오.

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

수업으로 가져 오기

import UITextView_Placeholder

그리고 placeholder이미 만든 속성을 추가 하십시오UITextView

textView.placeholder = "Put some detail"

그게 다야 ... 여기 어떻게 보이는지 (세 번째 상자는 UITextView) 여기에 이미지 설명을 입력하십시오


2

반대로은이 게시물에 대한 모든 대답에 대해, UITextView 않습니다 자리 표시 자 속성이 있습니다. 이해력을 벗어난 이유로 IB에만 노출됩니다.

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

스토리 보드를 사용하는 경우 정적 자리 표시 자만으로도 속성을 관리자에서 설정하면됩니다.

다음과 같이 코드에서이 속성을 설정할 수도 있습니다.

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

날씨가 흐려지면 개인 API를 통해 액세스 할 수 있으며 속성 노출됩니다.

이 방법으로 제출하지 않았습니다. 그러나 나는이 방법을 곧 제출 할 것이고 그에 따라이 답변을 업데이트 할 것입니다.

최신 정보:

이 코드를 Apple의 문제없이 여러 릴리스로 제공했습니다.

업데이트 : 이것은 11.2 이전 Xcode에서만 작동합니다


스위프트 5에서 당신은 myTextView, placeholder = "당신의 눈 색깔을 입력하십시오"를 작성할 수 있습니다
user462990

@ user462990 이에 대한 문서 링크를 제공 할 수 있습니까? 나는 이것이 정확하다고 생각하지 않습니다.
Alex Chase

죄송합니다. dox를 찾지 못했지만 테스트하기가 매우 쉽습니다 ... 예 : "alert.addTextField {(textField3) in textField3.placeholder = language.JBLocalize (phrase :"yourName ")}. jbLocalize는 a를 반환하는 번역 관리자입니다. 문자열
user462990 April

3
@ user462990 당신은 참조하는 UITextFieldNOT UITextView 더 신중하게 질문 / 답변을 참조하십시오.
Alex Chase

3
훌륭한 해결책이지만 나를 위해 작동하지 않습니다. 어떻게 든 항상 충돌합니다. 사용중인 배포 대상, swift 및 xCode 버전을 지정할 수 있습니까?
T. Pasichnyk

1

ios에는 TextView에 자리 표시자를 직접 추가하는 속성이 없으므로 레이블을 추가하고 textView의 변경 사항을 표시하거나 숨길 수 있습니다. SWIFT 2.0 및 textviewdelegate를 구현해야합니다

func textViewDidChange(TextView: UITextView)
{

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

}

1

Swift-UITextView를 상속받은 클래스를 작성했으며 UILabel을 하위 뷰로 추가하여 자리 표시 자 역할을했습니다.

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

}

자리 표시자를 배치하기 위해 제약 조건을 사용하는 좋은 예이지만 자리 표시 자의 이상적인 위치는 텍스트의 첫 문자가 이동하는 바로 위에 있으므로 텍스트 뷰의 글꼴을 기반으로 해당 위치를 계산하는 것이 더 적합합니다. 텍스트보기에 설정된 글꼴 크기에 관계없이
clearlight

1

나는 @nerdist의 솔루션을 좋아한다. 이를 바탕으로 다음과 같은 확장을 만들었습니다 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
    }
  }
}

예를 들어 ViewController 클래스에서 다음과 같이 사용합니다.

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

UITextview에 자리 표시 자!

여기에 이미지 설명을 입력하십시오

업데이트 :

코드에서 textview의 텍스트를 변경하는 경우 updateVisibitly 메서드를 호출하여 자리 표시자를 숨기십시오.

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

자리 표시자가 두 번 이상 추가되지 않도록하기 위해 개인 add()함수가에 추가됩니다 extension.


원본 개선에 다시 한 번 감사드립니다. 나는이 이번 주말에 wayyy 너무 많은 시간을 보냈지 만 난 당신이 내가 결국 해낸 EZ 자리 표시 자 극단적 인 변형 즐길 수 있습니다 생각 : stackoverflow.com/a/41081244/2079103을 (난의 발전에 기여하는 당신에게 신용을 준 자리 표시 자 솔루션)
clearlight

1

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

}

swift3에서 :

import UIKit

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

}

나는 급히 수업을 썼다. 필요할 때마다이 클래스를 가져와야합니다.


Swift의 버전을 언급하십시오 (Swift 3에서는 작동하지 않음)
clearlight

1

평판 때문에 댓글을 추가 할 수 없습니다. @clearlight 답변에 대리인 필요를 하나 더 추가하십시오.

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

필요하다

textViewDidChange처음 호출되지 않기 때문에


1

textview에 사용할 수있는 자리 표시자가 없습니다. 사용자가 textview에 입력 할 때 레이블을 위에 놓은 다음 숨기거나 사용자가 모든 값을 제거 할 때 기본값으로 설정해야합니다.


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
    }
	


1

편집이 완료되면 자리 표시 자 텍스트가 다시 나타나도록 대기열을 전달해야했습니다.

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

이 값을 사용자가 입력하는 값으로 사용하려면 마지막 줄”textView.text =”Description”대신 무엇을 입력합니까?
ISS

1

빠른:

당신의 추가 TextView @IBOutlet:

@IBOutlet weak var txtViewMessage: UITextView!

viewWillAppear방법에서 다음을 추가하십시오.

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

    txtViewMessage.delegate = self    // Give TextViewMessage delegate Method

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

}

DelegateUsing 확장 (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
        }
    }
}

1

우리의 솔루션은 UITextView textand textColor속성을 사용 하지 않아도되므로 문자 카운터를 유지 관리하는 경우 편리합니다.

간단 해:

1) UITextView스토리 보드에서 마스터와 동일한 속성으로 더미 를 만듭니다 UITextView. 플레이스 홀더 텍스트를 더미 텍스트에 지정하십시오.

2) 두 개의 상단, 왼쪽 및 오른쪽 가장자리를 정렬 UITextViews.

3) 더미를 마스터 뒤에 놓습니다.

4) textViewDidChange(textView:)마스터 의 델리게이트 기능을 무시하고 마스터에 0 문자가 있으면 더미를 표시하십시오. 그렇지 않으면 마스터를 보여주십시오.

이것은 둘 다 UITextViews투명한 배경을 가지고 있다고 가정합니다 . 그렇지 않은 경우 0자가있을 때 더미를 맨 위에 놓고 0이 넘는 문자가있을 때 아래로 밉니다. 또한 커서가 오른쪽을 따르도록 응답기를 교체해야합니다 UITextView.


1

스위프트 4, 4.2 및 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]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.