Marcador de posición en UITextView

717

Mi aplicación usa un UITextView. Ahora quiero UITextViewque tenga un marcador de posición similar al que puede establecer para un UITextField.

¿Como hacer esto?

usuario142019
fuente
TTTextEditor de Three20 (en sí mismo utilizando UITextField) admite texto de marcador de posición y crece por altura (se convierte en UITextView).
Josh Benjamin el
20
¿Qué tal usar la categoría UITextView + Placeholder? github.com/devxoul/UITextView-Placeholder
devxoul
2
Estoy a favor de la solución de @ devxoul porque usa categoría, no subclase. Y también crea un campo para las opciones de 'marcador de posición' (texto de marcador de posición y color de texto) en el inspector de IB. Utiliza algunas técnicas de unión. Qué gran código
samthui7
Si está utilizando UITextViewla solución se vuelve bastante diferente. Aquí hay un par de soluciones. Marcador de posición flotante y marcadores de posición nativos falsos
claro

Respuestas:

672

Hice algunas modificaciones menores a la solución de bcd para permitir la inicialización desde un Xibarchivo, ajuste de texto y para mantener el color de fondo. Esperemos que ahorre a otros el problema.

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
Jason George
fuente
2
en algunos casos (especialmente compatibilidad con iOS 5) se requiere anular pegar: - (nulo) pegar: (id) remitente {[super pegar: remitente]; [auto textChanged: nil]; }
Martin Ullrich
3
¡Buen material! Recordatorio sobre las mejores prácticas para NSString (o cualquier clase que tenga un equivalente NSMutableXXX), la propiedad debe ser "copiar" y no "retener".
Oli
2
¿Cómo instanciar este código? No veo ningún texto de marcador de posición y nada se borra cuando empiezo a escribir.
usuario798719
40
Esta es una implementación muy, muy mal escrita. Aquí hay una versión mucho más limpia que también vigila los cambios de dictado: github.com/cbowns/MPTextView
cbowns
10
No modifique la jerarquía de vistas en drawRect.
Karmeye
634

De manera fácil, solo cree texto de marcador de posición UITextViewutilizando los siguientes UITextViewDelegatemétodos:

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

solo recuerde configurar myUITextViewcon el texto exacto en la creación, por ejemplo

UITextView *myUITextView = [[UITextView alloc] init];
myUITextView.delegate = self;
myUITextView.text = @"placeholder text here...";
myUITextView.textColor = [UIColor lightGrayColor]; //optional

y hacer que la clase principal sea UITextViewDelegateantes de incluir estos métodos, por ejemplo

@interface MyClass () <UITextViewDelegate>
@end

Código para Swift 3.1

func textViewDidBeginEditing(_ textView: UITextView) 
{
    if (textView.text == "placeholder text here..." && textView.textColor == .lightGray)
    {
        textView.text = ""
        textView.textColor = .black
    }
    textView.becomeFirstResponder() //Optional
}

func textViewDidEndEditing(_ textView: UITextView)
{
    if (textView.text == "")
    {
        textView.text = "placeholder text here..."
        textView.textColor = .lightGray
    }
    textView.resignFirstResponder()
}

solo recuerde configurar myUITextViewcon el texto exacto en la creación, por ejemplo

 let myUITextView = UITextView.init()
 myUITextView.delegate = self
 myUITextView.text = "placeholder text here..."
 myUITextView.textColor = .lightGray

y hacer que la clase principal sea UITextViewDelegateantes de incluir estos métodos, por ejemplo

class MyClass: UITextViewDelegate
{

}
CmKndy
fuente
1
Esto es genial (me encanta lo simple) para 1 pantalla con 1 UITextView. La razón de las soluciones más complicadas es que si tiene una aplicación más grande con MUCHAS pantallas y MUCHAS UITextViews, no desea hacer esto una y otra vez. Probablemente desee subclasificar UITextView para satisfacer sus necesidades y luego usarlo.
ghostatron
42
Si alguien escribe "texto de marcador de posición aquí ..." en el cuadro de texto, también se comporta como texto de marcador de posición. Además, durante el envío deberá verificar todos esos criterios.
Anindya Sengupta
77
Se supone que el texto del marcador de posición se muestra incluso cuando el campo se convierte en el respondedor, y este método no funcionará para esto.
phatmann
17
@jklp Diría que la forma "sobredimensionada" es más limpia y reutilizable ... y parece que no textaltera el atributo de la vista de texto, que es bastante agradable ... mientras que este método lo modifica
Cameron Askew
2
¿Por qué las llamadas a convertirse en FirstResponder y resignFirstResponder en los métodos de delegado?
Adam Johns
119

No estaba muy contento con ninguna de las soluciones publicadas, ya que eran un poco pesadas. Agregar vistas a la vista no es realmente ideal (especialmente endrawRect: ). Ambos tenían fugas, lo cual tampoco es aceptable.

Aquí está mi solución: 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

Es mucho más simple que los demás, ya que no usa subvistas (o tiene fugas). Sientase libre de usarlo.

Actualización 10/11/11: ahora está documentado y admite su uso en Interface Builder.

Actualización 24/11/13: apunte al nuevo repositorio.

Sam Soffes
fuente
Me gusta su solución, y agregué una anulación setTextpara actualizar también el marcador de posición al cambiar la propiedad de texto programáticamente: - (vacío) setText: (NSString *) string {[super setText: string]; [self_updateShouldDrawPlaceholder]; }
olegueret
1
También me gusta su solución, pero se ha perdido el método awakefromnib, por lo que su método init no siempre será llamado. Lo tomé de uno de los otros aquí.
toxaq
Nota: los números mágicos variarán según el tamaño de fuente. El posicionamiento exacto se puede calcular a partir de la fuente, pero probablemente no valga la pena para esta implementación. La posición correcta del marcador de posición debe ser 2px a la derecha de la posición del cursor cuando no hay texto.
memmons
Para resolver la carga desde nib: probablemente desee agregar un - (id) initWithCoder: (NSCoder *) aDecoder; inicializador para acompañar al existente.
Joris Kluivers
Esto es dulce. Sin notificationembargo, el argumento está mal escrito en el último método, y es un cambio demasiado pequeño para enviarlo como edición.
Phil Calvin
53

Encontré una manera muy fácil de imitar un marcador de posición

  1. en la NIB o el código, configure textColor de textView en lightGrayColor (la mayoría de las veces)
  2. asegúrese de que el delegado de textView esté vinculado al propietario del archivo e implemente UITextViewDelegate en su archivo de encabezado
  3. establezca el texto predeterminado de su vista de texto en (ejemplo: "marcador de posición de Foobar")
  4. implementar: (BOOL) textViewShouldBeginEditing: (UITextView *) textView

Editar:

Cambió las declaraciones if para comparar etiquetas en lugar de texto. Si el usuario eliminó su texto, también fue posible eliminar accidentalmente una parte del marcador de posición @"Foobar placeholder". Esto significaba que si el usuario volvía a ingresar el texto Ver el siguiente método de delegado, -(BOOL) textViewShouldBeginEditing:(UITextView *) textViewno funcionaría como se esperaba. Intenté comparar por el color del texto en la instrucción if, pero descubrí que el color gris claro configurado en el generador de interfaces no es lo mismo que el color gris claro establecido en el código con[UIColor lightGreyColor]

- (BOOL) textViewShouldBeginEditing:(UITextView *)textView
{
    if(textView.tag == 0) {
        textView.text = @"";
        textView.textColor = [UIColor blackColor];
        textView.tag = 1;
    }
    return YES;
}

También es posible restablecer el texto del marcador de posición cuando vuelve el teclado y la [longitud de la vista de texto] == 0

EDITAR:

Solo para aclarar la última parte: así es cómo puede volver a configurar el texto del marcador de posición:

- (void)textViewDidChange:(UITextView *)textView
{
   if([textView.text length] == 0)
   {
       textView.text = @"Foobar placeholder";
       textView.textColor = [UIColor lightGrayColor];
       textView.tag = 0;
   }
}
vdevos
fuente
12
¡Me gusta mucho este enfoque! Lo único que haría con la edición anterior sería mover la implementación del método textViewDidChange: al método textViewDidEndEditing: para que el texto del marcador de posición solo regrese una vez que haya terminado de trabajar con el objeto.
horseshoe7
52

Lo que puede hacer es configurar la vista de texto con algún valor inicial en la textpropiedad, y cambiar textColora [UIColor grayColor]algo similar. Luego, cada vez que la vista de texto se vuelva editable, borre el texto y presente un cursor, y si el campo de texto vuelve a estar vacío, vuelva a colocar el texto del marcador de posición. Cambie el color [UIColor blackColor]según corresponda.

No es exactamente lo mismo que la funcionalidad de marcador de posición en un UITextField, pero está cerca.

Tim
fuente
99
Siempre he usado lightGrayColor , que parece coincidir con el color del texto del marcador de posición.
Bill
Estoy leyendo esto ahora, pero quiero agregar que restablecer el color a negro y restablecer la propiedad de texto en textViewShouldBeginEditing: (UITextView *) textView funciona muy bien. Esta es una solución muy agradable y rápida en comparación con las soluciones a continuación (pero aún así soluciones elegantes a continuación, subclasificando uitextview. Mucho más modular).
Enrico Susatyo
3
Es cierto, pero no imita el comportamiento de UITextField, que solo reemplaza su texto de marcador de posición cuando el usuario escribe algo, no cuando comienza la edición, y que agrega el marcador de posición nuevamente en el momento en que la vista está vacía, no cuando finaliza la edición.
Ash
47

Se puede establecer la etiqueta de la UITextViewpor

[UITextView addSubView:lblPlaceHoldaer];

y esconderlo TextViewdidChange método.

Esta es la manera simple y fácil.

MohammedYakub Moriswala
fuente
45

Si alguien necesita una solución para Swift:

Agregue UITextViewDelegate a su clase

var placeHolderText = "Placeholder Text..."

override func viewDidLoad() {
    super.viewDidLoad()
    textView.delegate = self
}

func textViewShouldBeginEditing(textView: UITextView) -> Bool {

    self.textView.textColor = .black

    if(self.textView.text == placeHolderText) {
        self.textView.text = ""
    }

    return true
}

func textViewDidEndEditing(textView: UITextView) {
    if(textView.text == "") {
        self.textView.text = placeHolderText
        self.textView.textColor = .lightGray
    }
}

override func viewWillAppear(animated: Bool) {

    if(currentQuestion.answerDisplayValue == "") {
        self.textView.text = placeHolderText
        self.textView.textColor = .lightGray
    } else {
        self.textView.text = "xxx" // load default text / or stored 
        self.textView.textColor = .black
    }
}
derdida
fuente
Esto está bien pero no lo suficientemente bueno. si el usuario escribe "Texto de marcador de posición ..." (caso extremo obviamente), se rompe su lógica
Lucas Chwe
45

Solución simple de Swift 3

Añadir UITextViewDelegatea tu clase

Conjunto yourTextView.delegate = self

Crea placeholderLabely posiciona dentroyourTextView

Ahora solo anima placeholderLabel.alphaen textViewDidChange:

  func textViewDidChange(_ textView: UITextView) {
    let newAlpha: CGFloat = textView.text.isEmpty ? 1 : 0
    if placeholderLabel.alpha != newAlpha {
      UIView.animate(withDuration: 0.3) {
        self.placeholderLabel.alpha = newAlpha
      }
    }
  }

es posible que tengas que jugar con la placeholderLabelposición para configurarlo correctamente, pero eso no debería ser demasiado difícil

budidino
fuente
1
Gran respuesta, solución simple. let alpha = CGFloat(textView.text.isEmpty ? 1.0 : 0.0) if alpha != lblPlaceholder.alpha { UIView.animate(withDuration: 0.3) { self.lblPlaceholder.alpha = alpha } }
Agregué
24

Extendí la respuesta de KmKndy, para que el marcador de posición permanezca visible hasta que el usuario comience a editarlo en UITextViewlugar de solo tocarlo. Esto refleja la funcionalidad en las aplicaciones de Twitter y Facebook. ¡Mi solución no requiere que subclasifique y funciona si el usuario escribe directamente o pega texto!

Ejemplo de marcador de posición Aplicación de Twitter

- (void)textViewDidChangeSelection:(UITextView *)textView{
    if ([textView.text isEqualToString:@"What's happening?"] && [textView.textColor isEqual:[UIColor lightGrayColor]])[textView setSelectedRange:NSMakeRange(0, 0)];

}

- (void)textViewDidBeginEditing:(UITextView *)textView{

    [textView setSelectedRange:NSMakeRange(0, 0)];
}

- (void)textViewDidChange:(UITextView *)textView
{
    if (textView.text.length != 0 && [[textView.text substringFromIndex:1] isEqualToString:@"What's happening?"] && [textView.textColor isEqual:[UIColor lightGrayColor]]){
        textView.text = [textView.text substringToIndex:1];
        textView.textColor = [UIColor blackColor]; //optional

    }
    else if(textView.text.length == 0){
        textView.text = @"What's happening?";
        textView.textColor = [UIColor lightGrayColor];
        [textView setSelectedRange:NSMakeRange(0, 0)];
    }
}

- (void)textViewDidEndEditing:(UITextView *)textView
{
    if ([textView.text isEqualToString:@""]) {
        textView.text = @"What's happening?";
        textView.textColor = [UIColor lightGrayColor]; //optional
    }
    [textView resignFirstResponder];
}

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
    if (textView.text.length > 1 && [textView.text isEqualToString:@"What's happening?"]) {
         textView.text = @"";
         textView.textColor = [UIColor blackColor];
    }

    return YES;
}

solo recuerde configurar myUITextView con el texto exacto en la creación, por ejemplo

UITextView *myUITextView = [[UITextView alloc] init];
myUITextView.delegate = self;
myUITextView.text = @"What's happening?";
myUITextView.textColor = [UIColor lightGrayColor]; //optional

y haga que la clase padre sea un delegado UITextView antes de incluir estos métodos, por ejemplo

@interface MyClass () <UITextViewDelegate>
@end
Matt
fuente
20

Recomiendo usar SZTextView.

https://github.com/glaszig/SZTextView

Agregue su valor predeterminado UITextViewde storyboardy luego cambie su clase personalizada SZTextViewcomo a continuación 👇👇👇👇

ingrese la descripción de la imagen aquí

Entonces verá dos nuevas opciones en el Attribute Inspector👇👇👇👇

ingrese la descripción de la imagen aquí

Vaibhav Saran
fuente
20

A continuación se muestra un puerto Swift del código ObjC "SAMTextView" publicado como una de las primeras respuestas a la pregunta. Lo probé en iOS 8. Modifiqué un par de cosas, incluido el desplazamiento de los límites para la colocación del texto del marcador de posición, ya que el original era demasiado alto y demasiado a la derecha (sugerencia utilizada en uno de los comentarios de esa publicación).

Sé que hay muchas soluciones simples, pero me gusta el enfoque de subclasificar UITextView porque es reutilizable y no tengo que saturar las clases que lo utilizan con los mecanismos.

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

Swift 4.2:

import UIKit

class PlaceholderTextView: UITextView {

    @IBInspectable var placeholderColor: UIColor = UIColor.lightGray
    @IBInspectable var placeholderText: String = ""

    override var font: UIFont? {
        didSet {
            setNeedsDisplay()
        }
    }

    override var contentInset: UIEdgeInsets {
        didSet {
            setNeedsDisplay()
        }
    }

    override var textAlignment: NSTextAlignment {
        didSet {
            setNeedsDisplay()
        }
    }

    override var text: String? {
        didSet {
            setNeedsDisplay()
        }
    }

    override var attributedText: NSAttributedString? {
        didSet {
            setNeedsDisplay()
        }
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setUp()
    }

    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
    }

    private func setUp() {
        NotificationCenter.default.addObserver(self,
         selector: #selector(self.textChanged(notification:)),
         name: Notification.Name("UITextViewTextDidChangeNotification"),
         object: nil)
    }

    @objc func textChanged(notification: NSNotification) {
        setNeedsDisplay()
    }

    func placeholderRectForBounds(bounds: CGRect) -> CGRect {
        var x = contentInset.left + 4.0
        var y = contentInset.top  + 9.0
        let w = frame.size.width - contentInset.left - contentInset.right - 16.0
        let h = frame.size.height - contentInset.top - contentInset.bottom - 16.0

        if let style = self.typingAttributes[NSAttributedString.Key.paragraphStyle] as? NSParagraphStyle {
            x += style.headIndent
            y += style.firstLineHeadIndent
        }
        return CGRect(x: x, y: y, width: w, height: h)
    }

    override func draw(_ rect: CGRect) {
        if text!.isEmpty && !placeholderText.isEmpty {
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.alignment = textAlignment
            let attributes: [NSAttributedString.Key: Any] = [
            NSAttributedString.Key(rawValue: NSAttributedString.Key.font.rawValue) : font!,
            NSAttributedString.Key(rawValue: NSAttributedString.Key.foregroundColor.rawValue) : placeholderColor,
            NSAttributedString.Key(rawValue: NSAttributedString.Key.paragraphStyle.rawValue)  : paragraphStyle]

            placeholderText.draw(in: placeholderRectForBounds(bounds: bounds), withAttributes: attributes)
        }
        super.draw(rect)
    }
}
luz clara
fuente
Gracias por hacer la versión rápida, ¿puedes explicar cuál es el punto de tener un método awakeFromNib que simplemente llama super?
Pierre
Establece el marcador de posición, pero no se actualiza una vez que comienza a escribir.
David
No importa, puse la llamada de notificación en wakekeFromNib y funciona ahora.
David
12

así es como lo hice:

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
bcd
fuente
12

Aquí hay una solución mucho más fácil que se comporta exactamente como el marcador de posición de UITextField pero no requiere dibujar vistas personalizadas o renunciar al primer respondedor.

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

}

(la segunda verificación en la instrucción else if es para el caso en el que no se ingresa nada y el usuario presiona la tecla de retroceso)

Simplemente configure su clase como UITextViewDelegate. En viewDidLoad debes inicializar como

- (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;
}
aning
fuente
2
La cosa es que si el usuario toca en algún lugar en medio del "texto de marcador de posición", el cursor se queda allí.
Alex Sorokoletov
10

Hola, puede usar IQTextView disponible en IQKeyboard Manager, es fácil de usar e integrar, simplemente configure la clase de su vista de texto en IQTextView y puede usar su propiedad para establecer la etiqueta de marcador de posición con el color que desee. Puede descargar la biblioteca desde IQKeyboardManager

o puedes instalarlo desde cocoapods.

Amit Shelgaonkar
fuente
IQKeyboardManager es muy útil y sin código. ¡La mejor respuesta para mí!
NSDeveloper
1
En realidad, voté y dejo comentarios por la razón. No sé IQTextView está disponible en IQKeyboardManager antes.
NSDeveloper
Tuve un problema con el delegado. UITextViewDelegate
Eliminé
respuesta sin calificación
grantespo
10

Lamento agregar otra respuesta, pero acabo de sacar algo como esto y esto creó el tipo de marcador de posición más cercano a UITextField.

Espero que esto ayude a alguien.

-(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);
    }
}
Shai Mishali
fuente
1
Tuve que cambiar el orden de los comandos en la primera instrucción if if(textView.textColor == [UIColor lightGrayColor]){ textView.textColor = [UIColor blackColor]; textView.text = [textView.text substringToIndex: 1]; De lo contrario, el primer carácter que se ingresó en la vista de texto se colocó al final del texto
Flexicoder
7

Manera simple de usar esto dentro de una línea de código:

Tome una etiqueta hasta UITextView en .nib conectando esta etiqueta a su código, después.

- (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;
}
SachinVsSachin
fuente
7

He modificado la implementación de Sam Soffes para que funcione con 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;
}

Recuerde establecer _placeholderAttribues = nilmétodos que puedan cambiar la fuente y otros signos que puedan afectarlos. También es posible que desee omitir la creación "perezosa" del diccionario de atributos si eso no lo molesta.

EDITAR:

Recuerde llamar a setNeedsDisplay en una versión anulada de setBounds si desea que el marcador de posición se vea bien después de las animaciones de distribución automática y similares.

Fabricante de clavos
fuente
Creo que debería agregar insets.left al parámetro offset x.
Karmeye
¿No es setFrame en lugar de setBounds?
JakubKnejzlik
No! Parece que setFrame no se llama durante las animaciones de diseño.
Nailer
6

También puede crear una nueva clase TextViewWithPlaceholder como una subclase de UITextView.

(Este código es un poco tosco, pero creo que está en el camino correcto).

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

}

En su delegado, agregue esto:

- (void)textViewDidChange:(UITextView *)textView
{
    if ([textView respondsToSelector: @selector(updateForTextChange)])
    {
        [textView updateForTextChange];
    }

}
Amagrammer
fuente
1
Para obtener el comportamiento exacto, debe pintar su propio marcador de posición anulando drawRect: (dibuje el marcador de posición solo si! [Self isFirstResponder] && [[self text] length] == 0), y llamando a setNeedsDisplay dentro de BecomeFirstResponder y resignFirstResponder
rpetrich
6

Hice mi propia versión de la subclase de 'UITextView'. Me gustó la idea de Sam Soffes de usar las notificaciones, pero no me gustó el drawRect: sobrescribir. Me parece excesivo. Creo que hice una implementación muy limpia.

Puedes ver mi subclase aquí . También se incluye un proyecto de demostración.

gcamp
fuente
6

Este hilo ha tenido muchas respuestas, pero aquí está la versión que prefiero.

Se extiende el vigente UITextViewclase para que sea fácilmente reutilizable, y no interceptar los eventos como textViewDidChange(lo que podría romper el código del usuario, si ya estaban interceptando estos eventos en otros lugares).

Usando mi código (que se muestra a continuación), puede agregar fácilmente un marcador de posición a cualquiera de los UITextViewssiguientes:

self.textViewComments.placeholder = @"(Enter some comments here.)";

Cuando configura este nuevo valor de marcador de posición, agrega silenciosamente un UILabelencima de él UITextView, luego lo oculta / muestra según sea necesario:

ingrese la descripción de la imagen aquí

De acuerdo, para realizar estos cambios, agregue un archivo "UITextViewHelper.h" que contenga este código:

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

... y un archivo UITextViewHelper.m que contiene esto:

//  UITextViewHelper.m
//  Created by Michael Gledhill on 13/02/15.
//
//  This UITextView category allows us to easily display a PlaceHolder string in our UITextView.
//  The downside is that, your code needs to set the "textValue" rather than the "text" value to safely set the UITextView's text.
//
#import "UITextViewHelper.h"
#import <objc/runtime.h>

@implementation UITextView (UITextViewHelper)

#define UI_PLACEHOLDER_TEXT_COLOR [UIColor colorWithRed:170.0/255.0 green:170.0/255.0 blue:170.0/255.0 alpha:1.0]

@dynamic placeholder;
@dynamic placeholderLabel;
@dynamic textValue;

-(void)setTextValue:(NSString *)textValue
{
    //  Change the text of our UITextView, and check whether we need to display the placeholder.
    self.text = textValue;
    [self checkIfNeedToDisplayPlaceholder];
}
-(NSString*)textValue
{
    return self.text;
}

-(void)checkIfNeedToDisplayPlaceholder
{
    //  If our UITextView is empty, display our Placeholder label (if we have one)
    if (self.placeholderLabel == nil)
        return;

    self.placeholderLabel.hidden = (![self.text isEqualToString:@""]);
}

-(void)onTap
{
    //  When the user taps in our UITextView, we'll see if we need to remove the placeholder text.
    [self checkIfNeedToDisplayPlaceholder];

    //  Make the onscreen keyboard appear.
    [self becomeFirstResponder];
}

-(void)keyPressed:(NSNotification*)notification
{
    //  The user has just typed a character in our UITextView (or pressed the delete key).
    //  Do we need to display our Placeholder label ?
   [self checkIfNeedToDisplayPlaceholder];
}

#pragma mark - Add a "placeHolder" string to the UITextView class

NSString const *kKeyPlaceHolder = @"kKeyPlaceHolder";
-(void)setPlaceholder:(NSString *)_placeholder
{
    //  Sets our "placeholder" text string, creates a new UILabel to contain it, and modifies our UITextView to cope with
    //  showing/hiding the UILabel when needed.
    objc_setAssociatedObject(self, &kKeyPlaceHolder, (id)_placeholder, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    self.placeholderLabel = [[UILabel alloc] initWithFrame:self.frame];
    self.placeholderLabel.numberOfLines = 1;
    self.placeholderLabel.text = _placeholder;
    self.placeholderLabel.textColor = UI_PLACEHOLDER_TEXT_COLOR;
    self.placeholderLabel.backgroundColor = [UIColor clearColor];
    self.placeholderLabel.userInteractionEnabled = true;
    self.placeholderLabel.font = self.font;
    [self addSubview:self.placeholderLabel];

    [self.placeholderLabel sizeToFit];

    //  Whenever the user taps within the UITextView, we'll give the textview the focus, and hide the placeholder if necessary.
    [self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap)]];

    //  Whenever the user types something in the UITextView, we'll see if we need to hide/show the placeholder label.
    [[NSNotificationCenter defaultCenter] addObserver:self selector: @selector(keyPressed:) name:UITextViewTextDidChangeNotification object:nil];

    [self checkIfNeedToDisplayPlaceholder];
}
-(NSString*)placeholder
{
    //  Returns our "placeholder" text string
    return objc_getAssociatedObject(self, &kKeyPlaceHolder);
}

#pragma mark - Add a "UILabel" to this UITextView class

NSString const *kKeyLabel = @"kKeyLabel";
-(void)setPlaceholderLabel:(UILabel *)placeholderLabel
{
    //  Stores our new UILabel (which contains our placeholder string)
    objc_setAssociatedObject(self, &kKeyLabel, (id)placeholderLabel, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    [[NSNotificationCenter defaultCenter] addObserver:self selector: @selector(keyPressed:) name:UITextViewTextDidChangeNotification object:nil];

    [self checkIfNeedToDisplayPlaceholder];
}
-(UILabel*)placeholderLabel
{
    //  Returns our new UILabel
    return objc_getAssociatedObject(self, &kKeyLabel);
}
@end

Sí, es mucho código, pero una vez que lo haya agregado a su proyecto e incluido el archivo .h ...

#import "UITextViewHelper.h"

... puedes usar marcadores de posición fácilmente UITextViews.

Sin embargo, hay un problema.

Si haces esto:

self.textViewComments.placeholder = @"(Enter some comments here.)";
self.textViewComments.text = @"Ooooh, hello there";

... el marcador de posición aparecerá en la parte superior del texto. Cuando establece el textvalor, no se llama a ninguna de las notificaciones regulares, por lo que no pude averiguar cómo llamar a mi función para decidir si mostrar / ocultar el marcador de posición.

La solución es establecer el en textValuelugar de text:

self.textViewComments.placeholder = @"(Enter some comments here.)";
self.textViewComments.textValue = @"Ooooh, hello there";

Alternativamente, puede establecer el textvalor y luego llamar checkIfNeedToDisplayPlaceholder.

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

Me gustan soluciones como esta, ya que "llenan el vacío" entre lo que Apple nos proporciona y lo que nosotros (como desarrolladores) realmente necesitamos en nuestras aplicaciones. Escribe este código una vez, lo agrega a su biblioteca de archivos .m / .h "auxiliares" y, con el tiempo, el SDK comienza a ser menos frustrante.

(Escribí un asistente similar para agregar un botón "borrar" a mis UITextViews, otra cosa que existe molestamente UITextFieldpero no en UITextView...)

Mike Gledhill
fuente
Me gusta lo limpio que es esto, pero no me gusta cómo requiere un segundo UIView / UILabel (y realmente no hereda los atributos / colores / fuente fácilmente del UITextView). Bonita contribución sin embargo
mattsven
6

Primero tome una etiqueta en el archivo .h.

Aqui tomo

UILabel * lbl;

Luego, en .m debajo de viewDidLoad declararlo

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 es mi TextView.

Ahora declara

-(void)textViewDidChange:(UITextView *)textView {

 if (![textView hasText]){

    lbl.hidden = NO;

 }
 else{
    lbl.hidden = YES;
 }

}

¡Y su marcador de posición Textview está listo!

Tania S
fuente
6

Recomiendo usar el pod 'UITextView + Placeholder'

pod 'UITextView+Placeholder'

en tu código

#import "UITextView+Placeholder.h"

////    

UITextView *textView = [[UITextView alloc] init];
textView.placeholder = @"How are you?";
textView.placeholderColor = [UIColor lightGrayColor];
Felix Cruz
fuente
5
    - (void)textViewDidChange:(UITextView *)textView
{
    placeholderLabel.hidden = YES;
}

poner una etiqueta sobre la vista de texto.

vatti
fuente
O incluso mejor, puede mostrarlo nuevamente cuando no hay texto presente: lblPlaceholder.hidden =! [TextView.text isEqualToString: @ ""];
Despotovic
Me gusta esta solución limpia, sin inyección de texto en sí.
Itachi
5

No es posible crear un marcador de posición en UITextView, pero puede generar un efecto como marcador de posición con esto.

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

O puede agregar una etiqueta en la vista de texto como

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

y establecer

- (void)textViewDidEndEditing:(UITextView *)theTextView
{
     if (![textView hasText]) {
     lbl.hidden = NO;
}
}

- (void) textViewDidChange:(UITextView *)textView
{
    if(![textView hasText]) {
      lbl.hidden = NO;
}
else{
    lbl.hidden = YES;
 }  
}
PJR
fuente
5

Esto imita perfectamente el marcador de posición de UITextField, donde el texto del marcador de posición permanece hasta que realmente escriba algo.

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
        }
    }
}
AmitP
fuente
4

Aquí hay otra forma de hacerlo, una que reproduce la pequeña sangría del UITextFieldmarcador de posición:

Arrastre un UITextFieldderecho debajo del UITextViewpara que sus esquinas superiores izquierdas estén alineadas. Agregue su texto de marcador de posición al campo de texto.

En viewDidLoad, agregue:

[tView setDelegate:self];
tView.contentInset = UIEdgeInsetsMake(-8,-8,0,0);
tView.backgroundColor = [UIColor clearColor];

Luego añade:

- (void)textViewDidChange:(UITextView *)textView {
    if (textView.text.length == 0) {
        textView.backgroundColor = [UIColor clearColor];            
    } else {
        textView.backgroundColor = [UIColor whiteColor];
    }
}
Chris
fuente
4

Hagámoslo fácil

Cree un UILabel y colóquelo en su vista de texto (asigne el texto como marcador de posición, coloque gris, puede hacer todo esto en su xib) Ahora en su archivo de encabezado, declare el UILabel y también la vista de texto. Delegue ahora puede simplemente ocultar la etiqueta cuando haces clic en la vista de texto

código completo a continuación

encabezamiento

@interface ViewController :UIViewController<UITextViewDelegate>{
 }
   @property (nonatomic,strong) IBOutlet UILabel *PlceHolder_label;
   @property (nonatomic,strong) IBOutlet UITextView *TextView;

@end

implementación

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

@final

No olvides conectar el textView y UILabel al propietario del archivo desde xib

Nithin M Keloth
fuente
4

Eche un vistazo a UTPlaceholderTextView .

Esta es una conveniente subclase de UITextView que admite un marcador de posición similar al de UITextField. Principales peculiaridades:

  • No usa subvistas
  • No anula drawRect:
  • El marcador de posición podría tener una longitud arbitraria y representarse de la misma manera que el texto habitual
Granfalloner
fuente
4

Leí todo esto, pero se me ocurrió una solución muy breve, Swift 3, que ha funcionado en todas mis pruebas. Podría soportar un poco más de generalidad, pero el proceso es simple. Aquí está todo lo que llamo "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()
    }
}
P. Ent
fuente
Algunas preocupaciones aquí. No deberías llamar layoutSubviews()directamente. Y no eliminó el observador de NotificationCenter.
Hlung
4

He escrito una clase en Swift. Puede importar esta clase cuando sea necesario.

import UIKit

clase pública 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)
}

}

ingrese la descripción de la imagen aquí

Sujatha Girijala
fuente
1
Esta es en realidad una de las soluciones más limpias que pude encontrar y terminé usando esto ... Especialmente teniendo en cuenta las inserciones y usando restricciones para la etiqueta es un buen toque, no he visto muchas (¿alguna?) Otras soluciones tomando cuidado de cambiar límites, fuentes, RTL, etc.
Marque el