UIButton状态变化动画

52
我正在使用带有正常和高亮状态图片的UIButton。它们按预期工作,但我想要一些渐变/融合转换效果而不仅仅是突然切换。
我该如何做到这一点?
7个回答

125

可以使用UIView的过渡动画来实现。即使isHighlighted属性不可动画化,也没关系,因为它会过渡整个视图。

Swift 3

UIView.transition(with: button,
                  duration: 4.0,
                  options: .transitionCrossDissolve,
                  animations: { button.isHighlighted = true },
                  completion: nil)

Objective-C

:目标-C
[UIView transitionWithView:button
                  duration:4.0
                   options:UIViewAnimationOptionTransitionCrossDissolve
                animations:^{ button.highlighted = YES; }
                completion:nil];

我认为 highlighted 属性不可动画化。 - Marty
11
@Marty highlighted属性不可动画化,因此必须使用+transitionWithView:duration:options:animations:completion:而不是+animateWithDuration:delay:options:animations:completion: - Marián Černý
如上所述,isHighlighted 在 iOS 中不是可动画化的属性。 - Jerland2
3
如您所述,“transition(with: UIView...)”方法并不关心某个属性是否可动画,因为它会转换整个视图。 - Marián Černý
2
这里的秘密在于 .transitionCrossDissolve。其他动画样式不能可靠地进行动画化。@MariánČerný- 我建议你扩展这个例子,并提出一个 where 的想法,例如在 ... isHighlighted { didSet {} } 内部执行。 - brandonscript

3

为了实现这一点,我扩展了UIButton。添加了一个名为hilightedImage的新属性,代码如下:

- (void)setHilightImage:(UIImageView *)_hilightImage
{
    if (hilightImage != _hilightImage) {
        [hilightImage release];
        hilightImage = [_hilightImage retain];
    }
    [hilightImage setAlpha:0];
    [self addSubview:hilightImage];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.14];
    if(hilightImage){
        [hilightImage setAlpha:1];
    }
    [UIView commitAnimations];
    [super touchesBegan:touches withEvent:event];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

    self.highlighted = FALSE;
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.14];
    if(hilightImage){
        [hilightImage setAlpha:0];
    }

    [UIView commitAnimations];
    [super touchesEnded:touches withEvent:event];
}

这不是子类吗,而不是扩展? - Marty
2
Marty,你可以用“扩展”这个词来表示“我创建了一个新类...”,就像例子中一样。 - Fattie

2

UIButton 继承自 UIView

因此,您可以获取其视图并调用 beginAnimations:context:

然后从那里调用所有适当的 setAnimation 方法。

UIView 类的以下属性可进行动画处理:

  • @property frame
  • @property bounds
  • @property center
  • @property transform
  • @property alpha
  • @property backgroundColor
  • @property contentStretch

参考:UIView 类参考


2

这里有一个自包含的解决方案,也支持布尔型animated标志。

- (void)setEnabled:(BOOL)enabled animated:(BOOL)animated
{
  if (_button.enabled == enabled) {
    return;
  }

  void (^transitionBlock)(void) = ^void(void) {
    _button.enabled = enabled;
  };

  if (animated) {
    [UIView transitionWithView:_button
                      duration:0.15
                       options:UIViewAnimationOptionTransitionCrossDissolve
                    animations:^{
                      transitionBlock();
                    }
                    completion:nil];
  } else {
    transitionBlock();
  }
}

2

@marián-ČernýSwift 中的回答:

Swift 2

UIView.transitionWithView(button, 
    duration: 4.0, 
    options: .TransitionCrossDissolve, 
    animations: { button.highlighted = true },
    completion: nil)

Swift 3, 4, 5

UIView.transition(with: button, 
    duration: 4.0, 
    options: .transitionCrossDissolve, 
    animations: { button.isHighlighted = true },
    completion: nil)

0

如果你在 iOS 15 之后来到这里并使用新的 UIButton.Configuration,你可能会使用 configurationUpdateHandler 来确定和设置配置。在这种情况下,你可以使用 UIView.transition(with: button API 设置配置(可能是函数中最后要做的事情),像这样:

UIView.transition(with: button,
                  duration: 0.2,
                  options: [.transitionCrossDissolve,
                            .beginFromCurrentState,
                            .allowUserInteraction,
                            .curveEaseInOut],
                  animations: { button.configuration = configuration })

0

我使用了CustomButton代替UIButton并重写了isHighlighted属性,这对我很有效;

class KeypadButton: UIButton {
    var highlightDuration: TimeInterval = 0.25
    
    override var isHighlighted: Bool {
        didSet {
            if oldValue == false && isHighlighted {
                highlight()
            } else if oldValue == true && !isHighlighted {
                unHighlight()
            }
        }
    }

    func highlight() {
        animateBackground(to: highlightedBackgroundColor, duration: highlightDuration)
    }

    func unHighlight() {
        animateBackground(to: normalBackgroundColor, duration: highlightDuration)
    }
    
    private func animateBackground(to color: UIColor?, duration: TimeInterval) {
        guard let color = color else { return }
        UIView.animate(withDuration: highlightDuration) {
            self.backgroundColor = color
        }
    }
}

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