UIView Hide / Show з анімацією


154

Моя проста мета - зникнути анімовані функції приховування та показу.

Button.hidden = YES;

Досить просто. Однак чи можна змусити її зникнути, а не просто зникнути? Це виглядає досить непрофесійно.

Відповіді:


259

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

Попереднє рішення

Рішення Михайла спрацює, але насправді це не найкращий підхід.

Проблема з відмиранням альфа полягає в тому, що іноді різні шари перегляду, що перекриваються, виглядають дивно, коли вони зникають. Існують деякі інші варіанти використання Core Animation. Спочатку включіть у ваш додаток QuartzCore Framework і додайте #import <QuartzCore/QuartzCore.h>до свого заголовка. Тепер ви можете зробити одне з наступного:

1) встановіть, button.layer.shouldRasterize = YES;а потім використовуйте альфа-анімаційний код, який Михайло надав у своїй відповіді. Це запобіжить дивному змішуванню шарів, але має невеликий показник продуктивності, і може зробити кнопку розмитою, якщо вона не вирівняна точно на межі пікселя.

Як варіант:

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 = НІ, для згасання ситуації; не мають ефекту анімації, щоб згасати, коли button.hidden = ТАК;
Джейсон

Здається, зламано в 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
Насправді це проста і найкраща відповідь
Іршад Мохамед

3
Хоча це анімує правильно, UISearchBar, який я намагаюся відобразити, з’являється в неправильному місці, поки анімація не завершиться, а потім миттєво перескочить на потрібне місце. Будь-яка ідея? Я використовую дошки розкадрів із створенням інтерфейсів та обмеженнями.
Грег Гілстон

5
Цей код не працює ... він безпосередньо змінює стан без анімації
Mihir Mehta

2
@evya Працюй лише для зникнення, коли заховано = НІ , Не працює для згасання, прихованого = ТАК
Джейсон

Дивовижно. 10x багато
В’ячеслав

125

Щоб зникнути:

Ціль-С

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

Щоб зникнути:

Ціль-С

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
}

використання
вмикання / вимкнення

Чомусь анімація на приховане = ТАК працювала для мене чудово, але анімація на прихований = НІ нічого не робила, тому таке поєднання анімації альфа та встановлення прихованого властивості було корисним.
арломедія

Я просто пишу демонстрацію, але тільки приховану = НІ, зникають у творах, дивно
Джейсон

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

Використовуйте це рішення для плавного ефекту fadeOut та 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()

Дякую


fadeOutпрацює на iOS 13, лише якщо я видаляю встановлені лінії self.isHidden.
Майк

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
    }

}

Ціль-С

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 виходить?
Хуан Боеро

Звичайно! Я додав версію Swift.
Валентин Шергін

5

код @Umair Afzal добре працює після швидких змін 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()

даючи деякий жорсткий ефект при FADEOUT, додав деякі краще рішення тут
Dhanu K

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 `
Ешлі Міллс

Є різниця, він не приховував погляду. І мені також потрібно було приховати погляд. Так і зробили, і поділіться цим.
Умаїр Афзал

3
Чому б просто не прокоментувати іншу відповідь, а не скопіювати її та видати як свою?
Ешлі Міллс

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

Ви можете зробити це ДУЖЕ легко, використовуючи бібліотеку Animatics :

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

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

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, він лише оживить один із цих станів, Причина полягає в тому, що ви встановлюєте прихований стан на значення false, перш ніж викликати UIView.animate методу який спричинить раптова видимість, і якщо ви лише анімуєте альфа, то об’єктний простір все ще є, але це не видно, що призведе до деяких проблем із інтерфейсом.

Тож найкращим підходом є спочатку перевірити, чи перегляд приховано, а потім встановіть альфа на 0,0, наприклад, коли ви встановите прихований стан на хибне, ви не побачите раптової видимості.

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 (з функцією :) приємно і акуратно.

Багато хто опублікував це, але ніхто не помітив, що там брехня виявиться лише під час запуску.

Ви можете перевести приховану властивість до істинної ідеально, тоді як при спробі переходу її в 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 . Отже, я створив функцію, яка приховує / приховує подання в потрібному порядку (коли ховається - встановіть альфа на 0, а потім сховано на істину; приховати - спочатку розкрийте подання, а потім встановіть його альфа на 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помилкова?
Джорджіо

0

Швидкий 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)?'

Корисна довідка .


це працює з автоматичним розгортанням? подібний код не анімує. isHiddenзначення виявляється миттєво (тобто, миттєво приховування / відображення).
Crashalot

0

Цей код дає анімацію, як натискання 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
        })
    }
}

Потім пройдіть isFadeIn( trueабо false)

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.isUserInteractionEnabledна self.isHidden, не пощастило взагалі.

Це воно. Коштуй мене колись, сподіваюся, що хтось допоможе.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.