Я делаю приложение, которое использует UITextView
. Теперь я хочу, чтобы UITextView
имел местозаполнитель, похожий на тот, который вы можете установить для UITextField
.
Кто-нибудь знает, как это сделать?
Я сделал несколько незначительных изменений в решении 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
_updateShouldDrawPlaceholder
и использование setNeedsDisplay
более тщательны.
Простой способ, просто создайте текст-заполнитель в 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
и сделайте родительский класс a UITextViewDelegate
, прежде чем включать эти методы, например.
@interface MyClass () <UITextViewDelegate>
@end
Код для Swift 3.1
func textViewDidBeginEditing(_ textView: UITextView)
{
if (textView.text == "placeholder text here...")
{
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
и сделайте родительский класс a UITextViewDelegate
, прежде чем включать эти методы, например.
class MyClass: UITextViewDelegate
{
}
Я не был доволен решением, которое было опубликовано, поскольку они были немного тяжелыми. Добавление представлений в представление не является идеальным (особенно в 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
Это намного проще, чем другие, поскольку он не использует subviews (или имеет утечки). Не стесняйтесь использовать его.
Обновление 11/10/11: Теперь оно задокументировано и поддерживает использование в Interface Builder.
Обновление 11/24/13: Укажите новое репо.
setText
чтобы также обновлять заполнитель при программном изменении свойства текста: - (void) setText: (NSString *) string {[super setText: string]; [self _updateShouldDrawPlaceholder]; }
Что вы можете сделать, это настроить текстовое представление с некоторым начальным значением в свойстве text
и изменить textColor
на [UIColor grayColor]
или что-то подобное. Затем, всякий раз, когда текстовое представление становится редактируемым, очищайте текст и представляйте курсор, а если текстовое поле снова пусто, верните текст заполнителя. При необходимости измените цвет на [UIColor blackColor]
.
Это не то же самое, что и функция-заполнитель в UITextField, но она закрывается.
Я нашел себе простой способ подражать владельцу места
Edit:
Изменены операторы 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;
}
Также возможно reset текст заполнителя при возврате клавиатуры и [длина текста] == 0
EDIT:
Просто, чтобы сделать последнюю часть более четкой - вот как вы можете вернуть текст заполнителя:
- (void)textViewDidChange:(UITextView *)textView
{
if([textView.text length] == 0)
{
textView.text = @"Foobar placeholder";
textView.textColor = [UIColor lightGrayColor];
textView.tag = 0;
}
}
Вы можете установить метку на UITextView
на
[UITextView addSubView:lblPlaceHoldaer];
и спрячьте его в методе TextViewdidChange
.
Это простой и легкий способ.
Если кому-то нужно решение для Swift:
Добавить UITextViewDelegate в класс
var placeHolderText = "Placeholder Text..."
override func viewDidLoad() {
super.viewDidLoad()
textView.delegate = self
}
func textViewShouldBeginEditing(textView: UITextView) -> Bool {
self.textView.textColor = UIColor.blackColor()
if(self.textView.text == placeHolderText) {
self.textView.text = ""
}
return true
}
func textViewDidEndEditing(textView: UITextView) {
if(textView.text == "") {
self.textView.text = placeHolderText
self.textView.textColor = UIColor.lightGrayColor()
}
}
override func viewWillAppear(animated: Bool) {
if(currentQuestion.answerDisplayValue == "") {
self.textView.text = placeHolderText
self.textView.textColor = UIColor.lightGrayColor()
} else {
self.textView.text = "xxx" // load default text / or stored
self.textView.textColor = UIColor.blackColor()
}
}
Я продлил ответ KmKndy, так что местозаполнитель остается видимым до тех пор, пока пользователь не начнет редактировать UITextView
, а не просто нажимает на него. Это отражает функциональность в приложениях Twitter и Facebook. Мое решение не требует от вас подкласса и работает, если пользователь вводит непосредственно или вставляет текст!
- (void)textViewDidChangeSelection:(UITextView *)textView{
if ([textView.text isEqualToString:@"What 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 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 happening?";
textView.textColor = [UIColor lightGrayColor];
[textView setSelectedRange:NSMakeRange(0, 0)];
}
}
- (void)textViewDidEndEditing:(UITextView *)textView
{
if ([textView.text isEqualToString:@""]) {
textView.text = @"What 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 happening?"]) {
textView.text = @"";
textView.textColor = [UIColor blackColor];
}
return YES;
}
просто не забудьте установить myUITextView с точным текстом при создании, например.
UITextView *myUITextView = [[UITextView alloc] init];
myUITextView.delegate = self;
myUITextView.text = @"What happening?";
myUITextView.textColor = [UIColor lightGrayColor]; //optional
и сделайте родительский класс делегатом UITextView, прежде чем включать эти методы, например.
@interface MyClass () <UITextViewDelegate>
@end
Простое решение Swift 3
Добавьте UITextViewDelegate
в свой класс
Установить yourTextView.delegate = self
Создайте placeholderLabel
и расположите его внутри yourTextView
Теперь просто оставьте placeholderLabel.alpha
на textViewDidChange
:
// MARK: TextView Delegate
func textViewDidChange(_ textView: UITextView) {
UIView.animate(withDuration: 0.3) {
self.placeholderLabel.alpha = textView.text.isEmpty ? 1 : 0
}
}
вам может потребоваться сыграть с позицией placeholderLabel
, чтобы настроить его правильно, но это не должно быть слишком сложно.
let alpha = CGFloat(textView.text.isEmpty ? 1.0 : 0.0) if alpha != lblPlaceholder.alpha { UIView.animate(withDuration: 0.3) { self.lblPlaceholder.alpha = alpha } }
Я рекомендую использовать SZTextView
.
https://github.com/glaszig/SZTextView
Добавьте по умолчанию UITextView
из storyboard
, а затем измените свой собственный класс на SZTextView
, как показано ниже <
Затем вы увидите две новые опции в Attribute Inspector
Здесь проще найти решение, которое ведет себя точно так же, как и 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;
}
вот как я это сделал:
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
Извините, что добавил еще один ответ, но я просто вытащил что-то вроде этого, и это создало экземпляр с ближайшим к 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);
}
}
if(textView.textColor == [UIColor lightGrayColor]){ textView.textColor = [UIColor blackColor]; textView.text = [textView.text substringToIndex: 1];
В противном случае первый символ, вводимый в текстовое представление, помещается в конец текста.
Ниже представлен Swift-порт кода Obam, который является одним из первых ответов на вопрос. Я протестировал его на iOS 8. Я изменил пару вещей, включая смещения, связанные с размещением текста-заполнителя, поскольку оригинал был слишком высоким и слишком правильным (использовалось предложение в одном из комментариев к этому сообщению).
Я знаю, что существует множество простых решений, но мне нравится подход к подклассу UITextView, поскольку он многократно используется, и мне не нужно загромождать классы, используя его с помощью механизмов.
Swift 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)
}
}
Я изменил реализацию 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
в методах, которые могут изменить шрифт и другие тики, которые могут повлиять на них. Вы также можете пропустить "ленивое" создание словаря атрибутов, если это вас не испугает.
EDIT:
Не забудьте вызвать setNeedsDisplay в переопределенной версии setBounds, если вам нравится, чтобы заполнитель выглядел хорошо после анимации автозапуска и т.п.
Простой способ использования этого в некоторой строке кода:
Возьмите одну метку до UITextView в .nib подключив эту метку к вашему коду, После этого.
- (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;
}
Сначала возьмите метку в файле .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 готов!
Привет, вы можете использовать IQTextView, доступный в IQKeyboard Manager, просто использовать и интегрировать только класс класса вашего текстового представления в IQTextView, и вы можете использовать его свойство для установки метки метки с желаемым цветом. Вы можете загрузить библиотеку из IQKeyboardManager
или вы можете установить его из cocoapods.
Я сделал свою собственную версию подкласса "UITextView". Мне понравилась идея Sam Soffes использования уведомлений, но мне не понравилось drawRect: overwrite. Кажется, переполняет меня. Я думаю, что я сделал очень чистую реализацию.
Вы можете посмотреть мой подкласс здесь. Также представлен демонстрационный проект.
Вы также можете создать новый класс 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];
}
}
Невозможно создать местозаполнитель в 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;
}
}
В этой ветке было много ответов, но вот версия, которую я предпочитаю.
Он расширяет существующий класс UITextView
, поэтому его легко повторно использовать, и он не перехватывает события типа textViewDidChange
(что может нарушить код пользователя, если они уже перехватывают эти события в другом месте).
Используя мой код (показано ниже), вы можете легко добавить местозаполнитель к любому из ваших UITextViews
следующим образом:
self.textViewComments.placeholder = @"(Enter some comments here.)";
Когда вы устанавливаете это новое значение placeholder, оно спокойно добавляет 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 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
Yup, это много кода, но как только вы добавили его в свой проект и включили файл .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];
Мне нравятся такие решения, поскольку они "заполняют пробел" между тем, что Apple предоставляет нам, и тем, что нам (как разработчикам) действительно нужно в наших приложениях. Вы пишете этот код один раз, добавьте его в свою библиотеку файлов "хелпер".m/.h, и со временем SDK начнет становиться менее расстраивающим.
(Я написал подобный помощник для добавления "чистой" кнопки в UITextViews, еще одна вещь, которая досадно существует в UITextField
, но не в UITextView
...)
Посмотрите UTPlaceholderTextView.
Это удобный подкласс UITextView, который поддерживает местозаполнитель, аналогичный элементу UITextField. Основные особенности:
Давайте упростим
Создайте один 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 к файловому администратору из xib
Здесь есть еще один способ сделать это, который воспроизводит небольшой отступ UITextField
placeholder:
Перетащите 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];
}
}
- (void)textViewDidChange:(UITextView *)textView
{
placeholderLabel.hidden = YES;
}
поместите надпись над текстом.
Это хорошо подбирает местозаполнитель 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
}
}
}
OK мой ansewer немного отличается Я создаю небольшой класс, чтобы сделать это для вас.
Файл TextViewShader.m
#import "TextViewShader.h"
@implementation TextViewShader
-(id)initWithShadedTextView:(NSString *)text textViewToShade:(UITextView *)textview {
self = [super initWithFrame:textview.frame];
if (self) {
if (shadeLabel==nil)
{
shadeLabel= [[UILabel alloc]initWithFrame:CGRectMake(10, 0, textview.frame.size.width, 30)];
}
shadeLabel.text =text;// @"Enter Your Support Request";
shadeLabel.textColor = [UIColor lightGrayColor];
[textview setDelegate: self];
[textview addSubview:shadeLabel];
}
return self;
}
-(void)textViewDidChange:(UITextView *)textView{
if (textView.text.length==0)
{
shadeLabel.hidden=false;
}
else
{
shadeLabel.hidden=true;
}
}
@end
Файл TextViewShader.h
#import <UIKit/UIKit.h>
@interface TextViewShader : UIView<UITextViewDelegate>{
UILabel *shadeLabel;
}
-(id)initWithShadedTextView:(NSString *)text textViewToShade:(UITextView *)textview ;
@end
это простая одна строка использования кода (не забудьте добавить #import "TextViewShader.h" )
TextViewShader* shader = [[TextViewShader alloc]initWithShadedTextView:@"Enter Your Support Request" textViewToShade: youruitextviewToshade];
получайте удовольствие:)
Как вставить placeholder в UITextView?
Ответ PJR работает как шарм. Те, что здесь, не работали для меня. Возможно, iOS5.
Я создал переменную экземпляра, чтобы проверить, покажу ли местозаполнитель или нет:
BOOL showPlaceHolder;
UITextView * textView; // and also the textView
В viewDidLoad я установил:
[self setPlaceHolder];
Вот что это делает:
- (void)setPlaceholder
{
textView.text = NSLocalizedString(@"Type your question here", @"placeholder");
textView.textColor = [UIColor lightGrayColor];
self.showPlaceHolder = YES; //we save the state so it won't disappear in case you want to re-edit it
}
Я также создал кнопку, чтобы отменить клавиатуру. Вам не обязательно это делать, но здесь все круто, что заполнитель снова отображается, если ничего не было введено
- (void)textViewDidBeginEditing:(UITextView *)txtView
{
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStyleDone target:self action:@selector(resignKeyboard)];
if (self.showPlaceHolder == YES)
{
textView.textColor = [UIColor blackColor];
textView.text = @"";
self.showPlaceHolder = NO;
}
}
- (void)resignKeyboard
{
[textView resignFirstResponder];
//here if you created a button like I did to resign the keyboard, you should hide it
if (textView.text.length == 0) {
[self setPlaceholder];
}
}
Я прочитал все это, но придумал очень короткое решение 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.
Я знаю, что уже есть много ответов на этот вопрос, но я действительно не нашел их достаточным (по крайней мере, в Свифте). Мне понадобилась функциональность "placeholder" UITextField
в моем UITextView
(я хотел, чтобы точное поведение, включая атрибуты отображения текста, анимации и т.д., И не хотело поддерживать это со временем). Я также хотел получить решение, которое обеспечило бы ту же самую точную границу, что и UITextField
(а не приближенное, похожее на то, как выглядит сейчас, но похожее на него и всегда будет выглядеть точно так же). Так что, хотя я и не был изначально фанатом для добавления дополнительного контроля в микс, казалось, что для достижения моих целей мне пришлось использовать фактический UITextField
и позволить ему выполнять работу.
Это решение обрабатывает позиционирование заполнителя и синхронизацию шрифта между двумя элементами управления, так что текст-заполнитель является точным шрифтом и позицией в виде текста, введенного в элемент управления (что-то большее, чем другие решения не адресуют).
// This class is necessary to support "inset" (required to position placeholder
// appropriately in TextView)
//
class TextField: UITextField
{
var inset: UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0);
override func textRectForBounds(bounds: CGRect) -> CGRect
{
return UIEdgeInsetsInsetRect(bounds, inset);
}
override func placeholderRectForBounds(bounds: CGRect) -> CGRect
{
return UIEdgeInsetsInsetRect(bounds, inset);
}
}
// This class implements a UITextView that has a UITextField behind it, where the
// UITextField provides the border and the placeholder text functionality (so that the
// TextView looks and works like a UITextField).
//
class TextView : UITextView, UITextViewDelegate
{
var textField = TextField();
required init?(coder: NSCoder)
{
super.init(coder: coder);
}
override init(frame: CGRect, textContainer: NSTextContainer?)
{
super.init(frame: frame, textContainer: textContainer);
self.delegate = self;
// Create a background TextField with clear (invisible) text and disabled
self.textField.borderStyle = UITextBorderStyle.RoundedRect;
self.textField.textColor = UIColor.clearColor();
self.textField.userInteractionEnabled = false;
// Align the background TextView to where text appears in the TextField, so
// that any placeholder will be in the correct position.
self.textField.contentVerticalAlignment = UIControlContentVerticalAlignment.Top;
self.textField.inset = UIEdgeInsets(
top: self.textContainerInset.top,
left: self.textContainerInset.left + self.textContainer.lineFragmentPadding,
bottom: self.textContainerInset.bottom,
right: self.textContainerInset.right
);
// The background TextField should use the same font (for the placeholder)
self.textField.font = self.font;
self.addSubview(textField);
self.sendSubviewToBack(textField);
}
convenience init()
{
self.init(frame: CGRectZero, textContainer: nil)
}
override var font: UIFont?
{
didSet
{
// Keep the font of the TextView and background textField in sync
self.textField.font = self.font;
}
}
var placeholder: String? = nil
{
didSet
{
self.textField.placeholder = self.placeholder;
}
}
override func layoutSubviews()
{
super.layoutSubviews()
// Do not scroll the background textView
self.textField.frame = CGRectMake(0, self.contentOffset.y, self.frame.width, self.frame.height);
}
// UITextViewDelegate - Note: If you replace delegate, your delegate must call this
func scrollViewDidScroll(scrollView: UIScrollView)
{
// Do not scroll the background textView
self.textField.frame = CGRectMake(0, self.contentOffset.y, self.frame.width, self.frame.height);
}
// UITextViewDelegate - Note: If you replace delegate, your delegate must call this
func textViewDidChange(textView: UITextView)
{
// Updating the text in the background textView will cause the placeholder to
// appear/disappear (including any animations of that behavior - since the
// textView is doing this itself).
self.textField.text = self.text;
}
}
fatalError
Я написал более чистую реализацию, попробовав некоторые из предложенных подходов, и отправил ее в Github. Запросы и вопросы по запросу приветствуются.
Некоторые ключевые улучшения, которые существуют в сравнении с другими подходами, представленными здесь:
drawRect:
. (Пожалуйста, никогда не делайте этого.)UITextField
).- (BOOL) textViewShouldBeginEditing:(UITextView *)textView
{
//NSLog(@"textViewShouldBeginEditing");
if( [tvComment.text isEqualToString:@"Comment"] && [tvComment.textColor isEqual:[UIColor lightGrayColor]] ){
tvComment.text = @"";
tvComment.textColor = [UIColor blackColor];
}
return YES;
}
- (void)keyboardWillBeHidden:(NSNotification*)aNotification{
//NSLog(@"keyboardWillBeHidden");
//Manage comment field placeholdertext
if(tvComment.text.length == 0){
tvComment.textColor = [UIColor lightGrayColor];
tvComment.text = @"Comment";
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
tvComment.textColor = [UIColor lightGrayColor];
}
TVComment - это свойство, которое содержит рассматриваемый textView. Это сделает трюк.
Вот код для быстрого 3.1
Оригинальный код Джейсона Джорджа в первом ответе.
Не забудьте установить свой собственный класс для TextView в конструкторе интерфейса в UIPlaceHolderTextView, а затем установить свойства placeholder и placeHolder.
import UIKit
@IBDesignable
class UIPlaceHolderTextView: UITextView {
@IBInspectable var placeholder: String = ""
@IBInspectable var placeholderColor: UIColor = UIColor.lightGray
private let uiPlaceholderTextChangedAnimationDuration: Double = 0.05
private let defaultTagValue = 999
private var placeHolderLabel: UILabel?
override func awakeFromNib() {
super.awakeFromNib()
NotificationCenter.default.addObserver(
self,
selector: #selector(textChanged),
name: NSNotification.Name.UITextViewTextDidChange,
object: nil
)
}
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
NotificationCenter.default.addObserver(
self,
selector: #selector(textChanged),
name: NSNotification.Name.UITextViewTextDidChange,
object: nil
)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
NotificationCenter.default.addObserver(
self,
selector: #selector(textChanged),
name: NSNotification.Name.UITextViewTextDidChange,
object: nil
)
}
deinit {
NotificationCenter.default.removeObserver(
self,
name: NSNotification.Name.UITextViewTextDidChange,
object: nil
)
}
@objc private func textChanged() {
guard !placeholder.isEmpty else {
return
}
UIView.animate(withDuration: uiPlaceholderTextChangedAnimationDuration) {
if self.text.isEmpty {
self.viewWithTag(self.defaultTagValue)?.alpha = CGFloat(1.0)
}
else {
self.viewWithTag(self.defaultTagValue)?.alpha = CGFloat(0.0)
}
}
}
override var text: String! {
didSet{
super.text = text
textChanged()
}
}
override func draw(_ rect: CGRect) {
if !placeholder.isEmpty {
if placeHolderLabel == nil {
placeHolderLabel = UILabel.init(frame: CGRect(x: 0, y: 8, width: bounds.size.width - 16, height: 0))
placeHolderLabel!.lineBreakMode = .byWordWrapping
placeHolderLabel!.numberOfLines = 0
placeHolderLabel!.font = font
placeHolderLabel!.backgroundColor = UIColor.clear
placeHolderLabel!.textColor = placeholderColor
placeHolderLabel!.alpha = 0
placeHolderLabel!.tag = defaultTagValue
self.addSubview(placeHolderLabel!)
}
placeHolderLabel!.text = placeholder
placeHolderLabel!.sizeToFit()
self.sendSubview(toBack: placeHolderLabel!)
if text.isEmpty && !placeholder.isEmpty {
viewWithTag(defaultTagValue)?.alpha = 1.0
}
}
super.draw(rect)
}
}
Swift 3.1
Попробовав все быстрые ответы, этот ответ спас бы мне 3 часа исследований. Надеюсь, это поможет.
Убедитесь, что ваш textField (независимо от вашего пользовательского имени) указывает на его делегат в Storyboard и имеет @IBOutlet с yourCustomTextField
Добавьте в viewDidLoad()
следующее: оно появится при загрузке представления:
Покажите мне, как выглядит местозаполнитель:
yourCustomTextField = "Start typing..."
yourCustomTextField.textColor = .lightGray
UIViewController, UITextViewDelegate, UINavigationControllerDelegate
Этот код заставит yourCustomTextField уйти при вводе текстового поля:
func textViewDidBeginEditing (_ textView: UITextView) {
if (textView.text == "Start typing...") {
textView.text = ""
textView.textColor = .black
}
textView.becomeFirstResponder()
}
func textViewDidEndEditing(_ textView: UITextView) {
if (textView.text == "") {
textView.text = "Start typing..."
textView.textColor = .lightGray
}
textView.resignFirstResponder()
}
Общей проблемой является то, что iOS не предоставляет встроенную функцию-заполнитель для текстовых просмотров. Расширение UITextView
ниже пытается решить эту проблему, предлагая удобство, которое можно было бы ожидать от собственной функции, требуя, чтобы только одна строка кода добавляла местозаполнитель в экземпляр textview.
Недостатком этого решения является то, что он создает цепочку делегирования вызовов, он уязвим для (маловероятных) изменений к протоколу UITextViewDelegate в обновлении iOS. В частности, если iOS добавляет новые методы протокола и реализует любой из них в делегате для текстового представления с помощью заполнителя, эти методы не будут вызываться, если вы также не обновили расширение для переадресации этих вызовов.
В качестве альтернативы ответ Inline Placeholder является прочным и простеньким, как может быть.
Примеры использования:
• Если в текстовом представлении, заполняющем заполнитель, не используется UITextViewDelegate
:
/* Swift 3 */
class NoteViewController : UIViewController {
@IBOutlet weak var noteView: UITextView!
override func viewDidLoad() {
noteView.addPlaceholder("Enter some text...", color: UIColor.lightGray)
}
}
  -
• Если в текстовом представлении, набирающем местозаполнитель, используется символ UITextViewDelegate
:
/* Swift 3 */
class NoteViewController : UIViewController, UITextViewDelegate {
@IBOutlet weak var noteView: UITextView!
override func viewDidLoad() {
noteView.addPlaceholder("Phone #", color: UIColor.lightGray, delegate: self)
}
}
Реализация (расширение UITextView
):
/* Swift 3 */
extension UITextView: UITextViewDelegate
{
func addPlaceholder(_ placeholderText : String,
color : UIColor? = UIColor.lightGray,
delegate : UITextViewDelegate? = nil) {
self.delegate = self // Make receiving textview instance a delegate
let placeholder = UITextView() // Need delegate storage ULabel doesn't provide
placeholder.isUserInteractionEnabled = false //... so we *simulate* UILabel
self.addSubview(placeholder) // Add to text view instance view tree
placeholder.sizeToFit() // Constrain to fit inside parent text view
placeholder.tintColor = UIColor.clear // Unused in textviews. Can host our 'tag'
placeholder.frame.origin = CGPoint(x: 5, y: 0) // Don't cover I-beam cursor
placeholder.delegate = delegate // Use as cache for caller delegate
placeholder.font = UIFont.italicSystemFont(ofSize: (self.font?.pointSize)!)
placeholder.text = placeholderText
placeholder.textColor = color
}
func findPlaceholder() -> UITextView? { // find placeholder by its tag
for subview in self.subviews {
if let textview = subview as? UITextView {
if textview.tintColor == UIColor.clear { // sneaky tagging scheme
return textview
}
}
}
return nil
}
/*
* Safely daisychain to caller delegate methods as appropriate...
*/
public func textViewDidChange(_ textView: UITextView) { // ← need this delegate method
if let placeholder = findPlaceholder() {
placeholder.isHidden = !self.text.isEmpty // ← ... to do this
placeholder.delegate?.textViewDidChange?(textView)
}
}
/*
* Since we're becoming a delegate on behalf of this placeholder-enabled
* text view instance, we must forward *all* that protocol activity expected
* by the instance, not just the particular optional protocol method we need to
* intercept, above.
*/
public func textViewDidEndEditing(_ textView: UITextView) {
if let placeholder = findPlaceholder() {
placeholder.delegate?.textViewDidEndEditing?(textView)
}
}
public func textViewDidBeginEditing(_ textView: UITextView) {
if let placeholder = findPlaceholder() {
placeholder.delegate?.textViewDidBeginEditing?(textView)
}
}
public func textViewDidChangeSelection(_ textView: UITextView) {
if let placeholder = findPlaceholder() {
placeholder.delegate?.textViewDidChangeSelection?(textView)
}
}
public func textViewShouldEndEditing(_ textView: UITextView) -> Bool {
if let placeholder = findPlaceholder() {
guard let retval = placeholder.delegate?.textViewShouldEndEditing?(textView) else {
return true
}
return retval
}
return true
}
public func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
if let placeholder = findPlaceholder() {
guard let retval = placeholder.delegate?.textViewShouldBeginEditing?(textView) else {
return true
}
return retval
}
return true
}
public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if let placeholder = findPlaceholder() {
guard let retval = placeholder.delegate?.textView?(textView, shouldChangeTextIn: range, replacementText: text) else {
return true
}
return retval
}
return true
}
public func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
if let placeholder = findPlaceholder() {
guard let retval = placeholder.delegate?.textView?(textView, shouldInteractWith: URL, in: characterRange, interaction:
interaction) else {
return true
}
return retval
}
return true
}
public func textView(_ textView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
if let placeholder = findPlaceholder() {
guard let retval = placeholder.delegate?.textView?(textView, shouldInteractWith: textAttachment, in: characterRange, interaction: interaction) else {
return true
}
return retval
}
return true
}
}
1. Как расширение основного класса iOS, такого как UITextView, важно знать, что этот код имеет без взаимодействия с любыми текстовыми изображениями, которые не активируют местозаполнитель, например экземпляры textview, которые не были инициализированы вызовом addPlaceholder()
2.. Текстовые представления с включенной подписью прозрачно становятся UITextViewDelegate
, чтобы отслеживать количество символов, чтобы контролировать видимость заполнителя. Если делегат передан в addPlaceholder()
, этот код-цепочка (то есть переадресация) делегирует обратные вызовы этому делегату.
3. Автор исследует способы проверки протокола UITextViewDelegate
и прокси-сервера автоматически без необходимости жесткого кодирования каждого метода. Это приведет к модификации кода из изменений подписи метода и добавления новых методов в протокол.
Я написал класс в swift. Вы можете импортировать этот класс, когда требуется.
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)
}
}
Более простой подход заключается в создании вторичного UITextView со всеми теми же атрибутами, что и исходный текстовый вид, за исключением другого textColor, с ограничениями для обеспечения их выравнивания. Затем, когда любые символы вводятся в основное текстовое представление, скройте клонированный текстовый вид, иначе покажите клонированный текстовый вид с некоторым текстом.
Это может быть достигнуто несколькими способами, но относительно чистым способом будет подкласс UITextView и сохранить всю эту логику в подклассе.
Итак, подкласс UITextView и пусть он лениво создает это место:
Файл интерфейса:
@interface FOOTextView : UITextView <UITextViewDelegate>
@property (nonatomic, copy) NSString *placeholderText;
- (void)checkPlaceholder;
@end
Файл реализации:
#import "FOOTextView.h"
@interface FOOTextView ()
@property (nonatomic, strong) UITextView *placeholderTextView;
@end
@implementation FOOTextView
- (void)checkPlaceholder {
// Hide the placeholder text view if we've got any text
self.placeholderTextView.hidden = (self.text.length > 0 || self.attributedText.length > 0);
}
- (void)setPlaceholderText:(NSString *)placeholderText {
_placeholderText = [placeholderText copy];
// Setup the placeholder text view if we haven't already
[self setupPlaceholderTextView];
// Apply the placeholder text to the placeholder text view
self.placeholderTextView.text = placeholderText;
}
- (void)setupPlaceholderTextView {
if (!self.placeholderTextView) {
// Setup the place holder text view, duplicating our visual setup
self.placeholderTextView = [[UITextView alloc] initWithFrame:CGRectZero];
self.placeholderTextView.translatesAutoresizingMaskIntoConstraints = NO;
self.placeholderTextView.textColor = self.placeholderTextColor ? self.placeholderTextColor : [UIColor colorWithRed:199.f/255.f green:199.f/255.f blue:205.f/255.f alpha:1.f];
self.placeholderTextView.userInteractionEnabled = NO;
self.placeholderTextView.font = self.font;
self.placeholderTextView.textAlignment = self.textAlignment;
self.placeholderTextView.backgroundColor = self.backgroundColor;
self.placeholderTextView.editable = NO;
// Our background color must be clear for the placeholder text view to show through
self.backgroundColor = [UIColor clearColor];
// Insert the placeholder text view into our superview, below ourself so it shows through
[self.superview insertSubview:self.placeholderTextView belowSubview:self];
// Setup constraints to ensure the placeholder text view stays aligned with us
NSLayoutConstraint *constraintCenterX = [NSLayoutConstraint constraintWithItem:self.placeholderTextView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.f constant:0.f];
NSLayoutConstraint *constraintCenterY = [NSLayoutConstraint constraintWithItem:self.placeholderTextView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.f constant:0.f];
NSLayoutConstraint *constraintWidth = [NSLayoutConstraint constraintWithItem:self.placeholderTextView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1.f constant:0.f];
NSLayoutConstraint *constraintHeight = [NSLayoutConstraint constraintWithItem:self.placeholderTextView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeHeight multiplier:1.f constant:0.f];
NSArray *constraints = @[constraintCenterX, constraintCenterY, constraintWidth, constraintHeight];
[self.superview addConstraints:constraints];
}
}
- (void)setPlaceholderTextColor:(UIColor *)placeholderTextColor {
_placeholderTextColor = placeholderTextColor;
self.placeholderTextView.textColor = _placeholderTextColor;
}
- (void)setBackgroundColor:(UIColor *)backgroundColor {
// We don't want a background color ourselves, instead we want our placeholder text view to have the desired background color
[self.placeholderTextView setBackgroundColor:backgroundColor];
}
- (void)removeFromSuperview {
// Ensure we also remove our placeholder text view
[self.placeholderTextView removeFromSuperview];
self.placeholderTextView = nil;
[super removeFromSuperview];
}
#pragma mark - Text View Delegation
- (void)textViewDidChange:(UITextView *)textView {
[self checkPlaceholder];
}
@end
Используя вышеприведенный класс, если вы установите экземпляр делегата FOOTextView для себя, все будет работать из коробки:
FOOTextView *myTextView = ...
myTextView.placeholderText = @"What on your mind?";
myTextView.placeholderTextColor = [UIColor lightGrayColor];
myTextView.delegate = myTextView;
Если вы хотите, чтобы другой объект захватил делегат, вам просто нужно вызвать метод viewPlaceholder текстового вида в методе textViewDidChange: delegate, например:
FOOTextView *myTextView = ...
myTextView.placeholderText = @"What on your mind?";
myTextView.placeholderTextColor = [UIColor lightGrayColor];
myTextView.delegate = self;
self.myTextView = myTextView;
- (void)textViewDidChange:(UITextView *)textView {
// Call the checkPlaceholder method to update the visuals
[self.myTextView checkPlaceholder];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.textViewEmpty = YES;
// Text view
self.textView = [[UITextView alloc] init];
self.textView.translatesAutoresizingMaskIntoConstraints = NO; // For AutoLayout
self.textView.delegate = self;
self.textView.textColor = [UIColor grayColor];
self.textView.text = @"Placeholder";
// Add subview and constraints
}
#pragma mark - UITextView
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView {
if (self.isTextViewEmpty) {
textView.textColor = [UIColor blackColor];
textView.text = @"";
}
return YES;
}
- (void)textViewDidChange:(UITextView *)textView {
if (textView.text.length > 0) {
self.textViewEmpty = NO;
} else {
self.textViewEmpty = YES;
}
}
- (BOOL)textViewShouldEndEditing:(UITextView *)textView {
if (self.isTextViewEmpty) {
textView.textColor = [UIColor lightGrayColor];
textView.text = placeholderText;
}
return YES;
}
В классе .h
@interface RateCommentViewController : UIViewController<UITextViewDelegate>{IBoutlet UITextview *commentTxtView;}
В классе .m
- (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];
}
}
Вот простой и умный способ добиться совершенного поведения.
Возьмите заполнитель из UITextField
.
Настройте текстовое поле и установите его текст прозрачным.
self.placeholderTextField = [[UITextField alloc] init];
/* adjust the frame to fit it in the first line of your textView */
self.placeholderTextField.frame = CGRectMake(0.0, 0.0, yourTextView.width, 30.0);
self.placeholderTextField.textColor = [UIColor clearColor];
self.placeholderTextField.userInteractionEnabled = NO;
self.placeholderTextField.font = yourTextView.font;
self.placeholderTextField.placeholder = @"sample placeholder";
[yourTextView addSubview:self.placeholderTextField];
Установите делегат textView и синхронизируйте textField и textView.
yourTextView.delegate = self;
затем
- (void)textViewDidChange:(UITextView *)textView {
self.placeholderTextField.text = textView.text;
}
Мое решение GCPTextView - простая, легкая замена для UITextView, которая не полагается на UILabelView. Его можно найти на https://github.com/mbachrach/GCPTextView.
Самый простой способ изменить цвет текста заполнителя - через построитель интерфейса раскадровки XCode. Выберите интересующий UITextField и откройте инспектор идентификации справа. Нажмите на символ плюса в атрибутах Runtime User Defined Runtime и добавьте новую строку с Key Path как _placeholderLabel.textColor, введите цвет и значение в нужный цвет.
UITextView
.
Просто создайте подкласс @IBDesignable
вашего UITextView
:
@IBDesignable class AttributedTextView: UITextView {
private let placeholderLabel = UILabel()
@IBInspectable var placeholder: String = "" {
didSet {
setupPlaceholderLabelIfNeeded()
textViewDidChange()
}
}
override var text: String! {
didSet {
textViewDidChange()
}
}
//MARK: - Initialization
override func awakeFromNib() {
super.awakeFromNib()
setupPlaceholderLabelIfNeeded()
NotificationCenter.default.addObserver(self, selector: #selector(textViewDidChange), name: .UITextViewTextDidChange, object: nil)
}
//MARK: - Deinitialization
deinit {
NotificationCenter.default.removeObserver(self)
}
//MARK: - Internal
func textViewDidChange() {
placeholderLabel.isHidden = !text.isEmpty
layoutIfNeeded()
}
//MARK: - Private
private func setupPlaceholderLabelIfNeeded() {
placeholderLabel.removeFromSuperview()
placeholderLabel.frame = CGRect(x: 0, y: 8, width: frame.size.width, height: 0)
placeholderLabel.textColor = UIColor.lightGray
placeholderLabel.text = placeholder
placeholderLabel.sizeToFit()
insertSubview(placeholderLabel, at: 0)
}
}
а затем просто установите ваш заполнитель в инспекторе идентификации:
После просмотра (и опробования) большинства предлагаемых решений этой, казалось бы, очевидной, но отсутствующей функции UITextView, "лучшим", который я нашел, было то, что от BobDickinson. Но мне не нравилось прибегать к совершенно новому подклассу [я предпочитаю категории "drop-in" для таких простых функциональных дополнений] и не перехватывать методы UITextViewDelegate, что, вероятно, испортит ваш существующий код обработки UITextView. Итак, здесь я беру категорию drop-in, которая будет работать с любым существующим экземпляром 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 и вместо этого вместо него используется вместо этого placeholder (поэтому вам не нужно беспокоиться о правильном цвете и т.д.), а затем прослушивать уведомления, когда ваш UITextView изменяется на показать/скрыть этот UITextField (и, следовательно, он не мешает вашим существующим вызовам UITextViewDelegate). И нет никаких магических чисел...: -)
Объект objc_setAssociatedObject()/objc_getAssociatedObject() должен избегать подкласса UITextView. [К сожалению, чтобы правильно позиционировать UITextField, необходимо было ввести подкласс 'private', чтобы переопределить placeholderRectForBounds:]
Адаптировано из ответа Боб Дикинсона Свифта.
Простой класс для поддержки значков, отмеченных заполнителями в UITextView PlaceholderTextView
@IBOutlet weak var tvMessage: PlaceholderTextView!
// TODO: - Create Icon Text Attachment
let icon: NSTextAttachment = NSTextAttachment()
icon.image = UIImage(named: "paper-plane")
let iconString = NSMutableAttributedString(attributedString: NSAttributedString(attachment: icon))
tvMessage.icon = icon
// TODO: - Attributes
let textColor = UIColor.gray
let lightFont = UIFont(name: "Helvetica-Light", size: tvMessage.font!.pointSize)
let italicFont = UIFont(name: "Helvetica-LightOblique", size: tvMessage.font!.pointSize)
// TODO: - Placeholder Attributed String
let message = NSAttributedString(string: " " + "Personal Message", attributes: [ NSFontAttributeName: lightFont!, NSForegroundColorAttributeName: textColor])
iconString.append(message)
// TODO: - Italic Placeholder Part
let option = NSAttributedString(string: " " + "Optional", attributes: [ NSFontAttributeName: italicFont!, NSForegroundColorAttributeName: textColor])
iconString.append(option)
tvMessage.attributedPlaceHolder = iconString
tvMessage.layoutSubviews()
Я только что нашел, что с iOS 10
теперь вы можете фактически применить метод UITextView
к методу как UITextField
и установить внутри метода placeholder. Просто попробовал, и он работает без подкласса UITextView
.
Это пример того, что сработало для меня:
-(void)customizeTextField:(UITextField *)textField placeholder:(NSString *)pText withColor:(UIColor *)pTextColor{
textField.attributedPlaceholder = [[NSAttributedString alloc]
initWithString:pText
attributes:@{NSForegroundColorAttributeName:pTextColor}];
}
И чтобы использовать его для UITextView
, вам просто нужно передать его методу, используя такой бросок:
[self customizeTextField:(UITextField*)_myTextView placeholder:@"Placeholder" withColor:[UIColor blackColor]];
N.B: После теста я выяснил, что решение отлично работает и на iOS9.x
, но вызывает сбой на iOS8.x
Еще один ответ:
https://github.com/gcamp/GCPlaceholderTextView
Измените класс UITextView
в IB на GCPlaceholderTextView
и установите свойство placeholder
Я нашел собственное решение
- (void)textViewDidBeginEditing:(UITextView *)textView
{
if ([textView.text isEqualToString:PLACEHOLDER_TEXT])
{
textView.textColor = [UIColor lightGrayColor];
dispatch_async(dispatch_get_main_queue(), ^
{
textView.selectedRange = NSMakeRange(0, 0);
});
}
else
{
textView.textColor = [UIColor blackColor];
}
[textView becomeFirstResponder];
}
- (void)textViewDidEndEditing:(UITextView *)textView
{
if ([textView.text isEqualToString:@""])
{
textView.text = PLACEHOLDER_TEXT;
textView.textColor = [UIColor lightGrayColor];
}
[textView resignFirstResponder];
}
- (BOOL)textView:(UITextView *)textView
shouldChangeTextInRange:(NSRange)range
replacementText:(NSString *)text
{
if (range.location == 0 && range.length == [[textView text] length] && [text isEqualToString:@""])
{
textView.text = PLACEHOLDER_TEXT;
textView.textColor = [UIColor lightGrayColor];
dispatch_async(dispatch_get_main_queue(), ^
{
textView.selectedRange = NSMakeRange(0, 0);
});
return NO;
}
if ([textView.text isEqualToString:PLACEHOLDER_TEXT])
{
textView.text = @"";
textView.textColor = [UIColor blackColor];
}
return YES;
}
Если вы ищете простой способ достичь этого, попробуйте мой подход:
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
if ([[textView text] isEqualToString:PLACE_HOLDER_TEXT]) {
textView.text = @"";
textView.textColor = [UIColor blackColor];
}
return YES;
}
-(BOOL)textViewShouldEndEditing:(UITextView *)textView
{
if ([[textView text] length] == 0) {
textView.text = PLACE_HOLDER_TEXT;
textView.textColor = [UIColor lightGrayColor];
}
return YES;
}
Да, что он PLACE_HOLDER_TEXT
является NSString
, содержащим ваш placeholder
Ответ Джейсона будет немного похож на iOS7, исправить его, отрегулировав смещение _placeHolderLabel:
- (void)drawRect:(CGRect)rect
{
if( [[self placeholder] length] > 0 )
{
if (_placeHolderLabel == nil )
{
if ([[UIDevice currentDevice].systemVersion floatValue] >= 7)
_placeHolderLabel = [[UILabel alloc] initWithFrame:CGRectMake(4,8,self.bounds.size.width - 8,0)];
else
_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];
}
Более простой и по-прежнему учитывающий некоторый текст, введенный пользователем в какой-то момент
BOOL placeHolderTextVisible;
в viewDidLoad, установите его в YES (или DidMoveToSuperview или awakeFromNib)
то на - (BOOL) textView: (UITextView *) textView shouldBeginEditing
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView;
{
if (placeHolderTextVisible) {
placeHolderTextVisible = NO;
textView.text = @"";
}
return YES;
}
Three20 TTTextEditor (сам использует UITextField) поддерживает текст заполнителя, а также растет по высоте (он превращается в UITextView).
Вы можете просто установить метку в текстовом виде.
MyUITextView.h
@interface MyUITextView : UITextView {
UILabel* _placeholderLabel;
}
@property(nonatomic, assign)NSString *placeholder;
MyUITextView.m
@implementation MyUITextView
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Create placeholder
viewFrame = CGRectMake(0, 0, frame.size.width, 15);
_placeholderLabel = [[UILabel alloc] initWithFrame:viewFrame];
_placeholderLabel.textColor = [UIColor lightGrayColor];
[self addSubview:_placeholderLabel];
// Add text changed notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:nil];
}
return self;
}
- (void)setPlaceholder:(NSString *)placeholder {
_placeholderLabel.text = placeholder;
}
- (NSString*)placeholder {
return _placeholderLabel.text;
}
#pragma mark UITextViewTextDidChangeNotification
- (void)textChanged:(NSNotification *)notification {
_placeholderLabel.hidden = ([self.text lenght] == 0);
}
@end
Мне удалось добавить "place place" в UITextView с меньшим количеством кода. Это то, что я сделал:
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(60, 800, 200, 60)];
textView.text = @"Write characters here...";
textView.textColor=[UIColor grayColor];
textView.font = [UIFont fontWithName:@"Hevlatica" size:15];
textView.delegate=self;
Я предполагаю, что это не фактический заполнитель, потому что вы должны удалить текст перед тем, как писать, но это может помочь, если вам нужно что-то более простое.