使用自动布局动画化 UILabel 宽度

Animate UILabel width with auto layout

我正在尝试为 UILabel 的宽度设置动画。由于某种原因,动画不起作用并且标签会立即更改其大小。我使用自动布局。这是我为重现此代码而编写的示例项目中的代码。点击按钮更改按钮和标签的尾随约束。

@interface ViewController ()

@property (assign, nonatomic) BOOL controlsResized;
@property (weak, nonatomic) IBOutlet UIButton *button;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *buttonTrailingConstraint;
@property (weak, nonatomic) IBOutlet MyLabel *label;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *labelTrailingConstraint;
- (IBAction)doTapButton:(id)sender;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self.button setTranslatesAutoresizingMaskIntoConstraints:NO];
    [self.label setTranslatesAutoresizingMaskIntoConstraints:NO];
    self.label3.text = @"asdfasdfds sklfjfjls sdlkfj jjjjkfjkfdsjkdfsjklffsjkfdsjkl";
}    


- (IBAction)doTapButton:(id)sender
{
    if (!self.controlsResized)
    {
        [UIView animateWithDuration:0.5 animations:^{
            self.buttonTrailingConstraint.constant = 0;
            [self.button layoutIfNeeded];
            self.labelTrailingConstraint.constant = 0;
            [self.label layoutIfNeeded];

            self.controlsResized = YES;
        }];
    }
    else
    {
        [UIView animateWithDuration:0.5 animations:^{
            self.buttonTrailingConstraint.constant = 100;
            [self.button layoutIfNeeded];
            self.labelTrailingConstraint.constant = 100;
            [self.label layoutIfNeeded];

            self.controlsResized = NO;
        }];
    }
}

@implementation MyLabel

- (void)drawTextInRect:(CGRect)rect
{
    NSLog(@"drawTextInRect");
    [super drawTextInRect:rect];
}

@end

如您在 video 中所见,它适用于按钮,但不适用于标签。

我试图调查 class 并从 MyLabel class 编辑标签。看起来 - (void)drawTextInRect:(CGRect)rect 在点击按钮时立即被调用,这是动画块中 [self.label layoutIfNeeded]; 的原因。为什么会这样?我希望框架可以更改为动画,因为它是在动画块内设置的。它按 UIButton 的预期工作。

为什么 UILabel 不调整动画大小?有办法解决吗?

编辑: 我尝试了当前答案中建议的方法,但它们也不起作用。我认为动画代码在这里不是问题,因为它适用于我示例中的按钮。该问题似乎与特定于 UILabel 的问题有关。 UILabel 处理尺寸变化动画的方式似乎与其他 UIView 子classes 不同,但我不明白为什么。

我会 post 发表评论,但我没有必要的声誉。这是我会做的:

self.buttonTrailingConstraint.constant = x;
self.labelTrailingConstraint.constant = x;

[UIView animateWithDuration:0.5 animations:^{

    [self.view layoutIfNeeded];
}];

当您更新约束时,您需要考虑到您正在修改的相应视图的约束不是唯一需要更新的约束,因此最好在父视图上调用布局方法。

这每次都对我有用。希望这有帮助。

在动画之前必须设置约束常量。

试试这个:

- (IBAction)doTapButton:(id)sender
{
    if (!self.controlsResized)
    {
        self.buttonTrailingConstraint.constant = 0;
        self.labelTrailingConstraint.constant = 0;

        [UIView animateWithDuration:0.5 animations:^{
            [self.view layoutIfNeeded];
        } completion:^(BOOL finished) {
            self.controlsResized = YES;
        }];
    }
    else
    {
        self.buttonTrailingConstraint.constant = 100;
        self.labelTrailingConstraint.constant = 100;

        [UIView animateWithDuration:0.5 animations:^{
            [self.view layoutIfNeeded];
        } completion:^(BOOL finished) {
            self.controlsResized = NO;
        }];
    }
}

要使此 100% 按预期工作,需要几个部分:

  • 先调用layoutIfNeeded
  • 在动画块外设置约束常量
  • 在块内调用 layoutIfNeeded

从工作实施中复制:

[self.view layoutIfNeeded];
self.topContainerConstraint.constant = constraintValue;
[UIView animateWithDuration:0.25 animations:^{
    [self.view layoutIfNeeded];
} completion:^(BOOL finished){ }];

我会将您的函数更改为如下所示(显然未经测试):

- (IBAction)doTapButton:(id)sender
{
    if (!self.controlsResized)
    {
        [self.view layoutIfNeeded];
        self.labelTrailingConstraint.constant = 0;
        self.buttonTrailingConstraint.constant = 0;
        [UIView animateWithDuration:0.5 animations:^{
            [self.view layoutIfNeeded];
            self.controlsResized = YES;
        }];
    }
    else
    {
        [self.view layoutIfNeeded];
        self.labelTrailingConstraint.constant = 100;
        self.buttonTrailingConstraint.constant = 100;
        [UIView animateWithDuration:0.5 animations:^{
            [self.view layoutIfNeeded];
            self.controlsResized = NO;
        }];
    }
}