UIView隐藏/显示动画


154

我的简单目标是淡化动画隐藏和显示功能。

Button.hidden = YES;

很简单。但是,有可能使它淡出而不是消失吗?这样看来相当不专业。

Answers:


258

在iOS 4及更高版本中,有一种方法可以仅使用UIView过渡方法来执行此操作,而无需导入QuartzCore。您可以说:

目标C

[UIView transitionWithView:button
                  duration:0.4
                   options:UIViewAnimationOptionTransitionCrossDissolve
                animations:^{
                     button.hidden = YES;
                }
                completion:NULL];

迅速

UIView.transition(with: button, duration: 0.4, 
                  options: .transitionCrossDissolve, 
                  animations: {
                 button.hidden = false
              })

先前的解决方案

Michail的解决方案会起作用,但实际上并不是最佳方法。

Alpha淡入的问题在于,有时不同的重叠视图层在淡出时看起来很奇怪。还有一些使用Core Animation的替代方法。首先在您的应用程序中包含QuartzCore框架,并将其添加#import <QuartzCore/QuartzCore.h>到标题中。现在,您可以执行以下操作之一:

1)设置button.layer.shouldRasterize = YES;然后使用Michail在其答案中提供的alpha动画代码。这样可以防止各层怪异地融合在一起,但会降低性能,并且如果按钮未完全在像素边界上对齐,则可能会使按钮显得模糊。

或者:

2)改用以下代码设置淡入淡出的动画效果:

CATransition *animation = [CATransition animation];
animation.type = kCATransitionFade;
animation.duration = 0.4;
[button.layer addAnimation:animation forKey:nil];

button.hidden = YES;

这种方法的好处是,即使按钮无法设置动画(例如按钮的文本或图像),您也可以对其进行淡入淡出处理,只需设置过渡,然后立即设置属性即可。


5
@robmathers,我只是测试您的代码,当button.hidden = NO时,上面的两个代码才可以工作;当button.hidden = YES时,没有淡出的动画效果;
杰森

似乎在iOS 12.0中被破坏了
user3532505

5
您应该将要设置动画的事物的超级视图用作transitionWithView参数,以确保成功淡入和淡出。
allenh

159

UIView动画属性是:

- frame
- bounds
- center
- transform
- alpha
- backgroundColor
- contentStretch

描述: 动画

isHidden 不是其中之一,因此我认为最好的方法是:

斯威夫特4:

func setView(view: UIView, hidden: Bool) {
    UIView.transition(with: view, duration: 0.5, options: .transitionCrossDissolve, animations: {
        view.isHidden = hidden
    })
}

目标C:

- (void)setView:(UIView*)view hidden:(BOOL)hidden {
    [UIView transitionWithView:view duration:0.5 options:UIViewAnimationOptionTransitionCrossDissolve animations:^(void){
        [view setHidden:hidden];
    } completion:nil];
}

8
实际上,这是最简单,最好的答案
Irshad Mohamed

3
尽管动画效果正确,但我尝试显示的UISearchBar出现在错误的位置,直到动画完成,然后立即跳转到正确的位置。任何想法?我正在将Storyboard与Interface Builder和Constraints一起使用。
格雷格·希尔斯顿

5
该代码无法正常工作……它直接更改状态而没有设置动画
Mihir Mehta

2
@evya仅在隐藏=否时淡入工作,而不在隐藏= YES时淡入工作
杰森

太棒了 10倍
维亚切斯拉夫

124

淡出:

目标C

[UIView animateWithDuration:0.3 animations:^{
    button.alpha = 0;
} completion: ^(BOOL finished) {//creates a variable (BOOL) called "finished" that is set to *YES* when animation IS completed.
    button.hidden = finished;//if animation is finished ("finished" == *YES*), then hidden = "finished" ... (aka hidden = *YES*)
}];

迅捷2

UIView.animateWithDuration(0.3, animations: {
    button.alpha = 0
}) { (finished) in
    button.hidden = finished
}

斯威夫特3,4,5

UIView.animate(withDuration: 0.3, animations: {
    button.alpha = 0
}) { (finished) in
    button.isHidden = finished
}

淡入:

目标C

button.alpha = 0;
button.hidden = NO;
[UIView animateWithDuration:0.3 animations:^{
    button.alpha = 1;
}];

迅捷2

button.alpha = 0
button.hidden = false
UIView.animateWithDuration(0.3) {
    button.alpha = 1
}

斯威夫特3,4,5

button.alpha = 0
button.isHidden = false
UIView.animate(withDuration: 0.3) {
    button.alpha = 1
}

将淡入/淡出与隐藏状态结合使用可解决我的问题
ACLima

出于某种原因,对hidden = YES设置动画对我来说效果很好,但是对hidden = NO设置动画则无济于事,因此将alpha设置动画和设置hidden属性的这种组合很有用。
arlomedia '17

我只是写了一个演示,但只有隐藏=不,淡入淡出,很奇怪
Jason

9

我使用这个Swift 3扩展名:

extension UIView {

  func fadeIn(duration: TimeInterval = 0.5,
              delay: TimeInterval = 0.0,
              completion: @escaping ((Bool) -> Void) = {(finished: Bool) -> Void in }) {
    UIView.animate(withDuration: duration,
                   delay: delay,
                   options: UIViewAnimationOptions.curveEaseIn,
                   animations: {
      self.alpha = 1.0
    }, completion: completion)
  }

  func fadeOut(duration: TimeInterval = 0.5,
               delay: TimeInterval = 0.0,
               completion: @escaping (Bool) -> Void = {(finished: Bool) -> Void in }) {
    UIView.animate(withDuration: duration,
                   delay: delay,
                   options: UIViewAnimationOptions.curveEaseIn,
                   animations: {
      self.alpha = 0.0
    }, completion: completion)
  }
}

7

迅捷3

func appearView() {
     self.myView.alpha = 0
     self.myView.isHidden = false

     UIView.animate(withDuration: 0.9, animations: {
         self.myView.alpha = 1
     }, completion: {
         finished in
         self.myView.isHidden = false
     })
}

7

迅捷4.2

带有扩展名:

extension UIView {
func hideWithAnimation(hidden: Bool) {
        UIView.transition(with: self, duration: 0.5, options: .transitionCrossDissolve, animations: {
            self.isHidden = hidden
        })
    }
}

简单方法:

func setView(view: UIView, hidden: Bool) {
    UIView.transition(with: view, duration: 0.5, options: .transitionCrossDissolve, animations: {
        view.isHidden = hidden
    })
}

我该如何增加延迟呢?
cvdogan

7

使用此解决方案可获得平滑的淡出和淡入效果

extension UIView {
    func fadeIn(duration: TimeInterval = 0.5, delay: TimeInterval = 0.0, completion: @escaping ((Bool) -> Void) = {(finished: Bool) -> Void in }) {
        self.alpha = 0.0

        UIView.animate(withDuration: duration, delay: delay, options: UIView.AnimationOptions.curveEaseIn, animations: {
            self.isHidden = false
            self.alpha = 1.0
        }, completion: completion)
    }

    func fadeOut(duration: TimeInterval = 0.5, delay: TimeInterval = 0.0, completion: @escaping (Bool) -> Void = {(finished: Bool) -> Void in }) {
        self.alpha = 1.0

        UIView.animate(withDuration: duration, delay: delay, options: UIView.AnimationOptions.curveEaseOut, animations: {
            self.isHidden = true
            self.alpha = 0.0
        }, completion: completion)
    }
}

用法就像

uielement.fadeIn()
uielement.fadeOut()

谢谢


fadeOut仅当我删除设置的行时,才能在iOS 13上运行self.isHidden
Mike Taverne

6

UIView为此,我创建了类别,并实现了一个特殊的概念:visibility。我的解决方案的主要区别在于,您可以在调用[view setVisible:NO animated:YES]后立即进行同步检查[view visible]并获得正确的结果。这很简单,但是非常有用。

此外,允许避免使用“负布尔逻辑”(有关更多信息,请参见 代码完成,第269页,使用正布尔变量名)。

迅速

UIView+Visibility.swift

import UIKit


private let UIViewVisibilityShowAnimationKey = "UIViewVisibilityShowAnimationKey"
private let UIViewVisibilityHideAnimationKey = "UIViewVisibilityHideAnimationKey"


private class UIViewAnimationDelegate: NSObject {
    weak var view: UIView?

    dynamic override func animationDidStop(animation: CAAnimation, finished: Bool) {
        guard let view = self.view where finished else {
            return
        }

        view.hidden = !view.visible
        view.removeVisibilityAnimations()
    }
}


extension UIView {

    private func removeVisibilityAnimations() {
        self.layer.removeAnimationForKey(UIViewVisibilityShowAnimationKey)
        self.layer.removeAnimationForKey(UIViewVisibilityHideAnimationKey)
    }

    var visible: Bool {
        get {
            return !self.hidden && self.layer.animationForKey(UIViewVisibilityHideAnimationKey) == nil
        }

        set {
            let visible = newValue

            guard self.visible != visible else {
                return
            }

            let animated = UIView.areAnimationsEnabled()

            self.removeVisibilityAnimations()

            guard animated else {
                self.hidden = !visible
                return
            }

            self.hidden = false

            let delegate = UIViewAnimationDelegate()
            delegate.view = self

            let animation = CABasicAnimation(keyPath: "opacity")
            animation.fromValue = visible ? 0.0 : 1.0
            animation.toValue = visible ? 1.0 : 0.0
            animation.fillMode = kCAFillModeForwards
            animation.removedOnCompletion = false
            animation.delegate = delegate

            self.layer.addAnimation(animation, forKey: visible ? UIViewVisibilityShowAnimationKey : UIViewVisibilityHideAnimationKey)
        }
    }

    func setVisible(visible: Bool, animated: Bool) {
        let wereAnimationsEnabled = UIView.areAnimationsEnabled()

        if wereAnimationsEnabled != animated {
            UIView.setAnimationsEnabled(animated)
            defer { UIView.setAnimationsEnabled(!animated) }
        }

        self.visible = visible
    }

}

目标C

UIView+Visibility.h

#import <UIKit/UIKit.h>

@interface UIView (Visibility)

- (BOOL)visible;
- (void)setVisible:(BOOL)visible;
- (void)setVisible:(BOOL)visible animated:(BOOL)animated;

@end

UIView+Visibility.m

#import "UIView+Visibility.h"

NSString *const UIViewVisibilityAnimationKeyShow = @"UIViewVisibilityAnimationKeyShow";
NSString *const UIViewVisibilityAnimationKeyHide = @"UIViewVisibilityAnimationKeyHide";

@implementation UIView (Visibility)

- (BOOL)visible
{
    if (self.hidden || [self.layer animationForKey:UIViewVisibilityAnimationKeyHide]) {
        return NO;
    }

    return YES;
}

- (void)setVisible:(BOOL)visible
{
    [self setVisible:visible animated:NO];
}

- (void)setVisible:(BOOL)visible animated:(BOOL)animated
{
    if (self.visible == visible) {
        return;
    }

    [self.layer removeAnimationForKey:UIViewVisibilityAnimationKeyShow];
    [self.layer removeAnimationForKey:UIViewVisibilityAnimationKeyHide];

    if (!animated) {
        self.alpha = 1.f;
        self.hidden = !visible;
        return;
    }

    self.hidden = NO;

    CGFloat fromAlpha = visible ? 0.f : 1.f;
    CGFloat toAlpha = visible ? 1.f : 0.f;
    NSString *animationKey = visible ? UIViewVisibilityAnimationKeyShow : UIViewVisibilityAnimationKeyHide;

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
    animation.duration = 0.25;
    animation.fromValue = @(fromAlpha);
    animation.toValue = @(toAlpha);
    animation.delegate = self;
    animation.removedOnCompletion = NO;
    animation.fillMode = kCAFillModeForwards;
    [self.layer addAnimation:animation forKey:animationKey];
}

#pragma mark - CAAnimationDelegate

- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)finished
{
    if ([[self.layer animationForKey:UIViewVisibilityAnimationKeyHide] isEqual:animation]) {
        self.hidden = YES;
    }
}

@end

@valentin shergin Swift版本即将推出吗?
Juan Boero

当然!我添加了Swift版本。
Valentin Shergin '16

5

经过一些更改后,@ Umair Afzal的代码在swift 5中工作正常

 extension UIView {

func fadeIn(duration: TimeInterval = 0.5, delay: TimeInterval = 0.0, completion: @escaping ((Bool) -> Void) = {(finished: Bool) -> Void in }) {
    self.alpha = 0.0

    UIView.animate(withDuration: duration, delay: delay, options: UIView.AnimationOptions.curveEaseIn, animations: {
        self.isHidden = false
        self.alpha = 1.0
    }, completion: completion)
}

func fadeOut(duration: TimeInterval = 0.5, delay: TimeInterval = 0.0, completion: @escaping (Bool) -> Void = {(finished: Bool) -> Void in }) {
    self.alpha = 1.0

    UIView.animate(withDuration: duration, delay: delay, options: UIView.AnimationOptions.curveEaseIn, animations: {
        self.alpha = 0.0
    }) { (completed) in
        self.isHidden = true
        completion(true)
    }
  }
}

用来

yourView.fadeOut()
yourView.fadeIn()

同时给予淡出一些硬的效果,增加了一些更好的解决方案在这里
Dhanuķ

4

斯威夫特4

extension UIView {

func fadeIn(duration: TimeInterval = 0.5, delay: TimeInterval = 0.0, completion: @escaping ((Bool) -> Void) = {(finished: Bool) -> Void in }) {
    self.alpha = 0.0

    UIView.animate(withDuration: duration, delay: delay, options: UIViewAnimationOptions.curveEaseIn, animations: {
        self.isHidden = false
        self.alpha = 1.0
    }, completion: completion)
}

func fadeOut(duration: TimeInterval = 0.5, delay: TimeInterval = 0.0, completion: @escaping (Bool) -> Void = {(finished: Bool) -> Void in }) {
    self.alpha = 1.0

    UIView.animate(withDuration: duration, delay: delay, options: UIViewAnimationOptions.curveEaseIn, animations: {
        self.alpha = 0.0
    }) { (completed) in
        self.isHidden = true
        completion(true)
    }
}
}

要使用它,只需调用以下函数即可:

yourView.fadeOut() // this will hide your view with animation
yourView.fadeIn() /// this will show your view with animation

您刚刚复制了@MarkMckelvie的答案
Ashley Mills,

有所不同,他没有隐藏视图。我还需要隐藏视图。那就分享一下吧。
Umair Afzal

3
为什么不只是对另一个答案发表评论,而不是复制并以自己的名义散播呢?
Ashley Mills

2

isHidden 是一个立即值,您不能影响其上的动画,相反,您可以使用Alpha隐藏视图

UIView.transition(with: view, duration: 0.5, options: .transitionCrossDissolve, animations: {
        view.alpha = 0
    })

并显示:

UIView.transition(with: view, duration: 0.5, options: .transitionCrossDissolve, animations: {
      view.alpha = 1
})


1
func flipViews(fromView: UIView, toView: UIView) {

    toView.frame.origin.y = 0

    self.view.isUserInteractionEnabled = false

    UIView.transition(from: fromView, to: toView, duration: 0.5, options: .transitionFlipFromLeft, completion: { finished in            

        fromView.frame.origin.y = -900

        self.view.isUserInteractionEnabled = true

    })


}

1

你可以试试看

 func showView(objView:UIView){

    objView.alpha = 0.0
    UIView.animate(withDuration: 0.5, animations: {
        objView.alpha = 0.0
    }, completion: { (completeFadein: Bool) -> Void in
        objView.alpha = 1.0
        let transition = CATransition()
        transition.duration = 0.5
        transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
        transition.type = kCATransitionFade
        objView.layer.add(transition, forKey: nil)
    })
}

func HideView(objView:UIView){

    UIView.animate(withDuration: 0.5, animations: {
        objView.alpha = 1.0
    }, completion: { (completeFadein: Bool) -> Void in
        objView.alpha = 0.0
        let transition = CATransition()
        transition.duration = 0.5
        transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
        transition.type = kCATransitionFade
        objView.layer.add(transition, forKey: nil)
    })
}

并传递您的视图名称

        showView(objView: self.viewSaveCard)
        HideView(objView: self.viewSaveCard)

1

如果您的视图默认情况下设为“隐藏”,或者您更改了“隐藏”状态(我认为在很多情况下应该这样做),那么此页面中的任何一种方法都不会同时为您提供FadeIn / FadeOut动画,它只会为其中一种状态设置动画,原因是您在调用UIView.animate之前将Hidden状态设置为false方法这将导致突然的可见性;如果仅对Alpha进行动画处理,则对象空间仍然存在,但是不可见,这将导致某些UI问题。

因此,最好的方法是先检查视图是否被隐藏,然后将alpha设置为0.0,这样将Hidden状态设置为false时,您将不会看到可见的图像。

func hideViewWithFade(_ view: UIView) {
    if view.isHidden {
        view.alpha = 0.0
    }

    view.isHidden = false

    UIView.animate(withDuration: 0.3, delay: 0.0, options: .transitionCrossDissolve, animations: {
        view.alpha = view.alpha == 1.0 ? 0.0 : 1.0
    }, completion: { _ in
        view.isHidden = !Bool(truncating: view.alpha as NSNumber)
    })
}

这解决了其他人问到的淡入淡出问题。聪明的方法。
BooTooMany

1

UIView.transition(with :)函数非常简洁。

许多人都发布了它,但没有人注意到只有在运行它时才会出现故障。

您可以将隐藏属性完美地转换为true,而当您尝试将其转换为false时,视图将突然消失而没有任何动画。

这是因为此api仅视图中起作用,这意味着当您转换视图以显示(实际上本身会立即显示)时,仅其内容会逐渐动画。

当您尝试隐藏此视图时,其本身会立即隐藏,从而使动画内容无意义。

为了解决这个问题,在隐藏视图时,过渡目标应该是其父视图,而不是要隐藏的视图。

func transitionView(_ view: UIView?, show: Bool, completion: BoolFunc? = nil) {
    guard let view = view, view.isHidden == show, let parent = view.superview else { return }

    let target: UIView = show ? view : parent
    UIView.transition(with: target, duration: 0.4, options: [.transitionCrossDissolve], animations: {
        view.isHidden = !show
    }, completion: completion)
}

0

我对Swift 3的解决方案。因此,我创建了一个函数,该函数以正确的顺序隐藏/取消隐藏视图(隐藏时-将alpha设置为0,然后将isHidden设置为true;取消隐藏-首先显示视图,然后将其alpha设置为1):

func hide(_ hide: Bool) {
    let animations = hide ? { self.alpha = 0 } :
                            { self.isHidden = false }
    let completion: (Bool) -> Void = hide ? { _ in self.isHidden = true } :
                                            { _ in UIView.animate(withDuration: duration, animations: { self.alpha = 1 }) }
    UIView.animate(withDuration: duration, animations: animations, completion: completion)
}

为什么在completion块中有另一个动画hide是false?
乔治

0

迅捷4过渡

    UIView.transition(with: view, duration: 3, options: .transitionCurlDown,
                      animations: {
                        // Animations
                        view.isHidden = hidden
    },
                      completion: { finished in
                        // Compeleted
    })

如果您将方法用于较早的Swift版本,则会收到错误消息:

Cannot convert value of type '(_) -> ()' to expected argument type '(() -> Void)?'

有用的参考


这可以与自动布局一起使用吗?类似的代码没有动画。该isHidden值将立即呈现(即,立即隐藏/显示视图)。
Crashalot

0

这段代码给出了一个动画,就像在uinavigation controller中推送viewController一样。

CATransition *animation = [CATransition animation];
 animation.type = kCATransitionPush;
 animation.subtype = kCATransitionFromRight;
 animation.duration = 0.3;
 [_viewAccountName.layer addAnimation:animation forKey:nil];

 _viewAccountName.hidden = true;

用于流行动画...

 CATransition *animation = [CATransition animation];
 animation.type = kCATransitionPush;
 animation.subtype = kCATransitionFromLeft;
 animation.duration = 0.3;
 [_viewAccountName.layer addAnimation:animation forKey:nil];

 _viewAccountName.hidden = false;

0

尝试了一些已存在的答案,一些仅在一种情况下有效,其中一些需要添加两个功能。

选项1

与无关view.isHidden

extension UIView {
    func animate(fadeIn: Bool, withDuration: TimeInterval = 1.0) {
        UIView.animate(withDuration: withDuration, delay: 0.0, options: .curveEaseInOut, animations: {
            self.alpha = fadeIn ? 1.0 : 0.0
        })
    }
}

然后通过isFadeIntruefalse

view.animate(fadeIn: isFadeIn) 

选项2

不要传递任何参数。根据淡入或淡出isUserInteractionEnabled。这也非常适合来回动画的情况。

func animateFadeInOut(withDuration: TimeInterval = 1.0) {
    self.isUserInteractionEnabled = !self.isUserInteractionEnabled
    UIView.animate(withDuration: withDuration, delay: 0.0, options: .curveEaseInOut, animations: {
        self.alpha = self.isUserInteractionEnabled ? 1.0 : 0.0
    })
}

然后你打电话

yourView.animateFadeInOut()

为什么self.isUserInteractionEnabled

试图替换self.isUserInteractionEnabledself.isHidden,根本没有运气。

而已。花了我一些时间,希望它能帮助别人。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.