Заполнитель в UITextView - PullRequest
       217

Заполнитель в UITextView

700 голосов
/ 25 августа 2009

Мое приложение использует UITextView. Теперь я хочу, чтобы у UITextView был заполнитель, подобный тому, который вы можете установить для UITextField.

Как это сделать?

Ответы [ 60 ]

0 голосов
/ 05 октября 2012

Проще и с учетом некоторого текста, введенного пользователем в определенный момент

BOOL placeHolderTextVisible;

для viewDidLoad, установите для него значение YES (или DidMoveToSuperview, или awakeFromNib)

затем, на - (BOOL) textView: (UITextView *) textView shouldBeginEditing

    - (BOOL)textViewShouldBeginEditing:(UITextView *)textView;
{
   if (placeHolderTextVisible) {
    placeHolderTextVisible = NO;
    textView.text = @"";
   }
 return YES;
}
0 голосов
/ 03 июля 2013

Вы можете просто установить метку в текстовом представлении.

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
0 голосов
/ 12 апреля 2017

Swift 3.1

Попробовав все быстрые ответы, этот ответ сэкономил бы мне 3 часа исследований. Надеюсь, это поможет.

  1. Убедитесь, что ваш textField (независимо от используемого вами пользовательского имени) указывает на своего делегата в раскадровке и имеет @IBOutlet с yourCustomTextField

  2. Добавьте к viewDidLoad() следующее, оно появится при загрузке вида:

Покажите мне, что выглядит как заполнитель:

yourCustomTextField = "Start typing..." 
yourCustomTextField.textColor = .lightGray
  1. За пределами viewDidLoad, но внутри того же класса добавьте следующие объявления: UIViewController, UITextViewDelegate, UINavigationControllerDelegate

Этот код отключит ваш CustomTextField при вводе в textField:

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()
}
0 голосов
/ 30 сентября 2016

Простой класс для поддержки значков с атрибутами в 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()

Empty With text

0 голосов
/ 13 октября 2016

Изучив (и опробовав) большинство предлагаемых решений этой, казалось бы, очевидной, но отсутствующей функции 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:]

Адаптировано из быстрого ответа БобДикинсона.

0 голосов
/ 27 ноября 2018

Это моя версия UITextView с поддержкой заполнителей. Swift 4.2 https://gist.github.com/hlung/c5dda3a0c2087e5ae6c1fce8822c4713

Подкласс UITextView с поддержкой текстового заполнителя. Он использует другой UILabel, чтобы показать заполнитель, показанный, когда текст пуст.

0 голосов
/ 09 декабря 2016

Просто создайте @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)
    }
}

, а затем просто установите заполнитель в инспектор личности :

enter image description here

0 голосов
/ 08 ноября 2013

Ответ Джейсона будет выглядеть немного не так в 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];
}
0 голосов
/ 27 апреля 2019

Основываясь на некоторых замечательных предложениях, я смог собрать следующий легкий, совместимый с Interface-Builder подкласс UITextView, который:

  • Включает настраиваемый текст-заполнитель в стиле, аналогичном UITextField.
  • Не требует никаких дополнительных подпредставлений или ограничений.
  • Не требует делегирования или другого поведения от ViewController.
  • Не требует никаких уведомлений.
  • Сохраняет этот текст полностью отделенным от любых внешних классов, просматривая свойство text поля.

Любые предложения по улучшению приветствуются, особенно если есть какой-либо способ получить цвет заполнителя iOS программно, а не жестко кодировать его.

Swift v5:

import UIKit
@IBDesignable class TextViewWithPlaceholder: UITextView {

    override var text: String! { // Ensures that the placeholder text is never returned as the field's text
        get {
            if showingPlaceholder {
                return "" // When showing the placeholder, there's no real text to return
            } else { return super.text }
        }
        set { super.text = newValue }
    }
    @IBInspectable var placeholderText: String = ""
    @IBInspectable var placeholderTextColor: UIColor = UIColor(red: 0.78, green: 0.78, blue: 0.80, alpha: 1.0) // Standard iOS placeholder color (#C7C7CD). See https://stackoverflow.com/questions/31057746/whats-the-default-color-for-placeholder-text-in-uitextfield
    private var showingPlaceholder: Bool = true // Keeps track of whether the field is currently showing a placeholder

    override func didMoveToWindow() {
        super.didMoveToWindow()
        if text.isEmpty {
            showPlaceholderText() // Load up the placeholder text when first appearing, but not if coming back to a view where text was already entered
        }
    }

    override func becomeFirstResponder() -> Bool {
        // If the current text is the placeholder, remove it
        if showingPlaceholder {
            text = nil
            textColor = nil // Put the text back to the default, unmodified color
            showingPlaceholder = false
        }
        return super.becomeFirstResponder()
    }

    override func resignFirstResponder() -> Bool {
        // If there's no text, put the placeholder back
        if text.isEmpty {
            showPlaceholderText()
        }
        return super.resignFirstResponder()
    }

    private func showPlaceholderText() {
        showingPlaceholder = true
        textColor = placeholderTextColor
        text = placeholderText
    }
}
0 голосов
/ 02 августа 2013

Мне удалось добавить «заполнитель» в 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;

Я полагаю, что это не фактический заполнитель, потому что вы должны удалить текст, прежде чем писать, но это может помочь, если вы хотите что-то более простое.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...