UIView 动画隐藏/显示

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

Button.hidden = YES;

很简单。然而,有没有可能让它淡出,而不仅仅是消失?这样看起来很不专业。

202790 次浏览

淡出:

目标 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*)
}];

Swift 2

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

斯威夫特345

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

Swift 2

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

斯威夫特345

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

在 iOS4和更高版本中,有一种方法可以只使用 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 渐变的问题是,有时不同的重叠视图层在渐变时看起来很奇怪。还有其他一些使用核心动画的替代方案。首先在你的应用程序中包含 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;

这种方法的好处是你可以交叉淡化按钮的任何属性,即使它们不是可动画的(例如按钮的文本或图像) ,只需设置转换,然后立即设置属性。

为此,我为 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

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
})
}

目标丙:

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

你可以很容易地使用 动物库:

//To hide button:
AlphaAnimator(0) ~> button


//to show button
AlphaAnimator(1) ~> button

Swift 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
})
}

我对 Swift 3的解决方案。因此,我创建了这个函数,它按照正确的顺序隐藏/取消隐藏视图(当隐藏时——将 alpha 设置为0,然后将 Hidden 设置为 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)
}

我使用这个小小的 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)
}
}
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


})




}

迅捷4过渡

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

如果你使用老版本的快捷方式,你会得到一个错误:

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

有用的 参考文献

你可以试试这个。

 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)

Swift 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

这段代码提供了一个类似于将 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;

Swift 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
})
}

尝试了一些退出的答案,有的只适用于一种情况,有的需要添加两个函数。

选择一

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
})
}
}

然后传递 isFadeIn(truefalse)

view.animate(fadeIn: isFadeIn)

选择二

不要传递任何参数。它根据 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.isHidden代替 self.isUserInteractionEnabled, 一点运气都没有。

就是这样,花了我一些时间,希望能帮到别人。

@ 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()

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

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()

谢谢

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
})

如果你的视图被默认设置为隐藏或者你改变了隐藏状态,我认为在很多情况下你应该这样做,那么在这个页面中没有一个方法会同时给你 FadeIn/FadeOut 动画,它只会动画其中一个状态,原因是你在调用 UIView.animate方法之前将隐藏状态设置为 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)
})
}

Shift (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)
}

Swift 5.0,使用泛型:

func hideViewWithAnimation<T: UIView>(shouldHidden: Bool, objView: T) {
if shouldHidden == true {
UIView.animate(withDuration: 0.3, animations: {
objView.alpha = 0
}) { (finished) in
objView.isHidden = shouldHidden
}
} else {
objView.alpha = 0
objView.isHidden = shouldHidden
UIView.animate(withDuration: 0.3) {
objView.alpha = 1
}
}
}

用途:

hideViewWithAnimation(shouldHidden: shouldHidden, objView: itemCountLabelBGView)
hideViewWithAnimation(shouldHidden: shouldHidden, objView: itemCountLabel)
hideViewWithAnimation(shouldHidden: shouldHidden, objView: itemCountButton)

这里 itemCountLabelBGViewUIViewitemCountLabelUILabelitemCountButtonUIButton,所以它适用于父类为 UIView的每个视图对象。

UIView.transition(with: title3Label, duration: 0.4,
options: .transitionCrossDissolve,
animations: {
self.title3Label.isHidden = !self.title3Label.isHidden
})

在视图上应用带有一定延迟的转换,可以产生隐藏和显示效果