2012-05-25 9 views
5

En mi aplicación para iPhone, tengo una etiqueta multilínea que me gustaría expandir/contratar con el botón "Más". De esta manera:Expansión Multiline UILabel

Lorem ipsum dolor sit amet, consectetur 
adipiscing elit. Donec fringilla, turpis 
in porttitor imperdiet, eros turpis... 

           "<More>" 

debe animar a este:

Lorem ipsum dolor sit amet, consectetur 
adipiscing elit. Donec fringilla, turpis 
in porttitor imperdiet, eros turpis laoreet 
magna, id tempor ante lorem pulvinar lacus. 
Duis vitae nisl quis sapien dictum pellentesque. 

           "<Less>" 

que estoy tratando de conseguir un efecto en el que cada línea se revela de forma individual como la etiqueta crece, y luego escondido de forma individual, ya que se reduce. Growing funciona muy bien, pero salta a 3 líneas durante la animación de contracción. ¿Algunas ideas? Código y las propiedades siguientes:


Grow animación:

[UIView animateWithDuration:0.5 animations:^{ 
     view.frame = CGRectMake(startFrame.origin.x, startFrame.origin.y, startFrame.size.width, startFrame.size.height + 40.0); 
    }]; 

Shrink animación:

[UIView animateWithDuration:0.5 animations:^{ 
     view.frame = CGRectMake(startFrame.origin.x, startFrame.origin.y, startFrame.size.width, startFrame.size.height - 40.0); 
    }]; 

propiedades UILabel:

  • Líneas: 0
  • saltos de línea: Truncar cola
  • Modo Contenido: Top
+0

Sé que esto puede parecer un poco inútil, pero lo que sucede si se elimina el fragmento de código para la función de contracción, y lo escribe. No lo comentes, elimínalo y vuelve a escribirlo desde cero. He visto rarezas como esta y esto ha resuelto un problema al menos un par de veces en mi carrera. Aparte de eso, todo me parece correcto. – RLH

Respuesta

1

que tenía un problema similar tratando de hacer crecer una vista/encogerse. Aquí está mi post SO. Básicamente tuve que animar el marco para crecer y luego los límites/centro para reducir. Un poco incómodo lo sé, pero obtuve el efecto que quería.

+1

Esta estrategia (cambiar los límites de UILabel para reducir) ya no parece funcionar en iOS 6 ... ¿Alguien más ha encontrado esto/ha encontrado una solución? – cowfaboo

0

El problema no está en el código que envió. Se necesita más información para resolver este problema. Le recomiendo que compruebe lo siguiente:

  1. Que el objeto de vista que está cambiando de tamaño es el correcto.
  2. Agregue un fondo de color al objeto de vista que está cambiando de tamaño para poder ver cómo cambia su tamaño en tiempo real.

Si eso no ayuda, publique los métodos completos que están ejecutando estos fragmentos de animación que ha publicado.

1

Animando un UILabel tuvo un resultado similar al descrito, solo la animación disminuyó y el creciente saltó al tamaño completo.

Experimenté también con un UITextView, pero tampoco funcionó.

Mi solución fue tomar otra UIView, llamada coverView, y colocarla debajo de UILabel. Cuando UILabel se expande, se redimensiona sin animarlo y CoverView oculta la parte expandida, luego el coverView's origin.y y size.height se modifican en una animación para revelar el UILabel expandido. Cuando se contrae el UILabel, el coverView's origin.y y size.height se modifican en una animación para ocultar gradualmente el UILabel. En el bloque de finalización de la animación, el marco de UILabel se redimensiona a las dimensiones contratadas.

He creado una UIView personalizada para esto: AnimatedLabel. Puede encontrar el código completo a continuación. Lo escribí rápidamente y no lo comento, pero no debería ser difícil de entender.

Cómo se usa (trabaja con IB, también):

// Init 
AnimatedLabel *animLabel = [[AnimatedLabel alloc] initWithFrame:/*desired CGRect*/]; 

// Set the surroundingBackgroundColor, this color will be used for the coverView 
// you should set it to the container view's backgroundColor 
// IMPORTANT: transparency is not supported! (if its transparent you'll see the UILabel) 
animLabel.surroundingBackgroundColor = self.view.backgroundColor; 

// Set the text 
animLabel.label.text = /*text text text ...*/; 

// Call sizeToFit to modify the label's size to fit the text perfectly 
// so that the text is not moved after the label is expanded (UILabel always 
// centers the text vertically and if the label 
// is not resized you will see that the text is repositioned after the animation) 
[animLabel sizeToFit]; 

// Open the label 
[animLabel open]; 

// Close the label 
[animLabel close]; 

// You can resize the animLabel and the subviews will be correctly resized 
animLabel.frame = /*CGRect*/; 

AnimatedLabel.h

// 
// AnimatedLabel.h 
// labeltest 
// 
// Created by Alpar Szotyori on 20/06/2012. 
// Use it, modify it, improve it, share it! 
// 

#import <UIKit/UIKit.h> 

@interface AnimatedLabel : UIView 

@property (nonatomic, strong) UILabel *label; 
@property (nonatomic, strong) UIColor *surroundingBackgroundColor; 
@property (nonatomic) BOOL isOpen; 

// Public methods 

- (void)open; 
- (void)close; 

@end 

AnimatedLabel.m:

// 
// AnimatedLabel.m 
// labeltest 
// 
// Created by Alpar Szotyori on 20/06/2012. 
// Use it, modify it, improve it, share it! 
// 

#import "AnimatedLabel.h" 

@interface NSString (visibleText) 

- (NSString*)stringVisibleInRect:(CGRect)rect withFont:(UIFont*)font constrainedToSize:(CGSize)size; 

@end 

@implementation NSString (visibleText) 

- (NSString*)stringVisibleInRect:(CGRect)rect withFont:(UIFont*)font constrainedToSize:(CGSize)size 
{ 
    BOOL addEllipse = NO; 
    NSString *visibleString = @""; 
    for (int i = 2; i <= self.length; i++) 
    { 
     NSString *testString = [self substringToIndex:i]; 
     CGSize stringSize = [testString sizeWithFont:font constrainedToSize:size]; 
     if (stringSize.height > rect.size.height || stringSize.width > rect.size.width) { 
      addEllipse = YES; 
      break; 
     } 

     visibleString = testString; 
    } 

    if (addEllipse) { 
     if (visibleString.length >= 3) { 
      visibleString = [[visibleString substringToIndex:visibleString.length - 3] stringByAppendingString:@"…"]; 
     } 
    } 

    return visibleString; 
} 

@end 


@interface AnimatedLabel() 

@property (nonatomic, strong) UIView *coverView; 
@property (nonatomic, strong) UIColor *backgrndColor; 
@property (nonatomic) CGFloat origLabelHeight; 
@property (nonatomic, strong) NSString *origLabelText; 
@property (nonatomic) BOOL animating; 
@property (nonatomic) BOOL labelKVOSet; 

- (void)changeHeight:(CGFloat)height animated:(BOOL)animated; 
- (void)addKVOToLabel; 
- (void)removeKVOFromLabel; 

@end 


@implementation AnimatedLabel 

#pragma mark - Properties 

@synthesize label; 
@synthesize surroundingBackgroundColor = i_surroundingBackgroundColor; 
@synthesize isOpen; 

- (void)setSurroundingBackgroundColor:(UIColor *)surroundingBackgroundColor { 
    i_surroundingBackgroundColor = surroundingBackgroundColor; 
    self.coverView.backgroundColor = i_surroundingBackgroundColor; 
} 

#pragma mark - Initialization 

- (id)initWithCoder:(NSCoder *)aDecoder { 
    self = [super initWithCoder:aDecoder]; 
    if (self) { 
     self.label = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, self.frame.size.width, self.frame.size.height)]; 
     self.label.numberOfLines = 0; 
     self.label.backgroundColor = self.backgrndColor; 
     self.origLabelHeight = self.label.frame.size.height; 
     [self addKVOToLabel]; 

     self.coverView = [[UIView alloc] initWithFrame:CGRectZero]; 
     self.coverView.backgroundColor = [UIColor whiteColor]; 

     [self addSubview:self.label]; 
     [self addSubview:self.coverView]; 

     self.animating = NO; 
     self.isOpen = NO; 
    } 
    return self; 
} 

- (id)initWithFrame:(CGRect)frame 
{ 
    self = [super initWithFrame:frame]; 
    if (self) { 
     self.label = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, frame.size.width, frame.size.height)]; 
     self.label.numberOfLines = 0; 
     self.label.backgroundColor = self.backgrndColor; 
     self.origLabelHeight = self.label.frame.size.height; 
     [self addKVOToLabel]; 

     self.coverView = [[UIView alloc] initWithFrame:CGRectZero]; 
     self.coverView.backgroundColor = [UIColor whiteColor]; 

     [self addSubview:self.label]; 
     [self addSubview:self.coverView]; 

     self.animating = NO; 
     self.isOpen = NO; 
    } 
    return self; 
} 

#pragma mark - Overriden methods 

- (void)setFrame:(CGRect)frame { 
    if (!self.animating && self.isOpen) { 
     self.isOpen = NO; 
     [self changeHeight:self.origLabelHeight animated:NO]; 
     frame.size.height = self.origLabelHeight; 
    } 

    [super setFrame:frame]; 

    if (!self.animating) {   
     self.label.frame= CGRectMake(0.0, 0.0, frame.size.width, frame.size.height); 
     self.origLabelHeight = self.label.frame.size.height; 

     [self removeKVOFromLabel]; 

     NSString *visibleText = [self.origLabelText stringVisibleInRect:self.label.frame withFont:self.label.font constrainedToSize:CGSizeMake(frame.size.width, LONG_MAX)]; 
     self.label.text = visibleText; 
     [self.label sizeToFit]; 

     [self addKVOToLabel]; 
    } 
} 

- (void)setBackgroundColor:(UIColor *)backgroundColor { 
    [super setBackgroundColor:[UIColor clearColor]]; 

    self.backgrndColor = backgroundColor; 

    self.label.backgroundColor = self.backgrndColor; 
} 

- (UIColor *)backgroundColor { 
    return self.backgrndColor; 
} 

- (void)sizeToFit { 
    [self removeKVOFromLabel]; 

    NSString *visibleText = [self.origLabelText stringVisibleInRect:self.label.frame withFont:self.label.font constrainedToSize:CGSizeMake(self.frame.size.width, LONG_MAX)]; 
    self.label.text = visibleText; 
    [self.label sizeToFit]; 
    self.origLabelHeight = self.label.frame.size.height; 

    [self addKVOToLabel]; 

    self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.label.frame.size.width, self.label.frame.size.height); 
} 

#pragma mark - KVO methods 

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 
    if ([keyPath isEqualToString:@"text"]) { 
     self.origLabelText = [change objectForKey:NSKeyValueChangeNewKey]; 
    } 
} 

#pragma mark - Public methods 

- (void)open { 
    self.origLabelHeight = self.label.frame.size.height; 
    [self removeKVOFromLabel]; 
    self.label.text = self.origLabelText; 
    [self addKVOToLabel]; 
    [self.label sizeToFit]; 

    if (self.origLabelHeight == self.label.frame.size.height) { 
     return; 
    } 

    [self changeHeight:self.label.frame.size.height animated:YES]; 
    self.isOpen = YES; 
} 

- (void)close { 
    if (self.frame.size.height == self.origLabelHeight) { 
     return; 
    } 

    [self changeHeight:self.origLabelHeight animated:YES]; 
    self.isOpen = NO; 
} 

#pragma mark - Private methods 

#pragma mark - Properties 

@synthesize coverView, backgrndColor, origLabelHeight, origLabelText, animating, labelKVOSet; 

#pragma mark - Methods 

- (void)changeHeight:(CGFloat)height animated:(BOOL)animated { 
    if (self.frame.size.height == height) { 
     return; 
    } 

    if (height > self.frame.size.height) { 
     self.animating = YES; 

     [self.label sizeToFit]; 
     height = self.label.frame.size.height; 

     self.coverView.frame = CGRectMake(0.0, self.frame.size.height, self.frame.size.width, height - self.frame.size.height); 
     self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, height); 
     self.label.frame = CGRectMake(0.0, 0.0, self.frame.size.width, self.frame.size.height); 

     if (animated) { 
      [UIView animateWithDuration:0.5 animations:^(){ 
       self.coverView.frame = CGRectMake(0.0, self.frame.size.height, self.frame.size.width, 0.0); 
      } completion:^(BOOL completed){ 
       self.animating = NO; 
      }]; 
     } else { 
      self.coverView.frame = CGRectMake(0.0, self.frame.size.height, self.frame.size.width, 0.0); 
      self.animating = NO; 
     } 
    } else { 
     self.animating = YES; 

     if (animated) { 
      [UIView animateWithDuration:0.5 animations:^(){ 
       self.coverView.frame = CGRectMake(0.0, height, self.frame.size.width, self.frame.size.height - height); 
      } completion:^(BOOL completed){ 
       self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, height); 
       self.coverView.frame = CGRectMake(0.0, height, self.frame.size.width, 0.0); 
       self.label.frame = CGRectMake(0.0, 0.0, self.frame.size.width, height); 
       self.animating = NO; 
      }]; 
     } else { 
      self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, height); 
      self.coverView.frame = CGRectMake(0.0, height, self.frame.size.width, 0.0); 
      self.label.frame = CGRectMake(0.0, 0.0, self.frame.size.width, height); 
      self.animating = NO;    
     } 
    } 
} 

- (void)addKVOToLabel { 
    if (self.label == nil) { 
     return; 
    } 

    if (!self.labelKVOSet) { 
     self.labelKVOSet = YES; 
     [self.label addObserver:self forKeyPath:@"text" options:NSKeyValueObservingOptionNew context:nil]; 
    } 
} 

- (void)removeKVOFromLabel { 
    if (self.label == nil) { 
     return; 
    } 

    if (self.labelKVOSet) { 
     self.labelKVOSet = NO; 
     [self.label removeObserver:self forKeyPath:@"text"]; 
    } 
} 

@end 
2

Puede animar esto mediante el uso de restricciones de autodiseño y modificando el número de Líneas desde 3 a 0. (0 es un valor especial que significa mostrar cualquier número de líneas).

La etiqueta tiene un tamaño intrínseco que cambiará cuando modifiquen numberOfLines y esto afectará a las restricciones. El código se ve así:

@IBAction func buttonTapped(sender: UIButton) { 
    let numberOfLines = label.numberOfLines == 0 ? 3 : 0 
    label.numberOfLines = numberOfLines 
    let newTitle = numberOfLines == 0 ? "Less" : "More"  
    sender.setTitle(newTitle, forState: .Normal) 
    UIView.animateWithDuration(0.5) { self.view.layoutIfNeeded() } 
} 

Debe indicar a la vista que contiene la etiqueta y el botón que necesita para el diseño en el bloque de animación.

enter image description here