Widok rozmycia w stylu iOS 7

113

Czy ktoś zna jakieś elementy sterujące, które będą replikować widoki rozmycia w stylu iOS7.

Zakładam, że może istnieć podklasa UIView, która będzie replikować to zachowanie.

Mówię o tego typu widokach, które bardzo mocno rozmywają tło, tak że mają efekt pull z widoku tła.

wprowadź opis obrazu tutaj

endy
źródło
6
GPUImage może pomóc.
Maarten
@ No dobrze, wtedy pytanie będzie, kiedy pakiet SDK ios7 zostanie wydany, jak mogę to zrobić bez ograniczania mojej aplikacji tylko do ios7;).
koniec
1
Apple wydało kategorię UIImage, która robi dokładnie to. Po prostu próbuję go znaleźć.
Fogmeister,
1
Kod jest powiązany z sesją WWDC 201. Jest publiczny na githubie Nie mogę go znaleźć.
Fogmeister,

Odpowiedzi:

40

Aby to zrobić, możesz zmodyfikować coś takiego jak RWBlurPopover Bin Zhanga . Ten komponent używa mojego GPUImage, aby zastosować rozmycie gaussowskie do komponentów pod nim, ale równie łatwo możesz użyć CIGaussianBlur do tego samego. GPUImage może być jednak szybszy .

Ten komponent polega jednak na tym, że możesz uchwycić widok za tym, który prezentujesz, i może mieć problemy z widokami, które animują tę zawartość. Konieczność przejścia przez grafikę podstawową w celu rasteryzacji widoku tła spowolni działanie, więc prawdopodobnie nie mamy wystarczająco bezpośredniego dostępu, aby móc to zrobić w wydajny sposób dla nakładek na animowane widoki.

W ramach aktualizacji powyższego niedawno przerobiłem rozmycie w GPUImage, aby obsługiwać zmienne promienie, umożliwiając pełną replikację rozmiaru rozmycia w widoku centrum sterowania iOS 7. Na tej podstawie stworzyłem klasę GPUImageiOS7BlurFilter, która zawiera właściwy rozmiar rozmycia i korekcję kolorów, których Apple wydaje się używać tutaj. Oto jak rozmycie obrazu GPU (po prawej) porównuje się do rozmycia wbudowanego (po lewej):

Rozmycie jabłka Rozmycie GPUImage

Używam 4X downsampling / upsampling, aby zmniejszyć liczbę pikseli, na które musi działać rozmycie Gaussa, więc iPhone 4S może rozmazać cały ekran w około 30 ms używając tej operacji.

Nadal masz wyzwanie, jak w performatywny sposób wciągnąć treść w to rozmycie z widoków za tym.

Brad Larson
źródło
Jak myślisz, jak Apple to robi? Jeśli otworzysz aplikację Aparat i podciągniesz Centrum sterowania (ekran ustawień od dołu), rozmycie będzie zgodne z tym, co widzi kamera.
Snowman
2
@maq - Pomaga mieć pod maską dostęp do wszystkiego w systemie. To, co może zrobić ktoś, kto tworzy system operacyjny, różni się bardzo od tego, co pozwalają nam interfejsy publiczne.
Brad Larson
3
@maq - konkretnie obraz z kamery, tak. Jednym ze sposobów byłoby użycie GPUImage do wciągnięcia klatek kamery (przez AV Foundation), a następnie wysłanie tego zarówno do GPUImageView dla obrazu wyjściowego z kamery na żywo, a następnie równolegle do rozmycia gaussowskiego (i prawdopodobnie aby dopasować pozycję rozmytego widoku) i to wyjście do innego GPUImageView, który wyświetli niewyraźną zawartość za twoimi kontrolkami. Jest to możliwe, ponieważ mamy szybką ścieżkę od ramek aparatu do OpenGL ES, ale nie mamy nic podobnego dla ogólnych elementów UI.
Brad Larson
1
Robię zrzut ekranu UIView programowo i używam GPUImageGaussianBlurFilter, aby spróbować stworzyć podobny efekt, ale wynik jest bardzo różny od tego w stylu Apple. Filtr wydaje się rozmywać w siatce, z widocznymi kwadratami wszędzie, gdzie Apple to tylko jeden gładki arkusz.
Snowman
1
@maq - Upewnij się, że używasz najnowszego kodu z repozytorium, ponieważ wcześniej wystąpił błąd przy dużym promieniu rozmycia. To powiedziawszy, rozmycie domyślnie próbkuje tylko obszar 9-pikselowy, więc jeśli zwiększysz mnożnik rozmycia za tym punktem, zaczniesz widzieć artefakty pomijania pikseli. Odbywa się to ze względu na wydajność, ale można zmodyfikować shadery wierzchołków i fragmentów, aby próbkować większą liczbę pikseli. To lub spróbuj zastosować CIGaussianBlur, który ma bardziej uogólnioną implementację rozmycia. Któregoś dnia rozszerzę to rozmycie na większe promienie.
Brad Larson
28

Używam, FXBlurViewktóry działa świetnie na 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]

Dodałem go za pomocą:

FXBlurView *blurView = [[FXBlurView alloc] initWithFrame:CGRectMake(50, 50, 150, 150)];
[self.blurView setDynamic:YES];
[self.view addSubview:self.blurView];
Paul Peelen
źródło
2
Czy jest jakaś sztuczka w używaniu FXBlurView? Wypróbowałem to, ale zaowocowało to opóźnieniem w wyświetlaniu na iPhonie 5. Ich projekt demonstracyjny działa dobrze. Dość dziwne.
Kryzys
To zależy od tego, co chcesz robić. Kiedy kładłem się FXBlurViewna wierzchu, UIScrollViewmiałem również opóźniony wynik. Uważam, że ma to związek ze sposobem dodawania rozmycia. Apple, jeśli się nie mylę, uzyskuje bezpośredni dostęp do GPU, używając własnego rozmycia, czego jako programiści nie możemy zrobić. Dlatego rozwiązanie @cprcrack jest tutaj najlepszym rozwiązaniem.
Paul Peelen
2
Jak poradziłeś sobie z obrotami urządzeń za pomocą FXBlurView? W tej chwili przedstawiam modalne okno dialogowe; samo okno dialogowe obraca się dobrze, ale tło jest zgniecione w niewłaściwy sposób (zasadniczo należy je zaktualizować po obróceniu).
fatuhoku
1
Oczywiście FXBlurView jest dobrą opcją, ale jeśli chodzi o użycie procesora. niż wolę inne blurView. Używam tego w moim UICollectionVIew i UITableView, ale zwiększa to zużycie procesora. w następnym uruchomieniu komentuję te alokacje i staje się to normalne. mam nadzieję, że to komuś pomoże.
Vatsal Shukla,
27

UWAGA: ktoś w komentarzach stwierdził, że Apple odrzuca aplikacje używające tej techniki. Mnie się to NIE przydarzyło, ale tylko po to, by wziąć pod uwagę.

Może cię to zaskoczyć, ale możesz użyć UIToolbar , który już zawiera ten standardowy efekt (tylko iOS 7+). W Twoim widoku viewDidLoad kontrolera:

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
źródło
1
Tak, to mnie zaskoczyło. Sprawdziłem, ten pomysł działa. W ten sposób dokonałem tylko jednej zmiany linii viewDidLoad. - (void) viewDidLoad {[super viewDidLoad]; self.view = [[UIToolbar assign] initWithFrame: CGRectZero]; } W moim przypadku układam widoki programowo (w starym stylu). Głosuję za tym rozwiązaniem, ponieważ UIToolbar dziedziczy UIView, więc zasadniczo nie widzę żadnego problemu w tym rozwiązaniu. Będę testować więcej w miarę rozwoju i testowania tego rozwiązania.
Ashok
To świetny pomysł i wydaje się, że działa naprawdę dobrze. Chciałbym mieć większą kontrolę, ale to jest tak dobre, jak to tylko możliwe! To powiedziawszy, @SudhirJonathan wspomniał o powyższym linku, w którym autor przenosi to na wyższy poziom - KRADZIE CALAYER z UIToolBar i ponownie go wykorzystuje! Znakomity!
David H
2
Naprawdę? Na jakiej podstawie? Nie używa żadnego prywatnego API, a gdyby wszystkie innowacyjne sposoby robienia rzeczy zostały odrzucone, wiele efektów nie byłoby możliwych ...
TheEye
Pracowałem na moim iPhonie z iOS 7.1
marszałek
Czy istnieje sposób, aby „zgasić” rozmycie?
maxisme
13

Od iOS8 możesz używać UIBlurEffect .

Istnieją dobre przykłady na iOS8Sampler z UIBlurEffect i UIVibrancyEffect .

VivienCormier
źródło
2
Dla leniwych: UIVisualEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]; UIVisualEffectView *blurView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
Ric Santos
13

Najlepszym nowym sposobem na uzyskanie rozmytej nakładki jest użycie nowej funkcji iOS 8 UIVisualEffectView.

UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];

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

bluredView.frame = self.view.bounds;

[self.view addSubview:bluredView];

UIBlurEffect obsługuje trzy rodzaje stylów. Ciemne, jasne i bardzo lekkie.

Jeanette Müller
źródło
1

Możesz utworzyć klasę z UIToolBar, która jest podklasą UIView i utworzyć jej wystąpienie w osobnym kontrolerze widoku. To podejście przedstawia półprzezroczysty UIToolBar (podklasę podklasy przez UIView), który zapewnia informacje zwrotne na żywo (w tym przypadku dla 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

Po dostosowaniu powyższego UIView przejdź dalej i utwórz klasę, która jest podklasą ViewController. Poniżej utworzyłem klasę, która używa sesji AVCapture. Musisz użyć AVCaptureSession, aby nadpisać wbudowaną konfigurację aparatu Apple. W ten sposób możesz nałożyć przezroczysty UIToolBar z klasy 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
źródło
Odpowiedź wydaje się zupełnie niezwiązana z pytaniem.
kombinatoryczne
@combinatorial Jest to jeden z bardziej wydajnych sposobów dodawania przezroczystego widoku do innego kontrolera widoku. Dołączyłem kod AVCapture, aby pokazać, że można go użyć do informacji zwrotnej na żywo, zamiast po prostu dodawać rozmyty obraz tła, jak zalecali inni użytkownicy.
74U n3U7r1no
Ok, przepraszam, na początku możesz dodać coś, co podsumowuje podejście i powody, dla których go używasz.
kombinatoryczne
@combinatorial Thanks
74U n3U7r1no