使用自动布局实现UILabel宽度的动画效果

3

我正在尝试对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

如您在视频中所见,该按钮可以正常工作,但标签无法正常工作。
我尝试了调查并从MyLabel类创建了子类。看起来,在动画块中的[self.label layoutIfNeeded];导致- (void)drawTextInRect:(CGRect)rect立即被调用。为什么会这样?我希望框架能够根据设置在动画块内的方式进行动画变化,就像UIButton那样。但对于UILabel却没有按预期工作。
为什么UILabel不能执行动画缩放?有没有方法可以修复它?
编辑: 我已经尝试了目前答案中提出的方法,但它们也不起作用。我认为动画代码不是问题,因为它在我的示例中适用于按钮。问题似乎与UILabel特定有关。UILabel似乎处理大小更改的动画方式与其他UIView子类不同,但我无法理解原因。

你的代码显示出篡改的证据:它设置了 self.label3.text 但是 ViewController 没有 label3 属性。当你不展示真实的代码时,很难帮助你。无论如何,我怀疑在 storyboard 中设置标签的方式存在问题。 - rob mayoff
3个回答

3

我想发表评论,但是我的声望还不够。这是我会做的:

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

[UIView animateWithDuration:0.5 animations:^{

    [self.view layoutIfNeeded];
}];

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

这个方法每次都适用于我。希望这可以帮助您。


3
这也是我的最初反应,但在我的测试中出现了奇怪的行为:当扩展标签时它能完美地工作,但当再次缩小时却会跳动...奇怪。 - jcaron
@Bogdan Balta:我明白你的意思,你建议使用标准方法来动画化约束,我也知道这一点。然而,在我的代码中并没有问题,因为按钮的动画效果是正常的。问题似乎是特定于UILabel的。我会更改我的代码,因为它显然会让人感到困惑。如果您能够让您的动画代码与UILabel一起正常工作,请告诉我。 - Anastasia
@Bogdan Balta:您似乎是正确的,这确实是调用[self.view layoutIfNeeded];的问题。 - Anastasia

2

在执行动画之前,您必须设置约束的常量。

尝试这样做:

- (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;
        }];
    }
}

不,我明白你的意思,你建议使用标准的方法来动画化约束,我也知道这一点。但是在我的代码中并没有问题,因为按钮会按预期进行动画。问题似乎是UILabel特定的。我将更改我的代码,因为它显然会让人们感到困惑。如果您能够让您的动画代码与UILabel一起正常工作,请告诉我。 - Anastasia

2

使其完全按预期工作有几个要点:

  • 首先调用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;
        }];
    }
}

不,我明白你的意思,你建议使用标准的方式来实现约束动画,我也知道这一点。然而,在我的代码中并没有问题,因为按钮动画效果如预期。问题似乎是特定于 UILabel 的。我将更改我的代码,因为它显然会让人感到困惑。如果您能让您的动画代码与 UILabel 一起正常工作,请告诉我。 - Anastasia
太好了!我刚刚连线了一个演示来进行验证。 :) - Ben

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接