ตัวยึดตำแหน่งใน UITextView


717

UITextViewใบสมัครของฉันใช้ ตอนนี้ผมต้องการที่จะมีตัวยึดตำแหน่งเดียวกับที่คุณสามารถตั้งค่าสำหรับUITextViewUITextField

ทำอย่างไร


TTTextEditor ของ Three20 (ตัวเองใช้ UITextField) สนับสนุนข้อความตัวยึดรวมทั้งการเติบโตตามความสูง (เปลี่ยนเป็น UITextView)
Josh Benjamin


20
วิธีการเกี่ยวกับการใช้หมวดหมู่ UITextView + ตัวยึด? github.com/devxoul/UITextView-Placeholder
devxoul

2
ฉันชอบโซลูชันของ @ devxoul เพราะใช้หมวดหมู่ไม่ใช่คลาสย่อย และยังสร้างฟิลด์สำหรับตัวเลือก 'ตัวยึด' (สีตัวยึดและสีตัวอักษร) ในตัวตรวจสอบของ IB มันใช้เทคนิคการผูก รหัสที่ดีคืออะไร
samthui7

หากคุณกำลังใช้UITextViewวิธีแก้ไขปัญหาจะแตกต่างกันมาก นี่เป็นวิธีแก้ปัญหาสองสามข้อ ตัวยึดพื้นที่ลอยและตัวยึดพื้นที่ดั้งเดิมปลอม
clearlight

คำตอบ:


672

ฉันทำการแก้ไขเล็กน้อยเพื่อแก้ไขปัญหาของ bcd เพื่อให้สามารถเริ่มต้นจากXibไฟล์การตัดข้อความและเพื่อรักษาสีพื้นหลัง หวังว่ามันจะช่วยคนอื่นให้เดือดร้อน

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) จะต้องแทนที่ paste: - (void) paste: (id) ผู้ส่ง {[super paste: sender]; [ข้อความตัวเองเปลี่ยนแปลง: ไม่มี]; }
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ก่อนรวมถึงวิธีการเหล่านี้เช่น

@interface MyClass () <UITextViewDelegate>
@end

รหัสสำหรับ Swift 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ก่อนรวมถึงวิธีการเหล่านี้เช่น

class MyClass: UITextViewDelegate
{

}

1
นี่ยอดเยี่ยม (ฉันชอบเรียบง่าย) สำหรับ 1 หน้าจอที่มี 1 UITextView เหตุผลสำหรับการแก้ปัญหาที่ซับซ้อนมากขึ้นคือถ้าคุณมีแอพที่ใหญ่ขึ้นด้วยหน้าจอ MANY และ MANY UITextViews คุณไม่ต้องการทำสิ่งนี้ซ้ำแล้วซ้ำอีก คุณอาจต้องการ subclass UITextView เพื่อให้เหมาะกับความต้องการของคุณแล้วใช้มัน
ghostatron

42
หากมีคนพิมพ์ "ตัวยึดข้อความที่นี่ ... " ในกล่องข้อความก็จะทำงานเช่นเดียวกับข้อความตัวยึดตำแหน่ง นอกจากนี้ในระหว่างการส่งคุณจะต้องตรวจสอบเกณฑ์เหล่านั้นทั้งหมด
Anindya Sengupta

7
ข้อความตัวยึดควรจะแสดงแม้ว่าเขตข้อมูลจะกลายเป็นผู้ตอบกลับและวิธีนี้จะไม่ทำงานสำหรับสิ่งนี้
phatmann

17
@ jklp ฉันจะยืนยันว่า "over-engineered" เป็นวิธีที่สะอาดและใช้งานได้อีกครั้ง ... และดูเหมือนว่ามันไม่ยุ่งเกี่ยวกับtextคุณลักษณะของ textview ซึ่งค่อนข้างดี .. ทุกที่ที่วิธีนี้แก้ไขมัน
Cameron Askew

2
ทำไมการโทรถึงกลายเป็นครั้งแรกการตอบกลับและลาออกการตอบกลับครั้งแรกในวิธีการมอบสิทธิ์
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:ตอนนี้เป็นเอกสารและรองรับการใช้งานในเครื่องมือสร้างส่วนต่อประสาน

อัปเดต 11/24/13:ชี้ไปที่ repo ใหม่


ฉันชอบโซลูชันของคุณและฉันได้เพิ่มการแทนที่setTextเพื่ออัปเดตตัวยึดตำแหน่งเมื่อเปลี่ยนคุณสมบัติข้อความแบบเป็นโปรแกรม: - (เป็นโมฆะ) setText: (NSString *) สตริง {[super setText: string]; [ตนเอง _updateShouldDrawPlaceholder]; }
olegueret

1
ฉันชอบโซลูชันของคุณด้วย แต่คุณพลาดวิธี awakefromnib ดังนั้นวิธีการเริ่มต้นของคุณจะไม่ถูกเรียก ฉันเอามันมาจากอันอื่นที่นี่
toxaq

หมายเหตุ - หมายเลขเวทย์มนตร์จะแตกต่างกันไปขึ้นอยู่กับขนาดตัวอักษร ตำแหน่งที่แน่นอนสามารถคำนวณได้จากแบบอักษร แต่อาจไม่คุ้มค่าสำหรับการใช้งานนี้ ตำแหน่งที่ถูกต้องของตัวยึดตำแหน่งควรเป็น 2px ทางด้านขวาของตำแหน่งเคอร์เซอร์เมื่อไม่มีข้อความ
memmons

เพื่อแก้ปัญหาการโหลดจากปลายปากกา: คุณอาจต้องการเพิ่ม - (id) initWithCoder: (NSCoder *) aDecoder; initializer เพื่อติดตามอันที่มีอยู่
Joris Kluivers

นี่มันหวาน แม้ว่าnotificationอาร์กิวเมนต์จะถูกสะกดผิดในวิธีการสุดท้าย แต่ก็มีการเปลี่ยนแปลงน้อยเกินไปที่จะส่งเป็นการแก้ไข
Phil Calvin

53

ฉันพบว่าตัวเองเป็นวิธีที่ง่ายมากในการเลียนแบบผู้ถือสถานที่

  1. ใน NIB หรือรหัสให้ตั้งค่า textColor ของ textView เป็น lightGrayColor (เกือบตลอดเวลา)
  2. ตรวจสอบให้แน่ใจว่าผู้รับมอบสิทธิ์ของ textView ของคุณเชื่อมโยงกับเจ้าของไฟล์และใช้ UITextViewDelegate ในไฟล์ส่วนหัวของคุณ
  3. ตั้งค่าข้อความเริ่มต้นของมุมมองข้อความของคุณเป็น (ตัวอย่าง: "ตัวยึดตำแหน่ง Foobar")
  4. implement: (BOOL) textViewShouldBeginEditing: (UITextView *) textView

แก้ไข:

เปลี่ยนถ้าคำสั่งเพื่อเปรียบเทียบแท็กแทนที่จะเป็นข้อความ หากผู้ใช้ลบข้อความของพวกเขาก็เป็นไปได้ที่จะลบส่วนหนึ่งของเจ้าของสถานที่โดยไม่@"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 7

52

คุณสามารถทำอะไรได้ตั้งค่ามุมมองข้อความที่มีบางส่วนค่าเริ่มต้นในtextทรัพย์สินและเปลี่ยนtextColorไป[UIColor grayColor]หรือสิ่งที่คล้ายกัน จากนั้นเมื่อใดก็ตามที่มุมมองข้อความสามารถแก้ไขได้ให้ล้างข้อความและนำเสนอเคอร์เซอร์และหากช่องข้อความว่างเปล่าอีกครั้งให้ใส่ตัวยึดข้อความของคุณกลับมา เปลี่ยนสี[UIColor blackColor]เป็นตามความเหมาะสม

มันไม่เหมือนกับฟังก์ชั่นตัวจองพื้นที่ใน UITextField แต่มันปิด


9
ฉันใช้lightGrayColorเสมอซึ่งดูเหมือนจะตรงกับสีของข้อความตัวยึดตำแหน่ง
Bill

ฉันเพิ่งอ่านข้อความนี้ แต่ฉันต้องการเพิ่มการรีเซ็ตสีเป็นสีดำและรีเซ็ตคุณสมบัติข้อความใน textViewShouldBeginEditing: (UITextView *) textView ทำงานได้ดีมาก นี่เป็นโซลูชันที่ดีและรวดเร็วมากเมื่อเทียบกับโซลูชันด้านล่าง (แต่ยังคงเป็นโซลูชันที่สวยงามด้านล่าง, subclassing uitextview โมดูลอื่น ๆ อีกมากมาย)
Enrico Susatyo

3
เป็นจริง แต่ไม่ได้เลียนแบบพฤติกรรมของ UITextField ซึ่งจะแทนที่ข้อความตัวยึดเมื่อผู้ใช้พิมพ์บางอย่างไม่ใช่เมื่อทำการแก้ไขเริ่มต้นและเพิ่มตัวยึดตำแหน่งอีกครั้งมุมมองที่ว่างเปล่าไม่ใช่เมื่อการแก้ไขเสร็จสิ้นจริง
Ash

47

คุณสามารถตั้งค่าฉลากบนUITextViewโดย

[UITextView addSubView:lblPlaceHoldaer];

และซ่อนไว้ในTextViewdidChangeวิธีการ

นี่เป็นวิธีที่ง่ายและสะดวก


45

หากมีคนต้องการโซลูชันสำหรับ Swift:

เพิ่ม 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
    }
}

นี่โอเค แต่ไม่ดีพอ หากผู้ใช้พิมพ์ "Placeholder Text ... " (ชัดกรณีตัวอักษร) จะเป็นการแบ่งตรรกะของคุณ
Lucas Chwe

45

Simple Swift 3 solution

เพิ่ม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 โซลูชันของฉันไม่ต้องการให้คุณ subclass และทำงานถ้าผู้ใช้พิมพ์โดยตรงหรือวางข้อความ!

ตัวอย่างของตัวยึด แอพ Twitter

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

และทำให้คลาส parent เป็นตัวแทน UITextView ก่อนรวมวิธีการเหล่านี้เช่น

@interface MyClass () <UITextViewDelegate>
@end

20

SZTextViewผมแนะนำให้ใช้

https://github.com/glaszig/SZTextView

เพิ่มค่าเริ่มต้นของคุณUITextViewจากstoryboardนั้นเปลี่ยนคลาสที่กำหนดเองเป็นSZTextViewชอบด้านล่าง👇👇👇👇

ป้อนคำอธิบายรูปภาพที่นี่

จากนั้นคุณจะเห็นตัวเลือกใหม่สองตัวในAttribute Inspector👇👇👇👇

ป้อนคำอธิบายรูปภาพที่นี่


20

ด้านล่างนี้เป็นพอร์ต Swift ของรหัส "Objc" SAMTextView "ที่โพสต์เป็นหนึ่งในการตอบคำถามจำนวนหนึ่ง ฉันทดสอบบน iOS 8 ฉันปรับแต่งสิ่งต่าง ๆ สองสามอย่างรวมถึงขอบเขตชดเชยตำแหน่งข้อความตัวยึดเนื่องจากต้นฉบับสูงเกินไปและถูกเกินไป (ใช้คำแนะนำจากหนึ่งในความคิดเห็นต่อโพสต์นั้น)

ฉันรู้ว่ามีวิธีแก้ปัญหาง่าย ๆ มากมาย แต่ฉันชอบวิธีการของ subclassing 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)
    }
}

ขอบคุณสำหรับการทำเวอร์ชั่น swift คุณสามารถอธิบายสิ่งที่มีวิธีการ awakeFromNib ที่เพิ่งเรียกซุปเปอร์?
ปิแอร์

มันตั้งค่าตัวยึด แต่ไม่อัปเดตเมื่อคุณเริ่มพิมพ์
David

ไม่เป็นไรฉันใส่การแจ้งเตือนใน awakeFromNib และใช้งานได้ทันที
เดวิด

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

}

(การตรวจสอบครั้งที่สองในอื่นถ้าคำสั่งสำหรับกรณีที่ไม่มีอะไรถูกป้อนและผู้ใช้กด backspace)

เพียงตั้งค่าคลาสของคุณเป็น 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

สวัสดีคุณสามารถใช้ IQTextView ที่มีอยู่ใน IQKeyboard Manager มันใช้งานง่ายและบูรณาการเพียงแค่ตั้งค่าคลาสของมุมมองข้อความของคุณเป็น 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(textView.textColor == [UIColor lightGrayColor]){ textView.textColor = [UIColor blackColor]; textView.text = [textView.text substringToIndex: 1]; มิฉะนั้นตัวละครตัวแรกที่ถูกป้อนใน textview ถูกวางไว้ที่ส่วนท้ายของข้อความ
Flexicoder

7

วิธีง่ายๆในการใช้สิ่งนี้ภายในโค้ดบางบรรทัด:

ใช้หนึ่งป้ายกำกับจนถึง 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

ฉันได้ปรับเปลี่ยนการใช้งาน Sam Soffes เพื่อทำงานกับ iOS7:

- (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วิธีการที่อาจเปลี่ยนแบบอักษรและ thigns อื่น ๆ ที่อาจส่งผลกระทบต่อพวกเขา คุณอาจต้องการข้ามการทำพจนานุกรม "ขี้เกียจ" ของพจนานุกรมคุณลักษณะหากนั่นไม่ทำให้เกิดปัญหา

แก้ไข:

อย่าลืมเรียก setNeedsDisplay ใน setBounds เวอร์ชันที่แทนที่ถ้าคุณต้องการให้ตัวยึดตำแหน่งดูดีหลังจากภาพเคลื่อนไหวอัตโนมัติและสิ่งที่คล้ายกัน


ฉันคิดว่าคุณควรเพิ่ม insets.left ในพารามิเตอร์ offset x
Karmeye

มันไม่ setFrame แทน setBounds ใช่ไหม
JakubKnejzlik

Nope! setFrame ไม่ได้ถูกเรียกระหว่างการเคลื่อนไหวของเลย์เอาต์ที่ดูเหมือนว่า
Nailer

6

คุณสามารถสร้างคลาสใหม่ TextViewWithPlaceholder เป็นคลาสย่อยของ UITextView

(รหัสนี้ค่อนข้างหยาบ - แต่ฉันคิดว่ามันถูกต้อง)

@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!) และเรียก setNeedsDisplay ภายใน becomeFirstResponder และ resignFirstResponder
rpetrich

6

ฉันสร้างคลาสย่อยของตัวเองเป็น 'UITextView' ฉันชอบความคิดของSam Soffes ที่ใช้การแจ้งเตือน แต่ฉันไม่ชอบ drawRect: overwrite ดูเหมือน overkill กับฉัน ฉันคิดว่าฉันใช้งานได้ดีมาก

คุณสามารถดู subclass ของฉันที่นี่ โครงการสาธิตรวมอยู่ด้วย


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คุณสามารถใช้ตัวยึดตำแหน่งใน

มีหนึ่ง gotcha ว่า

หากคุณทำสิ่งนี้:

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

หรือคุณสามารถตั้งค่าแล้วโทรtextcheckIfNeedToDisplayPlaceholder

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

ฉันชอบวิธีแก้ปัญหาเช่นนี้เพราะพวกเขา "เติมช่องว่าง" ระหว่างที่ Apple ให้บริการกับเราและสิ่งที่เรา (ในฐานะนักพัฒนา) ต้องการแอพของเราจริงๆ คุณเขียนโค้ดนี้หนึ่งครั้งเพิ่มลงในไลบรารีของไฟล์ "helper" .m / .h และ SDK จะเริ่มหงุดหงิดน้อยลงเมื่อเวลาผ่านไป

(ฉันเขียนผู้ช่วยที่คล้ายกันเพื่อเพิ่มปุ่ม "ชัดเจน" ใน UITextViews ของฉันอีกสิ่งหนึ่งที่น่ารำคาญอยู่UITextFieldแต่ไม่ใช่ในUITextView... )


ฉันชอบความสะอาดนี้ แต่ฉันไม่ชอบว่าต้องใช้ UIView / UILabel ตัวที่สอง (และไม่ได้รับคุณลักษณะ / สี / แบบอักษรจาก UITextView จริง ๆ ) ผลงานที่ดีแม้ว่า
mattsven

6

ก่อนอื่นให้นำเลเบลในไฟล์. h

ที่นี่ฉันจะ

UILabel * lbl;

จากนั้นใน. m ภายใต้ viewDidLoad จะประกาศ

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 + ตัวยึดตำแหน่ง'

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

ใส่ป้ายกำกับเหนือ textview


หรือดียิ่งขึ้นคุณสามารถแสดงอีกครั้งเมื่อไม่มีข้อความ: lblPlaceholder.hidden =! [textView.text isEqualToString: @ ""];
Despotovic

ฉันชอบวิธีการทำความสะอาดนี้ไม่มีการฉีดข้อความให้ตัวเอง
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];
    }
    }

หรือคุณสามารถเพิ่มป้ายกำกับใน textview เช่นเดียวกับ

       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 ตอนนี้คุณสามารถซ่อนป้ายกำกับได้ เมื่อคุณคลิกที่ TextView

กรอกรหัสด้านล่าง

หัวข้อ

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

@end

อย่าลืมเชื่อมต่อ textView และ UILabel กับ filesowner จาก xib


4

ลองดูที่UTPlaceholderTextView

นี่คือคลาสย่อยที่สะดวกของ UITextView ที่สนับสนุนตัวยึดตำแหน่งที่คล้ายกับของ UITextField คุณสมบัติหลัก:

  • ไม่ใช้การดูย่อย
  • ไม่แทนที่ 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 ฯลฯ
ทำเครื่องหมาย
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.