*只有一个*动画块(UILabel)使用自动布局的问题

Posted

技术标签:

【中文标题】*只有一个*动画块(UILabel)使用自动布局的问题【英文标题】:Issue With *Only One* Animation Block (UILabel) Using AutoLayout 【发布时间】:2014-10-03 15:27:49 【问题描述】:

我正在使用 AutoLayout 并且正在为各种约束更改设置动画,没有任何问题。但是,VC 中有一个 UILabel 根本不会设置动画。我想要的动画是类似擦拭的效果。我已经设置了一个计时器,根据 rdelmar 的建议,因为 UILabel 不支持动画约束,并且正在获得我想要的效果,即只有一行长的标签。但是,多行标签不会产生类似擦拭的效果,而是字符会环绕并且随着标签宽度的增加。下面是我如何设置标签的约束。

- (void)initializeLabelWithMessage:(NSDictionary *)message 
    meaningLabel.text = [messagingSubMethods meaningStringForLabelFromMessage:message];        
    meaningLabel.numberOfLines = _expectedNumberOfLines;

    float meaningLabelHeight;
    if (_expectedNumberOfLines == 2) 
        meaningLabelHeight = 32;
     else if (_expectedNumberOfLines == 3) 
        meaningLabelHeight = 48;
     else 
        meaningLabelHeight = 18;
    

    _meaningWidthConstraint.constant = 0;
    _meaningHeightConstraint.constant = meaningLabelHeight;
    [self.view layoutIfNeeded];

以下是我对标签宽度进行“动画化”的方法,只有在 numberOfLines 为 1 时才能获得我想要的效果。

- (void)startMeaningAnimationTimer 
    _meaningAnimationTimer = [NSTimer timerWithTimeInterval:0.05 target:self selector:@selector(increaseMeaningLabelWidth) userInfo:nil repeats:YES];
    [[NSRunLoop mainRunLoop] addTimer:_meaningAnimationTimer forMode:NSDefaultRunLoopMode];
    [_meaningAnimationTimer fire];


- (void)increaseMeaningLabelWidth 
    if (_meaningWidthConstraint.constant < _expectedMeaningWidth) 
        _meaningWidthConstraint.constant = _meaningWidthConstraint.constant + 1;
        [self.view layoutIfNeeded];
     else
        [self stopMeaningAnimationTimer];

下面的代码是我根据 Rob 的建议添加的。第一个是我最初尝试将他的代码与我的代码集成。第二次尝试是看看我是否可以“复制并粘贴”他的代码。

- (void)attempt1 
    _meaning1WidthConstraint.constant = 0;
    [self.view layoutIfNeeded];
    [UIView animateWithDuration:0.5 animations:^
        _meaningWidthConstraint.constant = expectedWidth;
        [self.view layoutIfNeeded];
     completion:^(BOOL finished) 
        _meaning1WidthConstraint.constant = expectedWidth;
        [self.view layoutIfNeeded];
    ];


- (void)attempt2 
    _meaning1WidthConstraint.constant = [meaningLabel1 sizeThatFits:CGSizeMake(expectedWidth, CGFLOAT_MAX)].width;
    [meaningLabel1 layoutIfNeeded];

    // Now animate the container.
    _meaning1WidthConstraint.constant = _meaning1WidthConstraint.constant;
    [UIView animateWithDuration:2 animations:^
        [meaningLabel1.superview layoutIfNeeded];
    ];

【问题讨论】:

其他标签是否正常工作?如果我没记错的话,你不能以这种方式为标签设置动画。您需要改用计时器。 不,我制作动画的其他元素不是标签。我按照你的建议尝试了计时器,它看起来真的很时髦!随着宽度的增加,标签会重新调整字符。现在我试图找出一种方法让它像静态图像而不是调整大小的标签一样显示。通过为 UILabel 框架设置动画,我确实得到了我在之前的项目中想要的效果。不确定是否有办法使用 AutoLayout 轻松获得相同的效果。 不确定您所说的“重新调整字符”是什么意思。当我这样做时,我看不到这一点。你想达到什么效果?你想改变文字大小吗? 谢谢,对不起,应该更具体。标签只有一行的时候,正是我想要的效果;标签在显示其内容时具有类似擦拭的动画。但是,当它超过一行时,字符会通过换行符不断地重新组织自己。我可以创建一个新标签,但最好保留一个带有换行符的标签,其中字符在“动画”期间不会重新排序。 编辑您的问题,以包含有关如何设置标签及其约束的详细信息。如果是代码,请发布代码。否则发布标签的属性和大小检查器的屏幕截图。 【参考方案1】:

我不认为使用NSTimer 制作动画是一个好方法。

我猜你想要这样的东西:

我将标签放在容器视图中,并为容器视图的框架设置动画(通过约束)。这是我的场景大纲:

所以,我对标签有宽度和高度限制,我对容器视图(标签的超级视图)也有宽度和高度限制。这些宽度和高度约束的大小并不重要,因为我将在代码中设置它们。我对将标签固定在容器的顶部和左侧边缘有限制。我对将容器置于其父级(场景的根视图)中的容器有限制。

我检查了容器的“剪辑子视图”(在属性检查器中),并将标签的视图模式设置为“左上角”(默认为“左”)。

在我的视图控制器中,我有标签的出口,标签的宽度和高度约束,以及容器的宽度和高度约束:

@interface ViewController ()
@property (strong, nonatomic) IBOutlet UILabel *label;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *labelContainerWidthConstraint;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *labelContainerHeightConstraint;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *labelWidthConstraint;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *labelHeightConstraint;
@end

当视图加载时,我需要为小尺寸设置约束,所以我假装小按钮被点击,但我禁用了动画:

- (void)viewDidLoad 
    [super viewDidLoad];
    [UIView performWithoutAnimation:^
        [self smallButtonWasTapped:self];
    ];

当点击“小”按钮时,我想为容器的框架设置动画。我有一个硬编码的宽度,但我想通过询问标签如果它是一条线高它会有多高,以编程方式计算出高度。但是,我真的不希望它是一行高,所以我暂时更改了它的numberOfLines

- (IBAction)smallButtonWasTapped:(id)sender 
    self.labelContainerWidthConstraint.constant = 150;
    self.label.numberOfLines = 1;
    self.labelContainerHeightConstraint.constant = [self.label sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)].height;
    self.label.numberOfLines = 0;

然后我在一个动画块内执行布局,这样布局变化就会被动画化:

    [UIView animateWithDuration:2 animations:^
        [self.label.superview layoutIfNeeded];
    

请注意,我没有修改标签的约束,因此它将保持相同的框架和内容。它不会重排,这似乎是您的主要抱怨。

动画完成后,我想显示一个省略号来表示不再可见的内容,所以在完成块中,我更改标签的lineBreakMode 和约束:

      completion:^(BOOL finished) 
        if (finished) 
            self.label.lineBreakMode = NSLineBreakByTruncatingTail;
            self.labelWidthConstraint.constant = self.labelContainerWidthConstraint.constant;
            self.labelHeightConstraint.constant = self.labelContainerHeightConstraint.constant;
        
    ];

但我没有为布局设置动画,因此标签会立即更改为显示省略号。

当点击“大”按钮时,我想立即去掉省略号并使标签全尺寸,这样用户就不会看到丢失或重排的文本:

- (IBAction)bigButtonWasTapped:(id)sender 
    // First, get rid of the ellipsis and resize the label without animation.
    self.label.lineBreakMode = NSLineBreakByWordWrapping;
    self.label.numberOfLines = 0;
    self.labelWidthConstraint.constant = 300;
    self.labelHeightConstraint.constant = [self.label sizeThatFits:CGSizeMake(self.labelWidthConstraint.constant, CGFLOAT_MAX)].height;
    [self.label layoutIfNeeded];

然后我可以设置容器的约束以匹配标签,并为布局设置动画:

    // Now animate the container.
    self.labelContainerWidthConstraint.constant = self.labelWidthConstraint.constant;
    self.labelContainerHeightConstraint.constant = self.labelHeightConstraint.constant;
    [UIView animateWithDuration:2 animations:^
        [self.label.superview layoutIfNeeded];
    ];

我已经上传了我的项目to this github repository。

【讨论】:

哇,你是一个非常善良的人!我敢打赌,你以这种方式直接影响了许多程序员的生活。我们已经回到上面(抱歉延迟了 2 周),是的,计时器看起来确实有点滑稽。它也有点错误/不稳定。我照你说的做了,并将标签作为容器放在 UIView 中。我尝试以几种不同的方式操作代码,但标签仍然不会“动画”。相反,我能做到的最接近的是 UIView 动画正确,但标签是全尺寸的并且超出了其容器的宽度。 也刚刚插入了我尝试使用上面的回复的代码。

以上是关于*只有一个*动画块(UILabel)使用自动布局的问题的主要内容,如果未能解决你的问题,请参考以下文章

如何使用自动布局约束为移动 UILabel 设置动画?

自动布局动画保持 UILabel 居中

如何使用自动布局移动标签并为更改设置动画?

如何在 iOS 中根据 dp(不是点数)设置 UILabel 字体大小,就像只有自动布局的 android

使用自动布局动画 UIView 动画 UITableView 复选标记

UIView animateWithDuration 动画块之外的动画自动布局视图