UIView Hide / Show z animacją

154

Moim prostym celem jest zanikanie animowanych funkcji ukrywania i pokazywania.

Button.hidden = YES;

Wystarczająco proste. Czy można jednak sprawić, że zniknie, a nie zniknie? W ten sposób wygląda to raczej nieprofesjonalnie.

JTApps
źródło

Odpowiedzi:

259

W systemie iOS 4 i nowszych można to zrobić po prostu przy użyciu metody przejścia UIView bez konieczności importowania QuartzCore. Możesz po prostu powiedzieć:

Cel C

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

Szybki

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

Poprzednie rozwiązanie

Rozwiązanie Michail zadziała, ale tak naprawdę nie jest to najlepsze podejście.

Problem z zanikaniem alfa polega na tym, że czasami różne nakładające się warstwy widoku wyglądają dziwnie, gdy zanikają. Istnieje kilka innych możliwości korzystania z Core Animation. Najpierw umieść strukturę QuartzCore w swojej aplikacji i dodaj #import <QuartzCore/QuartzCore.h>do nagłówka. Teraz możesz wykonać jedną z następujących czynności:

1) ustaw, button.layer.shouldRasterize = YES;a następnie użyj kodu animacji alfa, który podał Michail w swojej odpowiedzi. Zapobiegnie to dziwnemu mieszaniu się warstw, ale ma niewielki spadek wydajności i może sprawić, że przycisk będzie wyglądał na rozmazany, jeśli nie jest dokładnie wyrównany na granicy pikseli.

Alternatywnie:

2) Zamiast tego użyj następującego kodu, aby animować zanikanie:

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

button.hidden = YES;

Zaletą tego podejścia jest to, że możesz przenikać dowolną właściwość przycisku, nawet jeśli nie można jej animować (np. Tekst lub obraz przycisku), po prostu skonfiguruj przejście, a następnie ustaw swoje właściwości.

Nick Lockwood
źródło
5
@robmathers, po prostu testuję twój kod, dwa powyższe kody działają tylko wtedy, gdy button.hidden = NIE, dla zanikania sytuacji; nie mają efektu animacji przy zanikaniu, gdy button.hidden = YES;
Jason,
Wygląda na to, że jest uszkodzony w iOS 12.0
user3532505
5
Powinieneś użyć nadzoru obiektu, który animujesz jako transitionWithViewparametru, aby zapewnić pomyślne włączanie i wyłączanie.
allenh
159

Animowane właściwości UIView to:

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

Opisz w: Animacje

isHidden nie jest jednym z nich, więc jak to widzę najlepiej jest:

Swift 4:

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

Cel C:

- (void)setView:(UIView*)view hidden:(BOOL)hidden {
    [UIView transitionWithView:view duration:0.5 options:UIViewAnimationOptionTransitionCrossDissolve animations:^(void){
        [view setHidden:hidden];
    } completion:nil];
}
evya
źródło
8
Właściwie to jest prosta i najlepsza odpowiedź
Irshad Mohamed
3
Chociaż animacja jest poprawna, pasek UISearchBar, który próbuję wyświetlić, pojawia się w niewłaściwym miejscu, dopóki animacja nie zostanie zakończona, a następnie natychmiast przeskoczy do właściwej pozycji. Dowolny pomysł? Używam scenorysów z konstruktorem interfejsu i ograniczeniami.
Greg Hilston
5
Ten kod nie działa ... bezpośrednio zmienia stan bez animacji
Mihir Mehta
2
@evya Działa tylko dla pojawiania się, gdy jest ukryty = NIE , Nie działa dla zanikania, ukryty = TAK
Jason
Niesamowite. 10x dużo
Wiaczesław
125

Aby zniknąć:

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

Szybki 2

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

Swift 3, 4, 5

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

Aby wprowadzić:

Cel C

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

Szybki 2

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

Swift 3, 4, 5

button.alpha = 0
button.isHidden = false
UIView.animate(withDuration: 0.3) {
    button.alpha = 1
}
Michaił Grebionkin
źródło
użycie fade in / out w połączeniu ze stanem ukrytym rozwiązało mój problem
ACLima
Z jakiegoś powodu animacja do hidden = YES działała dobrze, ale animacja do hidden = NO nic nie dała, więc ta kombinacja animowania alfy i ustawiania ukrytej właściwości była pomocna.
arlomedia
Po prostu piszę demo, ale tylko ukryte = NIE, zanikają, dziwne
Jason
9

Używam tego małego rozszerzenia 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)
  }
}
Mark Mckelvie
źródło
7

Szybki 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
     })
}
Scaraux
źródło
7

szybki 4.2

z rozszerzeniem:

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

prosta metoda:

func setView(view: UIView, hidden: Bool) {
    UIView.transition(with: view, duration: 0.5, options: .transitionCrossDissolve, animations: {
        view.isHidden = hidden
    })
}
Mohsen mokhtari
źródło
Jak mogę dodać opóźnienie dla tego?
cvdogan
7

Użyj tego rozwiązania, aby uzyskać płynne efekty zanikania i zanikania

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

użycie jest jak podobne

uielement.fadeIn()
uielement.fadeOut()

Dzięki

Dhanu K
źródło
fadeOutdziała na iOS 13 tylko wtedy, gdy usunę ustawione linie self.isHidden.
Mike Taverne
6

I stworzył kategorię UIViewdo tego celu i wdrożony specjalny nieco inną koncepcję: visibility. Główną różnicą w moim rozwiązaniu jest to, że możesz zadzwonić [view setVisible:NO animated:YES]i zaraz po tym synchronicznie sprawdzić [view visible]i uzyskać poprawny wynik. Jest to dość proste, ale niezwykle przydatne.

Poza tym dozwolone jest unikanie stosowania „negatywnej logiki boolowskiej” (zobacz Code Complete, strona 269, Użyj dodatnich nazw zmiennych boolowskich, aby uzyskać więcej informacji).

Szybki

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
    }

}

Cel 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
źródło
Nadchodzi wersja Swift @valentin shergin?
Juan Boero
Pewnie! Dodałem wersję Swift.
Valentin Shergin
5

kod @Umair Afzal działa dobrze w Swift 5 po pewnych zmianach

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

do użycia

yourView.fadeOut()
yourView.fadeIn()
Sanjay Mishra
źródło
dając trochę mocniejszy efekt podczas wyciszania, dodałem tutaj
Dhanu K
4

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

Aby z niego skorzystać, po prostu wywołaj te funkcje, takie jak:

yourView.fadeOut() // this will hide your view with animation
yourView.fadeIn() /// this will show your view with animation
Umair Afzal
źródło
Właśnie skopiowałeś odpowiedź @ MarkMckelvie
Ashley Mills,
Jest różnica, on nie ukrywał widoku. Musiałem też ukryć widok. Zrób to i udostępnij.
Umair Afzal
3
Dlaczego po prostu nie skomentować drugiej odpowiedzi, zamiast kopiować ją i udawać własną?
Ashley Mills,
2

isHidden jest wartością natychmiastową i nie możesz wpłynąć na animację na niej, zamiast tego możesz użyć Alpha do ukrycia widoku

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

I za pokazanie:

UIView.transition(with: view, duration: 0.5, options: .transitionCrossDissolve, animations: {
      view.alpha = 1
})
mohsen
źródło
1

Możesz to BARDZO łatwo zrobić używając biblioteki Animatics :

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

//to show button
AlphaAnimator(1) ~> button
Nikita Arkhipov
źródło
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

    })


}
Vimal Saifudin
źródło
1

Możesz tego spróbować.

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

I podaj nazwę swojego widoku

        showView(objView: self.viewSaveCard)
        HideView(objView: self.viewSaveCard)
kalpesh
źródło
1

Jeśli twój widok jest domyślnie ustawiony na ukryty lub zmienisz stan Ukryty, który myślę, że powinieneś w wielu przypadkach, to żadne z podejść na tej stronie nie da ci animacji FadeIn / FadeOut, animuje tylko jeden z tych stanów, powodem jest to, że ustawiasz stan Hidden na false przed wywołaniem metody UIView.animate , która spowoduje nagłą widoczność i jeśli animujesz tylko alfa, przestrzeń obiektu nadal istnieje, ale nie jest widoczna, co spowoduje pewne problemy z interfejsem użytkownika.

Najlepszym podejściem jest więc najpierw sprawdzenie, czy widok jest ukryty, a następnie ustawienie alfa na 0,0, w ten sposób, gdy ustawisz stan Ukryty na fałsz, nie zobaczysz nagłej widoczności.

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)
    })
}
Shahriar
źródło
To rozwiązuje problem, o który inni pytali, gdzie fadeiny nie działały. Sprytne podejście.
BooTooMany
1

UIView.transition (z :) funkcja jest ładna i schludna.

Wielu to opublikowało, ale nikt nie zauważył, że błąd pojawi się dopiero po uruchomieniu.

Możesz perfekcyjnie zmienić ukrytą właściwość na true, podczas gdy gdy spróbujesz zmienić ją na false, widok po prostu nagle zniknie bez żadnej animacji.

Dzieje się tak, ponieważ ten interfejs API działa tylko w widoku, co oznacza, że ​​po przejściu widoku do pokazania, w rzeczywistości sam pokazuje natychmiast, tylko jego zawartość jest animowana stopniowo.

Kiedy próbujesz ukryć ten widok, sam się ukryje od razu, sprawia, że ​​animacja jest bez znaczenia.

Aby rozwiązać ten problem, podczas ukrywania widoku celem przejścia powinien być jego widok nadrzędny, a nie widok, który chcesz ukryć.

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)
}
LiLi Kazine
źródło
0

Moje rozwiązanie dla Swift 3 . Stworzyłem więc funkcję, która ukrywa / odkrywa widok we właściwej kolejności (podczas ukrycia - ustaw alfa na 0, a potem isHidden na true; odkrywanie - najpierw odsłoń widok, a potem ustaw jego alfa na 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)
}
Nazariy Vlizlo
źródło
Dlaczego w completionbloku jest inna animacja, kiedy hidejest fałszywa?
Giorgio,
0

Swift 4 Transition

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

Jeśli zastosujesz podejście do starszych wersji Swift, pojawi się błąd:

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

Przydatne odniesienia .

nanospeck
źródło
czy to działa z autoukładem? podobny kod nie jest animowany. isHiddenwartość staje się natychmiast (tj natychmiast ukrywanie / pokazywanie widoku).
Crashalot
0

Ten kod daje animację, taką jak wypychanie viewController w kontrolerze uinavigation ...

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

 _viewAccountName.hidden = true;

Użyłem tego do animacji pop ...

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

 _viewAccountName.hidden = false;
Prasanna
źródło
0

Wypróbowałem niektóre z opuszczonych odpowiedzi, niektóre działają tylko w jednej sytuacji, a niektóre z nich wymagają dodania dwóch funkcji.

opcja 1

Nie ma z tym nic wspólnego 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
        })
    }
}

Następnie przejdź isFadeIn( truelub false)

view.animate(fadeIn: isFadeIn) 

Opcja 2

Nie przekazuj żadnego parametru. Zanika lub zanika zgodnie z isUserInteractionEnabled. To również bardzo dobrze pasuje do sytuacji, w której animowane są tam iz powrotem.

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

Wtedy dzwonisz

yourView.animateFadeInOut()

Dlaczego self.isUserInteractionEnabled?

Starał się zastąpić self.isUserInteractionEnabledprzez self.isHidden, bez powodzenia w ogóle.

Otóż ​​to. Kosztuje mnie kiedyś, mam nadzieję, że to komuś pomoże.

William Hu
źródło