Изучив (и опробовав) большинство предлагаемых решений этой, казалось бы, очевидной, но отсутствующей функции UITextView, я обнаружил, что «лучшим» наиболее близким было то, что предоставил BobDickinson. Но мне не нравилось прибегать к совершенно новому подклассу [я предпочитаю вставные категории для таких простых функциональных дополнений], а также к тому, что он перехватывал методы UITextViewDelegate, что, вероятно, испортит ваш существующий код обработки UITextView. Итак, вот мой взгляд на категорию вставки, которая будет работать с любым существующим экземпляром UITextView ...
#import <objc/runtime.h>
// Private subclass needed to override placeholderRectForBounds: to correctly position placeholder
@interface _TextField : UITextField
@property UIEdgeInsets insets;
@end
@implementation _TextField
- (CGRect)placeholderRectForBounds:(CGRect)bounds
{
CGRect rect = [super placeholderRectForBounds:bounds];
return UIEdgeInsetsInsetRect(rect, _insets);
}
@end
@implementation UITextView (Placeholder)
static const void *KEY;
- (void)setPlaceholder:(NSString *)placeholder
{
_TextField *textField = objc_getAssociatedObject(self, &KEY);
if (!textField) {
textField = [_TextField.alloc initWithFrame:self.bounds];
textField.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
textField.userInteractionEnabled = NO;
textField.font = self.font;
textField.contentVerticalAlignment = UIControlContentVerticalAlignmentTop;
textField.insets = UIEdgeInsetsMake(self.textContainerInset.top,
self.textContainerInset.left + self.textContainer.lineFragmentPadding,
self.textContainerInset.bottom,
self.textContainerInset.right);
[self addSubview:textField];
[self sendSubviewToBack:textField];
objc_setAssociatedObject(self, &KEY, textField, OBJC_ASSOCIATION_RETAIN);
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(updatePlaceholder:) name:UITextViewTextDidChangeNotification object:nil];
}
textField.placeholder = placeholder;
}
- (NSString*)placeholder
{
UITextField *textField = objc_getAssociatedObject(self, &KEY);
return textField.placeholder;
}
- (void)updatePlaceholder:(NSNotification *)notification
{
UITextField *textField = objc_getAssociatedObject(self, &KEY);
textField.font = self.font;
[textField setAlpha:self.text.length? 0 : 1];
}
@end
Его просто использовать, просто очевидное
UITextView *myTextView = UITextView.new;
...
myTextView.placeholder = @"enter text here";
Он работает, добавляя UITextField - в правильном месте - за вашим UITextView, и вместо этого эксплуатируя это заполнитель (следовательно, вам не нужно беспокоиться о получении правильного цвета и т. Д. ), затем прослушивание уведомлений всякий раз, когда ваш UITextView изменяется, чтобы показать / скрыть этот UITextField (и, следовательно, он не мешает вашим существующим вызовам UITextViewDelegate). И здесь нет магических чисел ...: -)
Функция objc_setAssociatedObject () / objc_getAssociatedObject () позволяет избежать необходимости создавать подкласс UITextView. [К сожалению, для правильного позиционирования UITextField было необходимо ввести «закрытый» подкласс, чтобы переопределить placeholderRectForBounds:]
Адаптировано из быстрого ответа БобДикинсона.