UILabel multi-line extension

In my iPhone app, I have a multi-line shortcut that I would like to expand / arrange using the More button. Like this:

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

                                "<More>"

Animate this:

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

I try to get an effect where each line appears individually as the label grows, and then individually hides as it compresses. Growing works fine, but it jumps to 3 lines during the compression animation. Any ideas? Code and properties below:


Enlarge Animation:

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

Compression animation:

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

UILabel Properties:

  • Lines: 0
  • Line breaks: truncated tail
  • Content Mode: Up
+5
source share
4 answers
+1

numberOfLines 3 0. (0 - , ).

, numberOfLines, . :

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

, , .

enter image description here

+2

UILabel , , , - .

UITextView, .

, UIView coverView UILabel. UILabel , , coverView , coverView origin.y size.height , UILabel. UILabel , coverView origin.y size.height , UILabel. UILabel .

UIView : AnimatedLabel. . , .

( IB ):

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

The problem is not with the code you posted. Additional information is needed to solve this problem. I recommend you check the following:

  • That the view object you are resizing is correct.
  • Add a colored background to the view object whose size you are resizing to see how its size changes in real time.

If this does not help, submit complete methods that execute these pieces of animation that you posted.

0
source

All Articles