6

在我的 iPhone 应用程序中,我有一个多行标签,我想使用“更多”按钮来扩展/收缩。像这样:

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

                                "<More>"

应该动画成这样:

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

我试图获得一种效果,即随着标签的增长,每条线都单独显示,然后随着标签的缩小单独隐藏。生长效果很好,但在收缩动画期间它会跳到 3 行。有任何想法吗?下面的代码和属性:


成长动画:

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

收缩动画:

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

UILabel 属性:

  • 行数:0
  • 换行符:截断尾部
  • 内容模式:顶部
4

4 回答 4

3

您可以通过使用自动布局约束并将 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() }
}

您需要告诉包含标签和按钮的视图,它需要在动画块中进行布局。

在此处输入图像描述

于 2015-12-15T12:20:21.887 回答
1

动画 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'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*/;

动画标签.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

动画标签.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
于 2012-06-20T14:55:18.117 回答
1

我在尝试使视图增大/缩小时遇到了类似的问题。这是我的SO帖子。基本上我必须动画框架来增长,然后边界/中心缩小。我知道有点尴尬,但我得到了我想要的效果。

于 2012-06-20T15:11:38.643 回答
0

问题不在您发布的代码中。需要更多信息来解决此问题。我建议您检查以下内容:

  1. 您正在调整大小的视图对象是正确的。
  2. 为您正在调整大小的视图对象添加彩色背景,以便能够实时查看其大小如何变化。

如果这没有帮助,请发布执行您发布的这些动画片段的完整方法。

于 2012-05-27T03:55:48.707 回答