Vista de desfoque estilo iOS 7

113

Alguém conhece algum controle que irá replicar as visualizações desfocadas no estilo iOS7?

Estou assumindo que pode haver algum tipo de subclasse UIView que irá replicar o comportamento.

Estou falando sobre essas visualizações de tipo que desfocam o fundo de forma extremamente espessa, de modo que têm efeitos de atração da visualização de fundo.

insira a descrição da imagem aqui

endy
fonte
6
GPUImage pode ajudar.
Maarten
@Anil bem, então a questão será quando o SDK do ios7 for lançado, como posso fazer isso sem restringir meu aplicativo apenas ao ios7;).
final de
1
A Apple lançou uma categoria UIImage que faz exatamente isso. Só estou tentando encontrar.
Fogmeister
1
O código está vinculado à sessão 201 da WWDC. É público no github, mas não consigo encontrar no atm.
Fogmeister

Respostas:

40

Você pode ser capaz de modificar algo como RWBlurPopover de Bin Zhang para fazer isso. Esse componente usa meu GPUImage para aplicar um desfoque Gaussiano aos componentes abaixo dele, mas você poderia facilmente usar um CIGaussianBlur para o mesmo. GPUImage pode ser um fio de cabelo mais rápido .

Esse componente depende de você ser capaz de capturar a visualização por trás do que você está apresentando, no entanto, e pode ter problemas com visualizações que são animadas por trás desse conteúdo. A necessidade de fazer uma viagem através do Core Graphics para rasterizar a visualização do plano de fundo tornará as coisas mais lentas, portanto, provavelmente não temos acesso direto o suficiente para ser capaz de fazer isso com desempenho para sobreposições em visualizações animadas.

Como uma atualização para o acima, eu recentemente retrabalhei os desfoque no GPUImage para oferecer suporte a raios variáveis, permitindo a replicação completa do tamanho do desfoque na visão central de controle do iOS 7. A partir disso, criei a classe GPUImageiOS7BlurFilter que encapsula o tamanho de desfoque adequado e a correção de cor que a Apple parece estar usando aqui. É assim que o desfoque do GPUImage (à direita) se compara ao desfoque integrado (à esquerda):

Borrão da maçã Desfoque de GPUImage

Eu uso um downsampling / upsampling 4X para reduzir o número de pixels sobre o qual o desfoque gaussiano tem que operar, então um iPhone 4S pode desfocar a tela inteira em cerca de 30 ms usando esta operação.

Você ainda tem o desafio de como colocar o conteúdo neste borrão a partir das visualizações por trás deste de maneira eficiente.

Brad Larson
fonte
Então, como você acha que a Apple está conseguindo? Se você abrir o aplicativo Câmera e puxar o Centro de Controle (a tela de configurações na parte inferior), o desfoque segue o que a Câmera vê.
Boneco de neve
2
@maq - Ajuda ter acesso por baixo do capô a tudo no sistema. O que alguém que desenvolve o sistema operacional pode fazer é muito diferente do que as interfaces públicas nos permitem fazer.
Brad Larson
3
@maq - Feeds de câmeras especificamente, sim. Uma maneira de fazer isso seria usar GPUImage para puxar os quadros da câmera (via AV Foundation) e, em seguida, ter essa saída para um GPUImageView para a saída da câmera ao vivo e, em seguida, ser alimentado em paralelo em um desfoque gaussiano (e possivelmente um corte para coincidir com a posição da visualização desfocada) e essa saída para outro GPUImageView que exibiria o conteúdo desfocado atrás de seus controles. Isso é possível porque temos um caminho rápido de quadros de câmera para OpenGL ES, mas não temos nada semelhante para elementos genéricos de IU.
Brad Larson
1
Estou tirando uma captura de tela de um UIView programaticamente e usando GPUImageGaussianBlurFilter para tentar criar um efeito semelhante, mas o resultado é muito diferente do estilo Apple. O filtro parece estar borrado em uma grade, com quadrados visíveis em todos os lugares, onde o da Apple é apenas uma folha lisa.
Boneco de neve
1
@maq - Certifique-se de estar usando o código mais recente do repositório, porque antes havia um bug em raios de grande desfoque. Dito isso, o desfoque apenas mostra uma área de 9 pixels por padrão e, portanto, se você aumentar o multiplicador do desfoque além desse ponto, começará a ver artefatos de pixels pulados. Isso é feito por motivos de desempenho, mas você pode modificar os sombreadores de vértice e fragmento para obter uma amostra de um número maior de pixels. Isso, ou tente aplicar um CIGaussianBlur, que tem uma implementação de blur mais generalizada. Um dia desses, estenderei esse desfoque para raios maiores.
Brad Larson
28

Eu estou usando FXBlurViewo que funciona muito bem em iOS5 +

https://github.com/nicklockwood/FXBlurView

CocoaPods:

-> FXBlurView (1.3.1)
   UIView subclass that replicates the iOS 7 realtime background blur effect, but works on iOS 5 and above.
   pod 'FXBlurView', '~> 1.3.1'
   - Homepage: http://github.com/nicklockwood/FXBlurView
   - Source:   https://github.com/nicklockwood/FXBlurView.git
   - Versions: 1.3.1, 1.3, 1.2, 1.1, 1.0 [master repo]

Eu adicionei usando:

FXBlurView *blurView = [[FXBlurView alloc] initWithFrame:CGRectMake(50, 50, 150, 150)];
[self.blurView setDynamic:YES];
[self.view addSubview:self.blurView];
Paul Peelen
fonte
2
Existe algum truque para usar o FXBlurView? Eu tentei, mas resultou em visualizações lentas em um iPhone 5. Seu projeto de demonstração funciona bem. Bastante estranho.
Cris
Depende do que você quer fazer. Quando coloquei FXBlurViewem cima de um UIScrollView, também obtive um resultado lento. Acredito que isso tenha a ver com a forma como o desfoque é adicionado. A Apple está, se não me engano, acessando diretamente a GPU ao usar seu próprio desfoque, o que não podemos fazer como desenvolvedores. Portanto, a solução @cprcrack é realmente a melhor solução aqui.
Paul Peelen
2
Como você lidou com as rotações do dispositivo com FXBlurView? Estou apresentando um diálogo modal no momento; a caixa de diálogo em si gira bem, mas o pano de fundo está espremido da maneira errada (basicamente, ele precisa ser atualizado após a rotação).
fatuhoku
1
claro, FXBlurView é uma boa opção, mas quando há questão de uso da CPU. do que eu prefiro outro blurView. Eu uso isso em meu UICollectionVIew e UITableView, mas aumenta meu uso de CPU. na próxima execução eu comento essas alocações e se torna normal. Espero que isso ajude alguém.
Vatsal Shukla
27

AVISO: alguém nos comentários afirmou que a Apple rejeita aplicativos que usam essa técnica. Isso NÃO aconteceu comigo, mas apenas para sua consideração.

Isso pode surpreendê-lo, mas você pode usar um UIToolbar , que já inclui esse efeito padrão (apenas iOS 7 ou 7). Na visualização do viewDidLoad do controlador:

self.view.opaque = NO;
self.view.backgroundColor = [UIColor clearColor]; // Be sure in fact that EVERY background in your view's hierarchy is totally or at least partially transparent for a kind effect!

UIToolbar *fakeToolbar = [[UIToolbar alloc] initWithFrame:self.view.bounds];
fakeToolbar.autoresizingMask = self.view.autoresizingMask;
// fakeToolbar.barTintColor = [UIColor white]; // Customize base color to a non-standard one if you wish
[self.view insertSubview:fakeToolbar atIndex:0]; // Place it below everything
cprcrack
fonte
1
Sim, me surpreendeu. Eu verifiquei, essa ideia funciona. Eu fiz apenas uma mudança de linha viewDidLoadassim. - (vazio) viewDidLoad {[super viewDidLoad]; self.view = [[UIToolbar alloc] initWithFrame: CGRectZero]; } No meu caso, eu faço o layout de minhas visualizações de maneira programática (estilo antigo). Eu votei positivamente nesta solução porque UIToolbar herda UIView, então, fundamentalmente, não vejo nenhum problema nesta solução. Vou testar mais à medida que vou desenvolvendo e testando essa solução.
Ashok
Esta é uma ótima ideia e parece funcionar muito bem. Gostaria de ter mais controle, mas isso é o melhor que pode acontecer! Dito isso, @SudhirJonathan mencionou um link acima onde o autor leva isso para o próximo nível - ELE ROUBA O CALAYER de um UIToolBar e o reaproveita! Brilhante!
David H
2
Realmente? Por quais motivos? Não está usando nenhuma API privada e, se todas as formas inovadoras de fazer as coisas fossem rejeitadas, muitos efeitos não seriam possíveis ...
TheEye
Trabalhou no meu iPhone iOS 7.1
março
Existe uma maneira de "diminuir" o borrão?
maxisme
13

Desde iOS8 você pode usar UIBlurEffect .

Existem bons exemplos no iOS8Sampler com UIBlurEffect e UIVibrancyEffect .

VivienCormier
fonte
2
Para os preguiçosos: UIVisualEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]; UIVisualEffectView *blurView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
Ric Santos
13

A melhor nova maneira de obter uma sobreposição manchada é usar o novo recurso UIVisualEffectView do iOS 8.

UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];

UIVisualEffectView *bluredView = [[UIVisualEffectView alloc] initWithEffect:effect];

bluredView.frame = self.view.bounds;

[self.view addSubview:bluredView];

O UIBlurEffect oferece suporte a três tipos de estilo. Dark, Light e ExtraLight.

Jeanette Müller
fonte
1

Você pode criar uma classe com um UIToolBar que é uma subclasse de UIView e instanciá-lo em um controlador de visualização separado. Essa abordagem demonstra um UIToolBar translúcido (subclasse de UIView) que fornece feedback ao vivo (neste caso, para uma AVCaptureSession).

YourUIView.h

#import <UIKit/UIKit.h>

@interface YourUIView : UIView
@property (nonatomic, strong) UIColor *blurTintColor;
@property (nonatomic, strong) UIToolbar *toolbar;
@end

YourUIView.m

#import "YourUIView.h"

@implementation YourUIView

- (instancetype)init
{
    self = [super init];
    if (self) {
        [self setup];
    }
    return self;
}

- (void)setup {
    // If we don't clip to bounds the toolbar draws a thin shadow on top
    [self setClipsToBounds:YES];

    if (![self toolbar]) {
        [self setToolbar:[[UIToolbar alloc] initWithFrame:[self bounds]]];
        [self.toolbar setTranslatesAutoresizingMaskIntoConstraints:NO];
        [self insertSubview:[self toolbar] atIndex:0];

        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_toolbar]|"
                                                                     options:0
                                                                     metrics:0
                                                                       views:NSDictionaryOfVariableBindings(_toolbar)]];
        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_toolbar]|"
                                                                     options:0
                                                                     metrics:0
                                                                       views:NSDictionaryOfVariableBindings(_toolbar)]];
    }
}

- (void) setBlurTintColor:(UIColor *)blurTintColor {
    [self.toolbar setBarTintColor:blurTintColor];
}

@end

Uma vez que o UIView acima tenha sido personalizado, vá em frente e crie uma classe que é uma subclasse de um ViewController. Abaixo, criei uma classe que está usando uma sessão AVCapture. Você deve usar AVCaptureSession para substituir a configuração de câmera embutida da apple. Assim, você pode sobrepor o UIToolBar translúcido da classe YourUIView .

YourViewController.h

#import <UIKit/UIKit.h>

@interface YourViewController : UIViewController
@property (strong, nonatomic) UIView *frameForCapture;
@end

YourViewController.m

#import "YourViewController.h"
#import <AVFoundation/AVFoundation.h>
#import "TestView.h"

@interface YourViewController ()
@property (strong, nonatomic) UIButton *displayToolBar;

@end

@implementation YourViewController


AVCaptureStillImageOutput *stillImageOutput;
AVCaptureSession *session;

- (void) viewWillAppear:(BOOL)animated
{
    session = [[AVCaptureSession alloc] init];
    [session setSessionPreset:AVCaptureSessionPresetPhoto];

    AVCaptureDevice *inputDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    NSError *error;
    AVCaptureDeviceInput *deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:inputDevice error:&error];

    if ([session canAddInput:deviceInput]) {
        [session addInput:deviceInput];
    }

    AVCaptureVideoPreviewLayer *previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
    [previewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
    CALayer *rootLayer = [[self view] layer];
    [rootLayer setMasksToBounds:YES];
    CGRect frame = [[UIScreen mainScreen] bounds];

    self.frameForCapture.frame = frame;

    [previewLayer setFrame:frame];

    [rootLayer insertSublayer:previewLayer atIndex:0];

    stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
    NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:AVVideoCodecJPEG, AVVideoCodecKey, nil];
    [stillImageOutput setOutputSettings:outputSettings];

    [session addOutput:stillImageOutput];

    [session startRunning];

    [self.navigationController setNavigationBarHidden:YES animated:animated];
    [super viewWillAppear:animated];
}


- (void)viewDidLoad
{
    [super viewDidLoad];

    /* Open button */

    UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 350, self.view.bounds.size.width, 50)];
    [button addTarget:self action:@selector(showYourUIView:) forControlEvents:UIControlEventTouchUpInside];
    [button setTitle:@"Open" forState:UIControlStateNormal];
    [button setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
    button.backgroundColor = [UIColor greenColor];
    [self.view addSubview:button];

    UIButton *anotherButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 50, self.view.bounds.size.width, 50)];
    [anotherButton addTarget:self action:@selector(showYourUIView:) forControlEvents:UIControlEventTouchUpInside];
    [anotherButton setTitle:@"Open" forState:UIControlStateNormal];
    [anotherButton setTitleColor:[UIColor greenColor] forState:UIControlStateNormal];
    anotherButton.backgroundColor = [UIColor redColor];
    [self.view addSubview:anotherButton];

}

- (void) showYourUIView:(id) sender
{
    TestView *blurView = [TestView new];
    [blurView setFrame:self.view.bounds];
    [self.view addSubview:blurView];
}


@end
74U n3U7r1no
fonte
A resposta parece não ter nenhuma relação com a pergunta.
combinatória de
@combinatorial Esta é uma das maneiras mais eficientes de adicionar uma visão transparente a outro controlador de visão. Incluí o código AVCapture para mostrar que ele poderia ser usado para feedback ao vivo, em vez de apenas adicionar uma imagem de fundo desfocada, como outros usuários recomendaram.
74U n3U7r1no
Ok, desculpas, você pode querer adicionar algo no início que resuma a abordagem e as razões para usá-la.
combinatória de
Obrigado @combinatorial
74U n3U7r1no