Chủ đề này đã có rất nhiều câu trả lời, nhưng đây là phiên bản tôi thích.
Nó mở rộngUITextView
lớp hiện có để có thể dễ dàng sử dụng lại và nó không chặn các sự kiện như textViewDidChange
(có thể phá vỡ mã của người dùng, nếu họ đã chặn các sự kiện này ở nơi khác).
Sử dụng mã của tôi (hiển thị bên dưới), bạn có thể dễ dàng thêm một trình giữ chỗ vào bất kỳ thứ gì UITextViews
như thế này:
self.textViewComments.placeholder = @"(Enter some comments here.)";
Khi bạn đặt giá trị giữ chỗ mới này, nó sẽ lặng lẽ thêm một giá trị UILabel
lên trên của bạn UITextView
, sau đó ẩn / hiển thị nó khi cần thiết:
Được rồi, để thực hiện những thay đổi này, hãy thêm tệp "UITextViewHelper.h" có chứa mã này:
// 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
... và tệp UITextViewHelper.m chứa tệp này:
// 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
Vâng, đó là rất nhiều mã, nhưng một khi bạn đã thêm nó vào dự án của mình và bao gồm tệp .h ...
#import "UITextViewHelper.h"
... Bạn có thể dễ dàng sử dụng trình giữ chỗ trong UITextViews
.
Có một gotcha mặc dù.
Nếu bạn làm điều này:
self.textViewComments.placeholder = @"(Enter some comments here.)";
self.textViewComments.text = @"Ooooh, hello there";
... trình giữ chỗ sẽ xuất hiện trên đầu văn bản. Khi bạn đặt text
giá trị, không có thông báo thông thường nào được gọi, vì vậy tôi không thể tìm ra cách gọi chức năng của mình để quyết định có hiển thị / ẩn trình giữ chỗ hay không.
Giải pháp là đặt textValue
thay vì text
:
self.textViewComments.placeholder = @"(Enter some comments here.)";
self.textViewComments.textValue = @"Ooooh, hello there";
Ngoài ra, bạn có thể đặt text
giá trị, sau đó gọi checkIfNeedToDisplayPlaceholder
.
self.textViewComments.text = @"Ooooh, hello there";
[self.textViewComments checkIfNeedToDisplayPlaceholder];
Tôi thích các giải pháp như thế này, vì chúng "lấp đầy khoảng trống" giữa những gì Apple cung cấp cho chúng tôi và những gì chúng tôi (với tư cách là nhà phát triển) thực sự cần trong các ứng dụng của mình. Bạn viết mã này một lần, thêm nó vào thư viện các tệp "helper" .m / .h và theo thời gian, SDK thực sự bắt đầu trở nên ít bực bội hơn.
(Tôi đã viết một trình trợ giúp tương tự để thêm nút "xóa" vào UITextViews của mình, một thứ khác tồn tại một cách khó chịu UITextField
nhưng không có trong UITextView
...)