Como aumentar e diminuir um UIVisualEffectView e / ou UIBlurEffect?

92

Eu quero esmaecer um UIVisualEffectsView com um UIBlurEffect dentro e fora:

var blurEffectView = UIVisualEffectView()
blurEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))

Eu uso uma animação normal dentro de uma função chamada por a UIButtonpara fade in, o mesmo para fade out, mas .alpha = 0& hidden = true:

blurEffectView.hidden = false
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseOut) {
    self.blurEffectView.alpha = 1
}

Agora, desvanecendo-se em ambas as direções não funciona, mas dá-me um erro quando desaparecendo para fora :

<UIVisualEffectView 0x7fdf5bcb6e80>está sendo solicitado a animar sua opacidade. Isso fará com que o efeito pareça interrompido até que a opacidade retorne a 1.

Questão

Como faço o fade UIVisualEffectViewin e fade out com sucesso sem quebrá- lo e ter uma transição de fading?

Nota

  • Tentei colocar o UIVisualEffectViewem um UIViewe desvanecer aquele, sem sucesso
LinusGeffarth
fonte
Você pode evitar o erro: 1. NÃO atribuindo um estilo inicial ao blurEffect. isto é, var blurEffectView = UIVisualEffectView () e então 2. atribuir blurEffect a blurEffectView DENTRO do bloco de animação. 3. animar a "atribuição de blurEffect para blurEffectView" é feito no lugar de ajustes para blurEffectView.alpha. ** O erro aparecerá mesmo se você atribuir um valor inicial, anulá-lo e adicioná-lo novamente. Portanto, a chave para evitar o erro é não atribuir o efeito a blurEffectView até o bloco de animação.
ObjectiveTC
Também precisa ser mencionado, o blurEffectView deve ter alpha = 1.0 para evitar o erro. A adulteração do alfa de qualquer forma produzirá o erro. Por exemplo, antes do bloco de animação você definir blurEffectView.alpha = 0.7, então no animationBlock você atribuir blurEffectView.effect = blurEffect, o erro será produzido.
ObjectiveTC

Respostas:

185

Acho que isso é novo no iOS9, mas agora você pode definir o efeito de UIVisualEffectViewdentro de um bloco de animação:

let overlay = UIVisualEffectView()
// Put it somewhere, give it a frame...
UIView.animate(withDuration: 0.5) {
    overlay.effect = UIBlurEffect(style: .light)
}

Defina nilpara remover.

MUITO IMPORTANTE - Ao testar isso no simulador, certifique-se de definir a substituição da qualidade gráfica do simulador como Alta qualidade para que isso funcione.

Jrturton
fonte
1
Funciona perfeitamente. Obrigado.
Baza207
Ótimo! Como você disse, não funciona para iOS 8: /
LinusGeffarth
Bem, exatamente por causa do que escrevi acima. Eu aprovei agora mesmo. @jpros
LinusGeffarth
10
Você também pode definir o efeito como nulo para obter um "desfoque" bonito
jpros
1
@jpros Você se importaria de explicar o que entende por "desfocar?"
ndmeiri
19

A documentação da Apple (atualmente) afirma ...

Ao usar a classe UIVisualEffectView, evite valores alfa menores que 1.

e

Definir o alfa para menos de 1 na visualização do efeito visual ou qualquer uma de suas supervisões faz com que muitos efeitos pareçam incorretos ou nem apareçam.

Eu acredito que algum contexto importante está faltando aqui ...

Eu sugeriria que a intenção é evitar valores alfa menores que 1 para uma exibição persistente. Na minha humilde opinião, isso não se aplica à animação de uma visualização.

Meu ponto - sugiro que valores alfa menores que 1 são aceitáveis ​​para animações.

A mensagem do terminal afirma:

UIVisualEffectView está sendo solicitado a animar sua opacidade. Isso fará com que o efeito pareça interrompido até que a opacidade retorne a 1.

Lendo isso com atenção, o efeito parecerá estar quebrado. Meus pontos sobre isso são:

  • a aparente quebra só importa realmente para uma visão que é persistente - não muda;
  • um persistente / imutável UIVisualEffect visão com um valor alfa menor que 1 não será apresentada conforme pretendido / projetado pela Apple; e
  • a mensagem no terminal não é um erro, apenas um aviso.

Para estender a resposta de @jrturton acima que me ajudou a resolver meu problema, eu adicionaria ...

Para esmaecer, UIVisualEffectuse o seguinte código (Objective-C):

UIView.animateWithDuration(1.0, animations: {
//  EITHER...
    self.blurEffectView.effect = UIBlurEffect(nil)
//  OR...
    self.blurEffectView.alpha = 0
}, completion: { (finished: Bool) -> Void in
    self.blurEffectView.removeFromSuperview()
} )

Eu uso com sucesso os dois métodos: definir a effectpropriedade como nile definir a alphapropriedade como 0.

Observe que definir effectcomo nilcria um "flash legal" (na falta de uma descrição melhor) no final da animação, enquanto definir alphacomo 0cria uma transição suave.

(Informe quaisquer erros de sintaxe ... eu escrevo em obj-c.)

andrewbuilder
fonte
Obrigado pela sua contribuição. Eu entendi seu ponto; o erro já foi resolvido para mim com exatamente o que você mencionou :)
LinusGeffarth
14

Aqui está a solução que eu acabei, que funciona tanto no iOS10 quanto em versões anteriores usando Swift 3

extension UIVisualEffectView {

    func fadeInEffect(_ style:UIBlurEffectStyle = .light, withDuration duration: TimeInterval = 1.0) {
        if #available(iOS 10.0, *) {
            let animator = UIViewPropertyAnimator(duration: duration, curve: .easeIn) {
                self.effect = UIBlurEffect(style: style)
            }

            animator.startAnimation()
        }else {
            // Fallback on earlier versions
            UIView.animate(withDuration: duration) {
                self.effect = UIBlurEffect(style: style)
            }
        }
    }

    func fadeOutEffect(withDuration duration: TimeInterval = 1.0) {
        if #available(iOS 10.0, *) {
            let animator = UIViewPropertyAnimator(duration: duration, curve: .linear) {
                self.effect = nil
            }

            animator.startAnimation()
            animator.fractionComplete = 1
        }else {
            // Fallback on earlier versions
            UIView.animate(withDuration: duration) {
                self.effect = nil
            }
        }
    }

}

Você também pode verificar esta essência para encontrar um exemplo de uso.

Gunhan
fonte
Usar o UIViewPropertyAnimatoré a única coisa que parece funcionar no iOS 11. Obrigado por isso!
LinusGeffarth
Não se esqueça de adicionar: import UIKit
Oleksandr
8

Apenas uma solução alternativa - coloque UIVisualEffectViewem uma visualização de contêiner e altere a alphapropriedade desse contêiner. Essa abordagem funciona perfeitamente para mim no iOS 9. Parece que não funciona mais no iOS 10.

berílio
fonte
Legal. Obrigado, vou tentar :)
LinusGeffarth
1
Você pode fornecer um código de amostra. Isso não parece funcionar.
cnotethegr8
Funciona para mim no iOS 9
5hrp
@DerrickHunt O mesmo aqui. Você encontrou uma solução?
damirstuhec
2
@damirstuhec Se o seu projeto for iOS 10 apenas, você pode usar a nova classe UIViewPropertyAnimator para animar a força UIBlurView. Se você precisar oferecer suporte a versões mais antigas, pode usar o código acima e a palavra-chave #available para usar UIViewPropertyAnimator para dispositivos com 10. Espero que isso ajude!
Derrick Hunt
5

Você pode alterar o alfa da visualização dos efeitos visuais sem problemas, exceto o aviso no console. A visualização pode parecer simplesmente parcialmente transparente, em vez de desfocada. Mas isso geralmente não é um problema se você estiver apenas alterando o alfa durante a animação.

Seu aplicativo não vai travar ou ser rejeitado por isso. Teste-o em um dispositivo real (ou oito). Se você está feliz com sua aparência e desempenho, tudo bem. A Apple está apenas avisando que pode não parecer ou não funcionar tão bem quanto uma visualização de efeitos visuais com um valor alfa de 1.

Dave Batton
fonte
5

Você pode tirar um instantâneo de uma visão estática subjacente e aumentar e diminuir o brilho sem tocar na opacidade da visão desfocada. Supondo um ivar de blurView:

func addBlur() {
    guard let blurEffectView = blurEffectView else { return }

    //snapShot = UIScreen.mainScreen().snapshotViewAfterScreenUpdates(false)
    let snapShot = self.view.snapshotViewAfterScreenUpdates(false)
    view.addSubview(blurEffectView)
    view.addSubview(snapShot)

    UIView.animateWithDuration(0.25, animations: {
        snapShot.alpha = 0.0
    }, completion: { (finished: Bool) -> Void in
        snapShot.removeFromSuperview()
    } )
}

func removeBlur() {
    guard let blurEffectView = blurEffectView else { return }

    let snapShot = self.view.snapshotViewAfterScreenUpdates(false)
    snapShot.alpha = 0.0
    view.addSubview(snapShot)

    UIView.animateWithDuration(0.25, animations: {
        snapShot.alpha = 1.0
    }, completion: { (finished: Bool) -> Void in
        blurEffectView.removeFromSuperview()
        snapShot.removeFromSuperview()
    } )
}
David H
fonte
Boa ideia também, mas não funcionará bem para conteúdo não estático (em movimento / animação) abaixo da visualização desfocada.
LinusGeffarth
@LinusG. Bem, a Apple diz que não devemos brincar com uma visão turva alfa. Você poderia usar um CADisplayLink para capturar capturas de tela de sua visualização de base e, em seguida, renderizá-las em uma visualização de imagem (talvez uma que você fez - bem leve) - e mostrá-las na visualização superior enquanto altera sua opacidade para 0. Possível, mas muito trabalhos.
David H
1
Esta foi a única maneira (até agora) de conseguir o que queria no iOS 10 - fade em modal em tela cheia, com um efeito de desfoque muito escuro. Obrigado!
Graham de
3

Se você quiser desaparecer UIVisualEffectView - para ios10, use UIViewPropertyAnimator

    UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:nil];
    blurEffectView.frame = self.view.frame;
    blurEffectView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

    UIView *blackUIView = [[UIView alloc]initWithFrame:self.view.frame];
    [bacgroundImageView addSubview:blackUIView];
    [blackUIView addSubview:blurEffectView];
    UIViewPropertyAnimator *animator  = [[UIViewPropertyAnimator alloc] initWithDuration:4.f curve:UIViewAnimationCurveLinear animations:^{
        [blurEffectView setEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
    }];

então você pode definir a porcentagem

[animator setFractionComplete:percent];

para ios9 você pode usar o componente alfa

kiril kiroski
fonte
2
A pergunta é especificamente marcada como "rápido", não "objetivo-c": forneça uma resposta em Swift. Obrigado!
Eric Aya
1
Obrigado pelo código C Objetivo. Achei útil, pois não havia muitos outros exemplos listados.
Adam Ware
2

O alfa do UIVisualEffectView sempre deve ser 1. Acho que você pode obter o efeito definindo o alfa da cor de fundo.

Fonte: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIVisualEffectView/index.html

Peter Lendvay
fonte
Peter, você pode explicar como definir o alfa da cor de fundo?
Boris Y.
@borisy usa o colorWithAlphaComponent:método nas UIColorinstâncias. Não tenho certeza de como obter o mesmo efeito com a configuração da cor de fundo, no entanto.
nêmesis
Ao alterar o componente alfa do plano de fundo não está funcionando como esperado
Honghao Zhang
1

Eu faço um uiview cujo alfa é 0 e adiciono blurview como subview disso. Assim, posso ocultar / mostrar ou arredondar os cantos com animação.

Kemal Can Kaynak
fonte
1

Acabei com a seguinte solução, usando animações separadas para o UIVisualEffectViewe o conteúdo. Usei o viewWithTag()método para obter uma referência ao UIViewinterior do UIVisualEffectView.

let blurEffectView = UIVisualEffectView()
// Fade in
UIView.animateWithDuration(1) { self.blurEffectView.effect = UIBlurEffect(style: .Light) }    
UIView.animateWithDuration(1) { self.blurEffectView.viewWithTag(1)?.alpha = 1 }
// Fade out
UIView.animateWithDuration(1) { self.blurEffectView.effect = nil }    
UIView.animateWithDuration(1) { self.blurEffectView.viewWithTag(1)?.alpha = 0 }

Eu preferiria que a animação única alterasse o alfa, mas isso evita o erro e parece funcionar tão bem.

demian
fonte
1

Eu acabei de ter esse problema e a maneira que eu resolvi foi hospedar o UIVisualEffectsView em um UIView e animar o alfa desse UIView.

Isso funcionou bem, exceto que assim que o alfa mudou abaixo de 1.0, ele se tornou um branco sólido e parecia muito chocante. Para contornar isso, você deve definir a propriedade de camada do UIViewcontainerView.layer.allowsGroupOpacity = false e isso impedirá que ele pisque em branco.

Agora você pode animar / fade out o UIView contendo a visualização de efeitos visuais e quaisquer outras subvisualizações usando sua propriedade alpha e não precisa se preocupar com falhas gráficas ou com o log de uma mensagem de aviso.

user1898712
fonte
0
_visualEffectView.contentView.alpha = 0;

Para alterar o alfa de UIVisualEffectView, você deve alterar o contentView de _visualEffectView. Se você alterar o alfa de _visualEffectView, você obterá este

<UIVisualEffectView 0x7ff7bb54b490> is being asked to animate its opacity. This will cause the effect to appear broken until opacity returns to 1.
Chile
fonte
0

Normalmente, eu só quero animar um desfoque quando estou apresentando um controlador de exibição na tela e quero desfocar o controlador de exibição de apresentação. Aqui está uma extensão que adiciona blur () e unblur () a um controlador de visualização para facilitar isso:

extension UIViewController {

   func blur() {
       //   Blur out the current view
       let blurView = UIVisualEffectView(frame: self.view.frame)
       self.view.addSubview(blurView)
       UIView.animate(withDuration:0.25) {
           blurView.effect = UIBlurEffect(style: .light)
       }
   }

   func unblur() {
       for childView in view.subviews {
           guard let effectView = childView as? UIVisualEffectView else { continue }
           UIView.animate(withDuration: 0.25, animations: {
                effectView.effect = nil
           }) {
               didFinish in
               effectView.removeFromSuperview()
           }
       }
   }
}

É claro que você pode tornar isso mais robusto, permitindo que o usuário escolha o estilo do efeito, modifique a duração, chame algo quando a animação for concluída, marque a visualização do efeito visual adicionado em desfoque () para garantir que seja a única removida quando você desfocar ( ), etc., mas não descobri a necessidade de fazer essas coisas até agora, pois essa tende a ser um tipo de operação do tipo "dispare e esqueça".

cc.
fonte
"disparar e esquecer" é tão identificável! Ha
LinusGeffarth
0

com base na resposta de @ cc, modifiquei sua extensão para desfocar a visualização

extensão UIView {

    função blur () {
        // Desfocar a visão atual
        let blurView = UIVisualEffectView (frame: self.bounds)
        self.addSubview (blurView)
        UIView.animate (withDuration: 0,25) {
            blurView.effect = UIBlurEffect (estilo: .dark)
        }
    }

    func unblur () {
        para childView em subvisualizações {
            guarda deixe effectView = childView como? UIVisualEffectView else {continue}
            UIView.animate (withDuration: 2.5, animações: {
                effectView.effect = nil
            }) {
                didFinish em
                effectView.removeFromSuperview ()
            }
        }
    }
}
Tel4tel
fonte
0

Melhorando a resposta @ Tel4tel e @cc, aqui está uma extensão com parâmetros e uma breve explicação.

extension UIView {

    // Perform a blur animation in the whole view
    // Effect tone can be .light, .dark, .regular...
    func blur(duration inSeconds: Double, effect tone: UIBlurEffectStyle) {
        let blurView = UIVisualEffectView(frame: self.bounds)
        self.addSubview(blurView)
        UIView.animate(withDuration: inSeconds) {
            blurView.effect = UIBlurEffect(style: tone)
        }
    }

    // Perform an unblur animation in the whole view
    func unblur(duration inSeconds: Double) {
        for childView in subviews {
            guard let effectView = childView as? UIVisualEffectView else { continue 
        }
            UIView.animate(withDuration: inSeconds, animations: {
                effectView.effect = nil
            }){
                didFinish in effectView.removeFromSuperview()
            }
        }
    }
}

Então você pode usá-lo como: view.blur(duration: 5.0, effect: .light) ou view.unblur(duration: 3.0)

Lembre-se de NÃO usá-lo, viewDidLoad()pois ele substituirá a animação. Além disso, ao executar em um Simulador, gire os gráficos para o nível Superior para poder ver a animação (Depurar> Substituição da Qualidade Gráfica> Alta Qualidade).

Rafael Díaz
fonte