UITextView의 자리 표시 자


717

내 응용 프로그램은을 사용합니다 UITextView. 이제에 UITextView설정할 수있는 자리 표시 자와 비슷한 자리 표시자가 필요합니다 UITextField.

이것을하는 방법?


Three20의 TTTextEditor (UITextField를 사용하는 자체)는 자리 표시 자 텍스트를 지원하고 높이에 따라 증가합니다 (UITextView로 바)).
Josh Benjamin


20
UITextView + Placeholder 카테고리를 사용하는 것은 어떻습니까? github.com/devxoul/UITextView-Placeholder
devxoul

2
나는 서브 클래스가 아닌 카테고리를 사용하는 @devxoul의 ​​솔루션을 선호합니다. 또한 IB 관리자에서 '자리 표시 자'옵션 (자리 표시 자 텍스트 및 텍스트 색상)에 대한 필드를 만듭니다. 일부 바인딩 기술을 사용합니다. 훌륭한 코드
samthui7

당신이 UITextView솔루션을 사용 하면 상당히 달라집니다. 다음은 몇 가지 솔루션입니다. 부동 자리 표시 자가짜 기본 자리 표시 자
clearlight

답변:


672

Xib파일 에서 초기화하고 텍스트 줄 바꿈하고 배경색을 유지 하기 위해 bcd 솔루션을 약간 수정했습니다 . 잘만되면 그것은 다른 사람들에게 문제를 덜어 줄 것입니다.

UIPlaceHolderTextView.h :

#import <Foundation/Foundation.h>
IB_DESIGNABLE
@interface UIPlaceHolderTextView : UITextView

@property (nonatomic, retain) IBInspectable NSString *placeholder;
@property (nonatomic, retain) IBInspectable UIColor *placeholderColor;

-(void)textChanged:(NSNotification*)notification;

@end

UIPlaceHolderTextView.m :

#import "UIPlaceHolderTextView.h"

@interface UIPlaceHolderTextView ()

@property (nonatomic, retain) UILabel *placeHolderLabel;

@end

@implementation UIPlaceHolderTextView

CGFloat const UI_PLACEHOLDER_TEXT_CHANGED_ANIMATION_DURATION = 0.25;

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
#if __has_feature(objc_arc)
#else
    [_placeHolderLabel release]; _placeHolderLabel = nil;
    [_placeholderColor release]; _placeholderColor = nil;
    [_placeholder release]; _placeholder = nil;
    [super dealloc];
#endif
}

- (void)awakeFromNib
{
    [super awakeFromNib];

    // Use Interface Builder User Defined Runtime Attributes to set
    // placeholder and placeholderColor in Interface Builder.
    if (!self.placeholder) {
        [self setPlaceholder:@""];
    }

    if (!self.placeholderColor) {
        [self setPlaceholderColor:[UIColor lightGrayColor]];
    }

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:nil];
}

- (id)initWithFrame:(CGRect)frame
{
    if( (self = [super initWithFrame:frame]) )
    {
        [self setPlaceholder:@""];
        [self setPlaceholderColor:[UIColor lightGrayColor]];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:nil];
    }
    return self;
}

- (void)textChanged:(NSNotification *)notification
{
    if([[self placeholder] length] == 0)
    {
        return;
    }

    [UIView animateWithDuration:UI_PLACEHOLDER_TEXT_CHANGED_ANIMATION_DURATION animations:^{
    if([[self text] length] == 0)
    {
        [[self viewWithTag:999] setAlpha:1];
    }
    else
    {
        [[self viewWithTag:999] setAlpha:0];
    }
    }];
}

- (void)setText:(NSString *)text {
    [super setText:text];
    [self textChanged:nil];
}

- (void)drawRect:(CGRect)rect
{
    if( [[self placeholder] length] > 0 )
    {
        if (_placeHolderLabel == nil )
        {
            _placeHolderLabel = [[UILabel alloc] initWithFrame:CGRectMake(8,8,self.bounds.size.width - 16,0)];
            _placeHolderLabel.lineBreakMode = NSLineBreakByWordWrapping;
            _placeHolderLabel.numberOfLines = 0;
            _placeHolderLabel.font = self.font;
            _placeHolderLabel.backgroundColor = [UIColor clearColor];
            _placeHolderLabel.textColor = self.placeholderColor;
            _placeHolderLabel.alpha = 0;
            _placeHolderLabel.tag = 999;
            [self addSubview:_placeHolderLabel];
        }

        _placeHolderLabel.text = self.placeholder;
        [_placeHolderLabel sizeToFit];
        [self sendSubviewToBack:_placeHolderLabel];
    }

    if( [[self text] length] == 0 && [[self placeholder] length] > 0 )
    {
        [[self viewWithTag:999] setAlpha:1];
    }

    [super drawRect:rect];
}

@end

2
경우에 따라 (예 : iOS 5 호환성) 붙여 넣기를 재정의해야합니다.-(void) paste : (id) sender {[super paste : sender]; [self textChanged : nil]; }
Martin Ullrich

3
좋은 물건! NSString (또는 NSMutableXXX와 동등한 클래스)에 대한 모범 사례에 대한 알림은 "보유"가 아니라 "복사"여야합니다.
Oli

2
이 코드를 어떻게 인스턴스화합니까? 자리 표시 자 텍스트가 표시되지 않고 입력을 시작할 때 아무것도 지워지지 않습니다.
user798719

40
이것은 매우 잘못 작성된 구현입니다. 여기에 또한 받아쓰기의 변경을 감시 방대 클리너 버전입니다 : github.com/cbowns/MPTextView
cbowns은

10
drawRect에서 뷰 계층을 수정하지 마십시오.
Karmeye

634

쉬운 방법 UITextView은 다음 UITextViewDelegate방법 을 사용하여 자리 표시 자 텍스트를 만드는 것입니다 .

- (void)textViewDidBeginEditing:(UITextView *)textView
{
    if ([textView.text isEqualToString:@"placeholder text here..."]) {
         textView.text = @"";
         textView.textColor = [UIColor blackColor]; //optional
    }
    [textView becomeFirstResponder];
}

- (void)textViewDidEndEditing:(UITextView *)textView
{
    if ([textView.text isEqualToString:@""]) {
        textView.text = @"placeholder text here...";
        textView.textColor = [UIColor lightGrayColor]; //optional
    }
    [textView resignFirstResponder];
}

myUITextView생성시 정확한 텍스트 로 설정 해야합니다. 예 :

UITextView *myUITextView = [[UITextView alloc] init];
myUITextView.delegate = self;
myUITextView.text = @"placeholder text here...";
myUITextView.textColor = [UIColor lightGrayColor]; //optional

UITextViewDelegate이러한 메소드를 포함하기 전에 부모 클래스를 a로 만듭니다.

@interface MyClass () <UITextViewDelegate>
@end

스위프트 3.1 코드

func textViewDidBeginEditing(_ textView: UITextView) 
{
    if (textView.text == "placeholder text here..." && textView.textColor == .lightGray)
    {
        textView.text = ""
        textView.textColor = .black
    }
    textView.becomeFirstResponder() //Optional
}

func textViewDidEndEditing(_ textView: UITextView)
{
    if (textView.text == "")
    {
        textView.text = "placeholder text here..."
        textView.textColor = .lightGray
    }
    textView.resignFirstResponder()
}

myUITextView생성시 정확한 텍스트 로 설정 해야합니다. 예 :

 let myUITextView = UITextView.init()
 myUITextView.delegate = self
 myUITextView.text = "placeholder text here..."
 myUITextView.textColor = .lightGray

UITextViewDelegate이러한 메소드를 포함하기 전에 부모 클래스를 a로 만듭니다.

class MyClass: UITextViewDelegate
{

}

1
이것은 1 UITextView가있는 1 화면에 훌륭합니다 (간단히 좋아합니다). 더 복잡한 솔루션의 이유는 MANY 화면과 MANY UITextView가있는 더 큰 앱이있는 경우이 작업을 반복해서 원하지 않기 때문입니다. 필요에 맞게 UITextView를 서브 클래 싱 한 다음 사용할 수도 있습니다.
ghostatron

42
텍스트 상자에 "placeholder text here ..."를 입력하면 플레이스 홀더 텍스트처럼 동작합니다. 또한 제출하는 동안 모든 기준을 확인해야합니다.
Anindya Sengupta

7
자리 표시 자 텍스트는 필드가 응답자가되는 경우에도 표시되어야하며이 방법은 작동하지 않습니다.
phatmann

17
나는 "과다 설계"방법은 깨끗하고 재사용 가능한 것을 주장 @jklp ... 그리고 그것은하지 변조 않는 것 같습니다 text좀 nice..whereas이 방법의 수정이있는 텍스트 뷰의 속성
Cameron Askew

2
왜 델리게이트 메소드에서 FirstResponder 및 resignFirstResponder가되어야 하는가?
Adam Johns

119

나는 조금 무거워서 게시 된 솔루션에 너무 만족하지 않았습니다. 뷰에 뷰를 추가하는 것은 실제로 이상적이지 않습니다 (특히 drawRect:). 둘 다 누출이 있었으므로 받아 들일 수 없습니다.

내 솔루션은 다음과 같습니다. SAMTextView

SAMTextView.h

//
//  SAMTextView.h
//  SAMTextView
//
//  Created by Sam Soffes on 8/18/10.
//  Copyright 2010-2013 Sam Soffes. All rights reserved.
//

#import <UIKit/UIKit.h>

/**
 UITextView subclass that adds placeholder support like UITextField has.
 */
@interface SAMTextView : UITextView

/**
 The string that is displayed when there is no other text in the text view.

 The default value is `nil`.
 */
@property (nonatomic, strong) NSString *placeholder;

/**
 The color of the placeholder.

 The default is `[UIColor lightGrayColor]`.
 */
@property (nonatomic, strong) UIColor *placeholderTextColor;

/**
 Returns the drawing rectangle for the text views’s placeholder text.

 @param bounds The bounding rectangle of the receiver.
 @return The computed drawing rectangle for the placeholder text.
 */
- (CGRect)placeholderRectForBounds:(CGRect)bounds;

@end

SAMTextView.m

//
//  SAMTextView.m
//  SAMTextView
//
//  Created by Sam Soffes on 8/18/10.
//  Copyright 2010-2013 Sam Soffes. All rights reserved.
//

#import "SAMTextView.h"

@implementation SAMTextView

#pragma mark - Accessors

@synthesize placeholder = _placeholder;
@synthesize placeholderTextColor = _placeholderTextColor;

- (void)setText:(NSString *)string {
  [super setText:string];
  [self setNeedsDisplay];
}


- (void)insertText:(NSString *)string {
  [super insertText:string];
  [self setNeedsDisplay];
}


- (void)setAttributedText:(NSAttributedString *)attributedText {
  [super setAttributedText:attributedText];
  [self setNeedsDisplay];
}


- (void)setPlaceholder:(NSString *)string {
  if ([string isEqual:_placeholder]) {
    return;
  }

  _placeholder = string;
  [self setNeedsDisplay];
}


- (void)setContentInset:(UIEdgeInsets)contentInset {
  [super setContentInset:contentInset];
  [self setNeedsDisplay];
}


- (void)setFont:(UIFont *)font {
  [super setFont:font];
  [self setNeedsDisplay];
}


- (void)setTextAlignment:(NSTextAlignment)textAlignment {
  [super setTextAlignment:textAlignment];
  [self setNeedsDisplay];
}


#pragma mark - NSObject

- (void)dealloc {
  [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextViewTextDidChangeNotification object:self];
}


#pragma mark - UIView

- (id)initWithCoder:(NSCoder *)aDecoder {
  if ((self = [super initWithCoder:aDecoder])) {
    [self initialize];
  }
  return self;
}


- (id)initWithFrame:(CGRect)frame {
  if ((self = [super initWithFrame:frame])) {
    [self initialize];
  }
  return self;
}


- (void)drawRect:(CGRect)rect {
  [super drawRect:rect];

  if (self.text.length == 0 && self.placeholder) {
    rect = [self placeholderRectForBounds:self.bounds];

    UIFont *font = self.font ? self.font : self.typingAttributes[NSFontAttributeName];

    // Draw the text
    [self.placeholderTextColor set];
    [self.placeholder drawInRect:rect withFont:font lineBreakMode:NSLineBreakByTruncatingTail alignment:self.textAlignment];
  }
}


#pragma mark - Placeholder

- (CGRect)placeholderRectForBounds:(CGRect)bounds {
  // Inset the rect
  CGRect rect = UIEdgeInsetsInsetRect(bounds, self.contentInset);

  if (self.typingAttributes) {
    NSParagraphStyle *style = self.typingAttributes[NSParagraphStyleAttributeName];
    if (style) {
      rect.origin.x += style.headIndent;
      rect.origin.y += style.firstLineHeadIndent;
    }
  }

  return rect;
}


#pragma mark - Private

- (void)initialize {
  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:self];

  self.placeholderTextColor = [UIColor colorWithWhite:0.702f alpha:1.0f];
}


- (void)textChanged:(NSNotification *)notification {
  [self setNeedsDisplay];
}

@end

하위보기를 사용하지 않거나 누출이 있기 때문에 다른 것보다 훨씬 간단합니다. 자유롭게 사용하십시오.

11/10/11 업데이트 : 이제 문서화되었으며 Interface Builder에서의 사용을 지원합니다.

11/24/13 업데이트 : 새로운 저장소를 가리 킵니다 .


귀하의 솔루션이 마음에 setText들며 프로그래밍 방식으로 텍스트 속성을 변경할 때 자리 표시 자도 업데이트 하는 재정의를 추가했습니다 .-(void) setText : (NSString *) string {[super setText : string]; [self _updateShouldDrawPlaceholder]; }
olegueret

1
나는 당신의 솔루션도 좋아하지만 awakefromnib 메소드를 놓쳤으므로 init 메소드가 항상 호출되지는 않습니다. 나는 다른 사람들 중 하나에서 가져 왔습니다.
toxaq

참고-마법의 숫자는 글꼴 크기에 따라 다릅니다. 글꼴에서 정확한 위치를 계산할 수 있지만이 구현에는 적합하지 않을 수 있습니다. 자리 표시 자의 올바른 위치는 텍스트가 없을 때 커서 위치의 오른쪽에 2 픽셀이어야합니다.
memmons

펜촉에서 로딩을 해결하려면 :-(id) initWithCoder : (NSCoder *) aDecoder; 이니셜 라이저가 기존 항목과 함께 제공됩니다.
Joris Kluivers

이것은 달콤합니다. 그러나 notification마지막 방법에서는 이 인수의 철자가 틀리며 편집으로 제출하기에는 너무 작은 변화입니다.
Phil Calvin

53

자리 표시자를 모방하는 가장 쉬운 방법을 찾았습니다.

  1. NIB 또는 코드에서 textView의 textColor를 lightGrayColor로 설정하십시오 (대부분)
  2. textView의 대리자가 파일의 소유자와 연결되어 있고 헤더 파일에서 UITextViewDelegate를 구현해야합니다.
  3. 텍스트보기의 기본 텍스트를 다음과 같이 설정하십시오 (예 : "Foobar placeholder")
  4. 구현 : (BOOL) textViewShouldBeginEditing : (UITextView *) textView

편집하다:

if 문이 텍스트가 아닌 태그를 비교하도록 변경되었습니다. 사용자가 텍스트를 삭제 한 경우 실수로 자리 표시 자의 일부를 삭제할 수도 @"Foobar placeholder"있습니다. 이는 사용자가 textView 다음 대리자 메서드를 다시 입력 한 경우 -(BOOL) textViewShouldBeginEditing:(UITextView *) textView예상대로 작동하지 않음을 의미합니다. if 문에서 텍스트의 색상으로 비교를 시도했지만 인터페이스 빌더에서 설정된 밝은 회색 색상이 코드에서 설정된 밝은 회색 색상과 같지 않음을 발견했습니다.[UIColor lightGreyColor]

- (BOOL) textViewShouldBeginEditing:(UITextView *)textView
{
    if(textView.tag == 0) {
        textView.text = @"";
        textView.textColor = [UIColor blackColor];
        textView.tag = 1;
    }
    return YES;
}

키보드가 반환되고 [textView length] == 0 일 때 자리 표시 자 텍스트를 재설정 할 수도 있습니다.

편집하다:

마지막 부분을 더 명확하게하기 위해 자리 표시 자 텍스트를 다시 설정하는 방법은 다음과 같습니다.

- (void)textViewDidChange:(UITextView *)textView
{
   if([textView.text length] == 0)
   {
       textView.text = @"Foobar placeholder";
       textView.textColor = [UIColor lightGrayColor];
       textView.tag = 0;
   }
}

12
나는이 접근법을 매우 좋아한다! 위의 편집을 수행하는 유일한 방법은 구현을 textViewDidChange : 메서드에서 textViewDidEndEditing : 메서드로 옮기는 것이므로 개체 작업이 끝나면 자리 표시 자 텍스트 만 반환됩니다.
horseshoe7

52

당신이 할 수있는 일은 text속성에 초기 값으로 텍스트보기를 설정 하고 textColor[UIColor grayColor]또는 유사한 것을 변경하는 것입니다. 그런 다음 텍스트보기를 편집 할 수있게되면 텍스트를 지우고 커서를 표시 한 다음 텍스트 필드가 다시 비어 있으면 자리 표시 자 텍스트를 다시 배치하십시오. 색상을 [UIColor blackColor]적절하게 변경하십시오 .

UITextField의 자리 표시 자 기능과 정확히 동일하지는 않지만 가깝습니다.


9
나는 항상 자리 표시 자 텍스트의 색상과 일치하는 것 같은 lightGrayColor를 사용했습니다 .
Bill

방금 이것을 읽고 있지만 textViewShouldBeginEditing : (UITextView *) textView에서 색상을 검은 색으로 재설정하고 텍스트 속성을 재설정하는 것이 좋습니다. 이것은 아래 솔루션과 비교할 때 매우 훌륭하고 빠른 솔루션입니다 (그러나 여전히 uitextview를 서브 클래스 화하여 훨씬 더 모듈화 된 아래의 우아한 솔루션).
Enrico Susatyo

3
사실, UITextField의 동작을 모방하지는 않습니다. UITextField는 편집이 시작될 때가 아니라 사용자가 무언가를 입력 할 때 자리 표시 자 텍스트 만 바꾸고 편집이 실제로 완료 될 때가 아니라 뷰가 비어있는 두 번째 자리 표시자를 다시 추가합니다.
Ash

47

UITextView의해 레이블을 설정할 수 있습니다

[UITextView addSubView:lblPlaceHoldaer];

TextViewdidChange방법에 숨길 수 있습니다 .

이것은 간단하고 쉬운 방법입니다.


45

누군가 스위프트 솔루션이 필요한 경우 :

클래스에 UITextViewDelegate 추가

var placeHolderText = "Placeholder Text..."

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

func textViewShouldBeginEditing(textView: UITextView) -> Bool {

    self.textView.textColor = .black

    if(self.textView.text == placeHolderText) {
        self.textView.text = ""
    }

    return true
}

func textViewDidEndEditing(textView: UITextView) {
    if(textView.text == "") {
        self.textView.text = placeHolderText
        self.textView.textColor = .lightGray
    }
}

override func viewWillAppear(animated: Bool) {

    if(currentQuestion.answerDisplayValue == "") {
        self.textView.text = placeHolderText
        self.textView.textColor = .lightGray
    } else {
        self.textView.text = "xxx" // load default text / or stored 
        self.textView.textColor = .black
    }
}

이것은 괜찮지 만 충분하지 않습니다. 사용자가 "자리 표시 자 텍스트 ..."(가장자리가 분명한 경우)를 입력하면 논리가 깨집니다.
Lucas Chwe

45

간단한 스위프트 3 솔루션

UITextViewDelegate수업에 추가

세트 yourTextView.delegate = self

작성 placeholderLabel하고 내부에 배치yourTextView

이제는 다음과 같이 애니메이션 placeholderLabel.alpha을 적용하십시오 textViewDidChange.

  func textViewDidChange(_ textView: UITextView) {
    let newAlpha: CGFloat = textView.text.isEmpty ? 1 : 0
    if placeholderLabel.alpha != newAlpha {
      UIView.animate(withDuration: 0.3) {
        self.placeholderLabel.alpha = newAlpha
      }
    }
  }

당신은 placeholderLabel그것을 올바르게 설정하기 위해 위치를 가지고 놀아야 할 수도 있지만, 그렇게 어렵지 않아야합니다.


1
좋은 대답, 간단한 해결책. 알파가 변경되어야 할 때만 애니메이션에 약간의 개선 사항을 추가했습니다. let alpha = CGFloat(textView.text.isEmpty ? 1.0 : 0.0) if alpha != lblPlaceholder.alpha { UIView.animate(withDuration: 0.3) { self.lblPlaceholder.alpha = alpha } }
Luciano Sclovsky

24

KmKndy의 답변을 확장하여 자리 표시자가 사용자가 UITextView누르기보다는 편집을 시작할 때까지 표시됩니다 . 이것은 Twitter 및 Facebook 앱의 기능을 반영합니다. 내 솔루션은 사용자가 직접 입력하거나 텍스트를 붙여 넣을 경우 서브 클래스를 만들 필요가 없으며 작동합니다!

자리 표시 자의 예 트위터 앱

- (void)textViewDidChangeSelection:(UITextView *)textView{
    if ([textView.text isEqualToString:@"What's happening?"] && [textView.textColor isEqual:[UIColor lightGrayColor]])[textView setSelectedRange:NSMakeRange(0, 0)];

}

- (void)textViewDidBeginEditing:(UITextView *)textView{

    [textView setSelectedRange:NSMakeRange(0, 0)];
}

- (void)textViewDidChange:(UITextView *)textView
{
    if (textView.text.length != 0 && [[textView.text substringFromIndex:1] isEqualToString:@"What's happening?"] && [textView.textColor isEqual:[UIColor lightGrayColor]]){
        textView.text = [textView.text substringToIndex:1];
        textView.textColor = [UIColor blackColor]; //optional

    }
    else if(textView.text.length == 0){
        textView.text = @"What's happening?";
        textView.textColor = [UIColor lightGrayColor];
        [textView setSelectedRange:NSMakeRange(0, 0)];
    }
}

- (void)textViewDidEndEditing:(UITextView *)textView
{
    if ([textView.text isEqualToString:@""]) {
        textView.text = @"What's happening?";
        textView.textColor = [UIColor lightGrayColor]; //optional
    }
    [textView resignFirstResponder];
}

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
    if (textView.text.length > 1 && [textView.text isEqualToString:@"What's happening?"]) {
         textView.text = @"";
         textView.textColor = [UIColor blackColor];
    }

    return YES;
}

생성시 정확한 텍스트로 myUITextView를 설정하는 것을 잊지 마십시오.

UITextView *myUITextView = [[UITextView alloc] init];
myUITextView.delegate = self;
myUITextView.text = @"What's happening?";
myUITextView.textColor = [UIColor lightGrayColor]; //optional

이 메소드를 포함하기 전에 부모 클래스를 UITextView 대리자로 만듭니다.

@interface MyClass () <UITextViewDelegate>
@end


20

다음은 "SAMTextView"ObjC 코드의 Swift 포트로서 질문에 대한 첫 번째 답변 중 하나로 게시되었습니다. iOS 8에서 테스트했습니다. 원본이 너무 높고 너무 옳았 기 때문에 자리 표시 자 텍스트의 배치에 대한 경계 오프셋을 포함하여 몇 가지 사항을 조정했습니다 (해당 게시물에 대한 의견 중 하나에서 제안 사용).

나는 많은 간단한 솔루션이 있다는 것을 알고 있지만 재사용 할 수 있고 메커니즘을 사용하여 클래스를 혼란스럽게 만들 필요가 없기 때문에 UITextView를 서브 클래스 화하는 접근법을 좋아합니다.

스위프트 2.2 :

import UIKit

class PlaceholderTextView: UITextView {

    @IBInspectable var placeholderColor: UIColor = UIColor.lightGrayColor()
    @IBInspectable var placeholderText: String = ""

    override var font: UIFont? {
        didSet {
            setNeedsDisplay()
        }
    }

    override var contentInset: UIEdgeInsets {
        didSet {
            setNeedsDisplay()
        }
    }

    override var textAlignment: NSTextAlignment {
        didSet {
            setNeedsDisplay()
        }
    }

    override var text: String? {
        didSet {
            setNeedsDisplay()
        }
    }

    override var attributedText: NSAttributedString? {
        didSet {
            setNeedsDisplay()
        }
    }

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

    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
    }

    private func setUp() {
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(PlaceholderTextView.textChanged(_:)),
                                                         name: UITextViewTextDidChangeNotification, object: self)
    }

    func textChanged(notification: NSNotification) {
        setNeedsDisplay()
    }

    func placeholderRectForBounds(bounds: CGRect) -> CGRect {
        var x = contentInset.left + 4.0
        var y = contentInset.top  + 9.0
        let w = frame.size.width - contentInset.left - contentInset.right - 16.0
        let h = frame.size.height - contentInset.top - contentInset.bottom - 16.0

        if let style = self.typingAttributes[NSParagraphStyleAttributeName] as? NSParagraphStyle {
            x += style.headIndent
            y += style.firstLineHeadIndent
        }
        return CGRect(x: x, y: y, width: w, height: h)
    }

    override func drawRect(rect: CGRect) {
        if text!.isEmpty && !placeholderText.isEmpty {
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.alignment = textAlignment
            let attributes: [ String: AnyObject ] = [
                NSFontAttributeName : font!,
                NSForegroundColorAttributeName : placeholderColor,
                NSParagraphStyleAttributeName  : paragraphStyle]

            placeholderText.drawInRect(placeholderRectForBounds(bounds), withAttributes: attributes)
        }
        super.drawRect(rect)
    }
}

스위프트 4.2 :

import UIKit

class PlaceholderTextView: UITextView {

    @IBInspectable var placeholderColor: UIColor = UIColor.lightGray
    @IBInspectable var placeholderText: String = ""

    override var font: UIFont? {
        didSet {
            setNeedsDisplay()
        }
    }

    override var contentInset: UIEdgeInsets {
        didSet {
            setNeedsDisplay()
        }
    }

    override var textAlignment: NSTextAlignment {
        didSet {
            setNeedsDisplay()
        }
    }

    override var text: String? {
        didSet {
            setNeedsDisplay()
        }
    }

    override var attributedText: NSAttributedString? {
        didSet {
            setNeedsDisplay()
        }
    }

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

    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
    }

    private func setUp() {
        NotificationCenter.default.addObserver(self,
         selector: #selector(self.textChanged(notification:)),
         name: Notification.Name("UITextViewTextDidChangeNotification"),
         object: nil)
    }

    @objc func textChanged(notification: NSNotification) {
        setNeedsDisplay()
    }

    func placeholderRectForBounds(bounds: CGRect) -> CGRect {
        var x = contentInset.left + 4.0
        var y = contentInset.top  + 9.0
        let w = frame.size.width - contentInset.left - contentInset.right - 16.0
        let h = frame.size.height - contentInset.top - contentInset.bottom - 16.0

        if let style = self.typingAttributes[NSAttributedString.Key.paragraphStyle] as? NSParagraphStyle {
            x += style.headIndent
            y += style.firstLineHeadIndent
        }
        return CGRect(x: x, y: y, width: w, height: h)
    }

    override func draw(_ rect: CGRect) {
        if text!.isEmpty && !placeholderText.isEmpty {
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.alignment = textAlignment
            let attributes: [NSAttributedString.Key: Any] = [
            NSAttributedString.Key(rawValue: NSAttributedString.Key.font.rawValue) : font!,
            NSAttributedString.Key(rawValue: NSAttributedString.Key.foregroundColor.rawValue) : placeholderColor,
            NSAttributedString.Key(rawValue: NSAttributedString.Key.paragraphStyle.rawValue)  : paragraphStyle]

            placeholderText.draw(in: placeholderRectForBounds(bounds: bounds), withAttributes: attributes)
        }
        super.draw(rect)
    }
}

빠른 버전을 수행하기 위해 고맙습니다 .super를 호출하는 awakeFromNib 메소드의 요점을 설명 할 수 있습니까?
피에르

자리 표시자를 설정하지만 입력을 시작하면 업데이트되지 않습니다.
David

알림 호출을 awakeFromNib에 넣고 지금 작동합니다.
David

12

이것이 내가 한 방법입니다.

UITextView2.h

#import <UIKit/UIKit.h>

@interface UITextView2 : UITextView <UITextViewDelegate> {
 NSString *placeholder;
 UIColor *placeholderColor;
}

@property(nonatomic, retain) NSString *placeholder;
@property(nonatomic, retain) UIColor *placeholderColor;

-(void)textChanged:(NSNotification*)notif;

@end

UITextView2.m

@implementation UITextView2

@synthesize placeholder, placeholderColor;

- (id)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [self setPlaceholder:@""];
        [self setPlaceholderColor:[UIColor lightGrayColor]];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:nil];
    }
    return self;
}

-(void)textChanged:(NSNotification*)notif {
    if ([[self placeholder] length]==0)
        return;
    if ([[self text] length]==0) {
        [[self viewWithTag:999] setAlpha:1];
    } else {
        [[self viewWithTag:999] setAlpha:0];
    }

}

- (void)drawRect:(CGRect)rect {
    if ([[self placeholder] length]>0) {
        UILabel *l = [[UILabel alloc] initWithFrame:CGRectMake(8, 8, 0, 0)];
        [l setFont:self.font];
        [l setTextColor:self.placeholderColor];
        [l setText:self.placeholder];
        [l setAlpha:0];
        [l setTag:999];
        [self addSubview:l];
        [l sizeToFit];
        [self sendSubviewToBack:l];
        [l release];
    }
    if ([[self text] length]==0 && [[self placeholder] length]>0) {
        [[self viewWithTag:999] setAlpha:1];
    }
    [super drawRect:rect];
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [super dealloc];
}


@end

12

다음은 UITextField의 자리 표시 자와 똑같이 동작하지만 사용자 지정 뷰를 그리거나 첫 번째 응답자를 사임하지 않아도되는 더 쉬운 솔루션입니다.

- (void) textViewDidChange:(UITextView *)textView{

    if (textView.text.length == 0){
        textView.textColor = [UIColor lightGrayColor];
        textView.text = placeholderText;
        [textView setSelectedRange:NSMakeRange(0, 0)];
        isPlaceholder = YES;

    } else if (isPlaceholder && ![textView.text isEqualToString:placeholderText]) {
        textView.text = [textView.text substringToIndex:1];
        textView.textColor = [UIColor blackColor];
        isPlaceholder = NO;
    }

}

(입력 내용이없고 사용자가 백 스페이스 키를 누른 경우 else if 문의 두 번째 점검)

클래스를 UITextViewDelegate로 설정하십시오. viewDidLoad에서 다음과 같이 초기화해야합니다.

- (void) viewDidLoad{
    // initialize placeholder text
    placeholderText = @"some placeholder";
    isPlaceholder = YES;
    self.someTextView.text = placeholderText;
    self.someTextView.textColor = [UIColor lightGrayColor];
    [self.someTextView setSelectedRange:NSMakeRange(0, 0)];

    // assign UITextViewDelegate
    self.someTextView.delegate = self;
}

2
사용자가 "자리 표시 자 텍스트"중간에 탭을두면 캐럿이 그대로 유지됩니다.
Alex Sorokoletov

10

안녕하세요 당신은 IQKeyboard Manager에서 사용할 수있는 IQTextView를 사용할 수 있습니다. 사용하기 쉽고 텍스트 뷰의 설정된 클래스를 IQTextView에 통합하고 플레이스 홀더 레이블을 원하는 색상으로 설정하는 데 속성을 사용할 수 있습니다. IQKeyboardManager 에서 라이브러리를 다운로드 할 수 있습니다

또는 cocoapods에서 설치할 수 있습니다.


IQKeyboardManager는 매우 유용하고 코드가 없습니다. 나에게 가장 좋은 대답!
NSDeveloper

1
실제로 나는 이유에 대해 찬성하고 의견을 남깁니다. 나는 IQTextView가 IQKeyboardManager에서 사용 가능하다는 것을 모른다.
NSDeveloper

대리인에게 문제가있었습니다. 나는 클래스에서 델리게이트 오버라이드를 제거하고 UITextViewDelegate잘 작동했다
TheoK

과소 답변
grantespo

10

다른 답변을 추가하여 죄송하지만 방금 이와 같은 것을 꺼내고 UITextField에 가장 가까운 자리 표시자를 만들었습니다.

이것이 누군가를 돕기를 바랍니다.

-(void)textViewDidChange:(UITextView *)textView{
    if(textView.textColor == [UIColor lightGrayColor]){
        textView.textColor  = [UIColor blackColor]; // look at the comment section in this answer
        textView.text       = [textView.text substringToIndex: 0];// look at the comment section in this answer
    }else if(textView.text.length == 0){
        textView.text       = @"This is some placeholder text.";
        textView.textColor  = [UIColor lightGrayColor];
        textView.selectedRange = NSMakeRange(0, 0);
    }
}

-(void)textViewDidChangeSelection:(UITextView *)textView{
    if(textView.textColor == [UIColor lightGrayColor] && (textView.selectedRange.location != 0 || textView.selectedRange.length != 0)){
        textView.selectedRange = NSMakeRange(0, 0);
    }
}

1
첫 번째 if 문에서 명령 순서를 변경해야했습니다. if(textView.textColor == [UIColor lightGrayColor]){ textView.textColor = [UIColor blackColor]; textView.text = [textView.text substringToIndex: 1]; 그렇지 않으면 textview에 입력되는 첫 번째 문자가 텍스트 끝에 배치되었습니다.
Flexicoder

7

일부 코드 줄에서 이것을 사용하는 간단한 방법 :

이 레이블을 코드에 연결 한 후 .nib에서 UITextView까지 하나의 레이블을 사용하십시오.

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{

    if (range.location>0 || text.length!=0) {
        placeholderLabel1.hidden = YES;
    }else{
        placeholderLabel1.hidden = NO;
    }
    return YES;
}

7

iOS7에서 작동하도록 Sam Soffes의 구현을 수정했습니다.

- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];

    if (_shouldDrawPlaceholder)
    {
        UIEdgeInsets insets = self.textContainerInset;        
        CGRect placeholderRect = CGRectMake(
                insets.left + self.textContainer.lineFragmentPadding,
                insets.top,
                self.frame.size.width - insets.left - insets.right,
                self.frame.size.height - insets.top - insets.bottom);

        [_placeholderText drawWithRect:placeholderRect
                           options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine
                        attributes:self.placeholderAttributes
                           context:nil];
    }
}

- (NSDictionary *)placeholderAttributes
{
    if (_placeholderAttributes == nil)
    {
        _placeholderAttributes = @
        {
            NSFontAttributeName : self.font,
            NSForegroundColorAttributeName : self.placeholderColor
        };
    }

    return _placeholderAttributes;
}

_placeholderAttribues = nil글꼴과 글꼴에 영향을 줄 수있는 다른 글꼴을 변경할 수있는 방법 을 설정해야 합니다. 버그가없는 경우 속성 사전의 "게으른"작성을 건너 뛸 수도 있습니다.

편집하다:

자동 레이아웃 애니메이션 등을 수행 한 후 자리 표시자가 잘 보이도록하려면 재정의 된 setBounds 버전에서 setNeedsDisplay를 호출해야합니다.


오프셋 x 매개 변수에 insets.left를 추가해야한다고 생각합니다.
Karmeye

setBounds 대신 setFrame이 아닙니까?
JakubKnejzlik

아니! 레이아웃 애니메이션 중에는 setFrame이 호출되지 않습니다.
일러

6

UITextView의 하위 클래스로 새 클래스 TextViewWithPlaceholder를 만들 수도 있습니다.

(이 코드는 다소 거칠지 만 올바른 방향으로 가고 있다고 생각합니다.)

@interface TextViewWithPlaceholder : UITextView
{

    NSString *placeholderText;  // make a property
    UIColor *placeholderColor;  // make a property
    UIColor *normalTextColor;   // cache text color here whenever you switch to the placeholderColor
}

- (void) setTextColor: (UIColor*) color
{
   normalTextColor = color;
   [super setTextColor: color];
}

- (void) updateForTextChange
{
    if ([self.text length] == 0)
    { 
        normalTextColor = self.textColor;
        self.textColor = placeholderColor;
        self.text = placeholderText;
    }
    else
    {
        self.textColor = normalTextColor;
    }

}

대리인에게 다음을 추가하십시오.

- (void)textViewDidChange:(UITextView *)textView
{
    if ([textView respondsToSelector: @selector(updateForTextChange)])
    {
        [textView updateForTextChange];
    }

}

1
당신의 drawRect를 재정 의하여 자신의 자리를 칠해야 정확한 동작을 얻으려면 : (자리를 그릴 경우에만 [자기 isFirstResponder] && [자동 텍스트] 길이] == 0!) becomeFirstResponder과 resignFirstResponder 내부 및 전화 setNeedsDisplay
rpetrich

6

'UITextView'하위 클래스의 자체 버전을 만들었습니다. Sam Soffes 의 알림 사용 아이디어는 마음에 들었지만 drawRect : 덮어 쓰기는 마음에 들지 않았습니다. 나에게 잔인한 것 같습니다. 나는 매우 깨끗한 구현을했다고 생각합니다.

내 서브 클래스를 여기서 볼 수 있습니다 . 데모 프로젝트도 포함되어 있습니다.


6

이 스레드에는 많은 답변이 있지만 여기에 내가 선호하는 버전이 있습니다.

그것은 확장 기존의 UITextView클래스 그렇게 쉽게 재사용 가능한이며, 그것은 하지 않습니다 같은 이벤트 가로 채기 textViewDidChange(이미 다른 곳에서 이러한 이벤트를 차단 한 경우, 사용자의 암호를 해독 수 있음).

내 코드 (아래 참조)를 사용하여 다음과 같이 자리 표시자를 쉽게 추가 할 수 있습니다 UITextViews.

self.textViewComments.placeholder = @"(Enter some comments here.)";

이 새로운 자리 표시 자 값을 설정하면 UILabel은 위에 조용히 추가 한 UITextView다음 필요에 따라 숨기거나 표시합니다.

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

자, 이렇게 변경하려면 다음 코드가 포함 된 "UITextViewHelper.h"파일을 추가하십시오.

//  UITextViewHelper.h
//  Created by Michael Gledhill on 13/02/15.

#import <Foundation/Foundation.h>

@interface UITextView (UITextViewHelper)

@property (nonatomic, strong) NSString* placeholder;
@property (nonatomic, strong) UILabel* placeholderLabel;
@property (nonatomic, strong) NSString* textValue;

-(void)checkIfNeedToDisplayPlaceholder;

@end

... 그리고 이것을 포함하는 UITextViewHelper.m 파일 :

//  UITextViewHelper.m
//  Created by Michael Gledhill on 13/02/15.
//
//  This UITextView category allows us to easily display a PlaceHolder string in our UITextView.
//  The downside is that, your code needs to set the "textValue" rather than the "text" value to safely set the UITextView's text.
//
#import "UITextViewHelper.h"
#import <objc/runtime.h>

@implementation UITextView (UITextViewHelper)

#define UI_PLACEHOLDER_TEXT_COLOR [UIColor colorWithRed:170.0/255.0 green:170.0/255.0 blue:170.0/255.0 alpha:1.0]

@dynamic placeholder;
@dynamic placeholderLabel;
@dynamic textValue;

-(void)setTextValue:(NSString *)textValue
{
    //  Change the text of our UITextView, and check whether we need to display the placeholder.
    self.text = textValue;
    [self checkIfNeedToDisplayPlaceholder];
}
-(NSString*)textValue
{
    return self.text;
}

-(void)checkIfNeedToDisplayPlaceholder
{
    //  If our UITextView is empty, display our Placeholder label (if we have one)
    if (self.placeholderLabel == nil)
        return;

    self.placeholderLabel.hidden = (![self.text isEqualToString:@""]);
}

-(void)onTap
{
    //  When the user taps in our UITextView, we'll see if we need to remove the placeholder text.
    [self checkIfNeedToDisplayPlaceholder];

    //  Make the onscreen keyboard appear.
    [self becomeFirstResponder];
}

-(void)keyPressed:(NSNotification*)notification
{
    //  The user has just typed a character in our UITextView (or pressed the delete key).
    //  Do we need to display our Placeholder label ?
   [self checkIfNeedToDisplayPlaceholder];
}

#pragma mark - Add a "placeHolder" string to the UITextView class

NSString const *kKeyPlaceHolder = @"kKeyPlaceHolder";
-(void)setPlaceholder:(NSString *)_placeholder
{
    //  Sets our "placeholder" text string, creates a new UILabel to contain it, and modifies our UITextView to cope with
    //  showing/hiding the UILabel when needed.
    objc_setAssociatedObject(self, &kKeyPlaceHolder, (id)_placeholder, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    self.placeholderLabel = [[UILabel alloc] initWithFrame:self.frame];
    self.placeholderLabel.numberOfLines = 1;
    self.placeholderLabel.text = _placeholder;
    self.placeholderLabel.textColor = UI_PLACEHOLDER_TEXT_COLOR;
    self.placeholderLabel.backgroundColor = [UIColor clearColor];
    self.placeholderLabel.userInteractionEnabled = true;
    self.placeholderLabel.font = self.font;
    [self addSubview:self.placeholderLabel];

    [self.placeholderLabel sizeToFit];

    //  Whenever the user taps within the UITextView, we'll give the textview the focus, and hide the placeholder if necessary.
    [self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap)]];

    //  Whenever the user types something in the UITextView, we'll see if we need to hide/show the placeholder label.
    [[NSNotificationCenter defaultCenter] addObserver:self selector: @selector(keyPressed:) name:UITextViewTextDidChangeNotification object:nil];

    [self checkIfNeedToDisplayPlaceholder];
}
-(NSString*)placeholder
{
    //  Returns our "placeholder" text string
    return objc_getAssociatedObject(self, &kKeyPlaceHolder);
}

#pragma mark - Add a "UILabel" to this UITextView class

NSString const *kKeyLabel = @"kKeyLabel";
-(void)setPlaceholderLabel:(UILabel *)placeholderLabel
{
    //  Stores our new UILabel (which contains our placeholder string)
    objc_setAssociatedObject(self, &kKeyLabel, (id)placeholderLabel, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    [[NSNotificationCenter defaultCenter] addObserver:self selector: @selector(keyPressed:) name:UITextViewTextDidChangeNotification object:nil];

    [self checkIfNeedToDisplayPlaceholder];
}
-(UILabel*)placeholderLabel
{
    //  Returns our new UILabel
    return objc_getAssociatedObject(self, &kKeyLabel);
}
@end

예, 코드가 많지만 일단 프로젝트에 추가하고 .h 파일을 포함 시키면 ...

#import "UITextViewHelper.h"

...에서 자리 표시자를 쉽게 사용할 수 있습니다 UITextViews.

그래도 하나의 문제가 있습니다.

이렇게하면 :

self.textViewComments.placeholder = @"(Enter some comments here.)";
self.textViewComments.text = @"Ooooh, hello there";

자리 표시자가 텍스트 위에 나타납니다 . text값 을 설정하면 일반 알림이 호출되지 않으므로 자리 표시자를 표시 / 숨길 지 여부를 결정하기 위해 함수를 호출하는 방법을 알 수 없었습니다.

해결책은 다음 textValue보다 오히려 설정하는 것입니다 text.

self.textViewComments.placeholder = @"(Enter some comments here.)";
self.textViewComments.textValue = @"Ooooh, hello there";

또는 text값을 설정 한 다음을 호출 할 수 있습니다 checkIfNeedToDisplayPlaceholder.

self.textViewComments.text = @"Ooooh, hello there";
[self.textViewComments checkIfNeedToDisplayPlaceholder];

나는 애플이 우리에게 제공하는 것과 우리가 개발자로서 우리의 앱에 실제로 필요 로하는 것 사이의 "격차를 메우기"때문에 이와 같은 솔루션을 좋아한다 . 이 코드를 한 번 작성하여 "helper".m / .h 파일 라이브러리에 추가하면 시간이 지남에 따라 SDK의 성능이 저하되기 시작합니다.

(저는 UITextViews에 "clear"버튼을 추가하는 비슷한 도우미를 작성 UITextField했습니다 UITextView.


나는 이것이 깨끗한 방법을 좋아하지만 두 번째 UIView / UILabel이 필요한 방법을 싫어합니다 (UITextView에서 속성 / 색상 / 글꼴을 쉽게 상속하지는 않습니다). 그래도 좋은 공헌
mattsven

6

먼저 .h 파일에 레이블을 붙입니다.

여기에 나는

UILabel * lbl;

그런 다음 viewDidLoad 아래의 .m에서 선언하십시오.

lbl = [[UILabel alloc] initWithFrame:CGRectMake(8.0, 0.0,250, 34.0)];

lbl.font=[UIFont systemFontOfSize:14.0];

[lbl setText:@"Write a message..."];

[lbl setBackgroundColor:[UIColor clearColor]];

[lbl setTextColor:[UIColor lightGrayColor]];

[textview addSubview:lbl];

textview는 내 TextView입니다.

이제 선언

-(void)textViewDidChange:(UITextView *)textView {

 if (![textView hasText]){

    lbl.hidden = NO;

 }
 else{
    lbl.hidden = YES;
 }

}

Textview 자리 표시자가 준비되었습니다!


6

포드 'UITextView + Placeholder'를 사용하는 것이 좋습니다.

pod 'UITextView+Placeholder'

당신의 코드에

#import "UITextView+Placeholder.h"

////    

UITextView *textView = [[UITextView alloc] init];
textView.placeholder = @"How are you?";
textView.placeholderColor = [UIColor lightGrayColor];

5
    - (void)textViewDidChange:(UITextView *)textView
{
    placeholderLabel.hidden = YES;
}

텍스트 뷰 위에 레이블을 붙입니다.


또는 텍스트가 없을 때 다시 표시 할 수 있습니다. lblPlaceholder.hidden =! [textView.text isEqualToString : @ ""];
Despotovic

나는이 깨끗한 솔루션을 좋아하고 textview 자체에 주입하지 않습니다.
Itachi

5

UITextView에서 자리 표시자를 만들 수는 없지만이 방법으로 자리 표시 자와 같은 효과를 생성 할 수 있습니다.

  - (void)viewDidLoad{      
              commentTxtView.text = @"Comment";
              commentTxtView.textColor = [UIColor lightGrayColor];
              commentTxtView.delegate = self;

     }
       - (BOOL) textViewShouldBeginEditing:(UITextView *)textView
     {
         commentTxtView.text = @"";
         commentTxtView.textColor = [UIColor blackColor];
         return YES;
     }

     -(void) textViewDidChange:(UITextView *)textView
     {

    if(commentTxtView.text.length == 0){
        commentTxtView.textColor = [UIColor lightGrayColor];
        commentTxtView.text = @"Comment";
        [commentTxtView resignFirstResponder];
    }
    }

또는 텍스트보기에 레이블을 추가 할 수 있습니다

       lbl = [[UILabel alloc] initWithFrame:CGRectMake(10.0, 0.0,textView.frame.size.width - 10.0, 34.0)];


[lbl setText:kDescriptionPlaceholder];
[lbl setBackgroundColor:[UIColor clearColor]];
[lbl setTextColor:[UIColor lightGrayColor]];
textView.delegate = self;

[textView addSubview:lbl];

그리고 설정

- (void)textViewDidEndEditing:(UITextView *)theTextView
{
     if (![textView hasText]) {
     lbl.hidden = NO;
}
}

- (void) textViewDidChange:(UITextView *)textView
{
    if(![textView hasText]) {
      lbl.hidden = NO;
}
else{
    lbl.hidden = YES;
 }  
}

5

이것은 UITextField의 자리 표시자를 완벽하게 모방합니다. 자리 표시 자 텍스트는 실제로 무언가를 입력 할 때까지 유지됩니다.

private let placeholder = "Type here"

@IBOutlet weak var textView: UITextView! {
    didSet {
        textView.textColor = UIColor.lightGray
        textView.text = placeholder
        textView.selectedRange = NSRange(location: 0, length: 0)
    }
}

extension ViewController: UITextViewDelegate {

    func textViewDidChangeSelection(_ textView: UITextView) {
        // Move cursor to beginning on first tap
        if textView.text == placeholder {
            textView.selectedRange = NSRange(location: 0, length: 0)
        }
    }

    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        if textView.text == placeholder && !text.isEmpty {
            textView.text = nil
            textView.textColor = UIColor.black
            textView.selectedRange = NSRange(location: 0, length: 0)
        }
        return true
    }

    func textViewDidChange(_ textView: UITextView) {
        if textView.text.isEmpty {
            textView.textColor = UIColor.lightGray
            textView.text = placeholder
        }
    }
}

4

여기에 또 다른 방법이 있습니다.이 UITextField자리 표시 자의 약간의 들여 쓰기를 재현하는 방법입니다 .

왼쪽 아래 모서리가 정렬되도록 UITextField오른쪽 아래를 끕니다 UITextView. 자리 표시 자 텍스트를 텍스트 필드에 추가하십시오.

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

[tView setDelegate:self];
tView.contentInset = UIEdgeInsetsMake(-8,-8,0,0);
tView.backgroundColor = [UIColor clearColor];

그런 다음 다음을 추가하십시오.

- (void)textViewDidChange:(UITextView *)textView {
    if (textView.text.length == 0) {
        textView.backgroundColor = [UIColor clearColor];            
    } else {
        textView.backgroundColor = [UIColor whiteColor];
    }
}

4

쉽게 만들 수 있습니다

하나의 UILabel을 작성하고 텍스트보기에 배치하십시오 (텍스트를 자리 표시 자 설정 색상으로 회색으로 지정하십시오-xib 에서이 모든 것을 할 수 있습니다). 이제 헤더 파일에서 UILabel과 textviewDelegate를 선언하십시오. 이제 레이블을 숨길 수 있습니다 텍스트 뷰를 클릭하면

아래의 완전한 코드

헤더

@interface ViewController :UIViewController<UITextViewDelegate>{
 }
   @property (nonatomic,strong) IBOutlet UILabel *PlceHolder_label;
   @property (nonatomic,strong) IBOutlet UITextView *TextView;

@end

이행

@implementation UploadFoodImageViewController
@synthesize PlceHolder_label,TextView;

  - (void)viewDidLoad
    {
       [super viewDidLoad];
    }


 - (BOOL)textViewShouldBeginEditing:(UITextView *)textView{

       if([textView isEqual:TextView]){
            [PlceHolder_label setHidden:YES];
            [self.tabScrlVw setContentOffset:CGPointMake(0,150) animated:YES];
          }
      return YES;
    }

@종료

textView 및 UILabel을 xib의 파일 소유자에게 연결하는 것을 잊지 마십시오


4

UTPlaceholderTextView를 살펴 보십시오 .

UITextField와 유사한 자리 표시자를 지원하는 UITextView의 편리한 하위 클래스입니다. 주요 특징 :

  • 하위 뷰를 사용하지 않습니다
  • drawRect를 재정의하지 않습니다.
  • 자리 표시자는 길이가 임의 일 수 있으며 일반적인 텍스트와 같은 방식으로 렌더링됩니다.

4

나는이 모든 것을 읽었지만 모든 테스트에서 효과가있는 매우 짧은 Swift 3 솔루션을 생각해 냈습니다. 좀 더 일반적 일 수는 있지만 프로세스는 간단합니다. 다음은 "TextViewWithPlaceholder"라고 부르는 전체 내용입니다.

import UIKit

class TextViewWithPlaceholder: UITextView {

    public var placeholder: String?
    public var placeholderColor = UIColor.lightGray

    private var placeholderLabel: UILabel?

    // Set up notification listener when created from a XIB or storyboard.
    // You can also set up init() functions if you plan on creating
    // these programmatically.
    override func awakeFromNib() {
        super.awakeFromNib()

        NotificationCenter.default.addObserver(self,
                                           selector: #selector(TextViewWithPlaceholder.textDidChangeHandler(notification:)),
                                           name: .UITextViewTextDidChange,
                                           object: self)

        placeholderLabel = UILabel()
        placeholderLabel?.alpha = 0.85
        placeholderLabel?.textColor = placeholderColor
    }

    // By using layoutSubviews, you can size and position the placeholder
    // more accurately. I chose to hard-code the size of the placeholder
    // but you can combine this with other techniques shown in previous replies.
    override func layoutSubviews() {
        super.layoutSubviews()

        placeholderLabel?.textColor = placeholderColor
        placeholderLabel?.text = placeholder

        placeholderLabel?.frame = CGRect(x: 6, y: 4, width: self.bounds.size.width-16, height: 24)

        if text.isEmpty {
            addSubview(placeholderLabel!)
            bringSubview(toFront: placeholderLabel!)
        } else {
            placeholderLabel?.removeFromSuperview()
        }
    }

    // Whenever the text changes, just trigger a new layout pass.
    func textDidChangeHandler(notification: Notification) {
        layoutSubviews()
    }
}

여기에 약간의 우려가 있습니다. layoutSubviews()직접 전화하면 안됩니다 . 그리고 NotificationCenter 관찰자를 제거하지 않았습니다.
Hlung

4

나는 빠른 수업을 썼습니다. 필요할 때마다이 클래스를 가져올 수 있습니다.

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

}

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


1
이것은 실제로 내가 찾을 수있는 가장 깨끗한 솔루션 중 하나이며 이것을 사용하여 끝났습니다. 특히 레이블에 대한 삽입을 고려하고 제약 조건을 사용하는 것은 좋은 터치입니다. 다른 솔루션이 많이 보지 못했습니다. 경계, 글꼴, RTL 등 변경 관리
Mark
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.