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

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



답변

스위프트 4 업데이트

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

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


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

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

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

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

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

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

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

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

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

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

textView.becomeFirstResponder()

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

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

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

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

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

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

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

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

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

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

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

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

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


답변


부동 자리 표시 자


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

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



답변

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


답변

빠른:

프로그래밍 방식으로 또는 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.


답변

이 확장명을 사용하면 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
    }
}


답변

아무도 언급하지 않은 것이 놀랍습니다 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.


답변

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

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

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

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

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