it-swarm.com.de

Wie kann ich einen ähnlichen Effekt wie in der iOS 7-Unschärfeansicht erzielen?

Ich versuche, diesen unscharfen Hintergrund von Apples öffentlich veröffentlichtem iOS 7-Beispielbildschirm zu replizieren:

iOS 7 Control Center screenshot

Diese Frage schlägt vor, einen CI-Filter auf die folgenden Inhalte anzuwenden, aber das ist ein ganz anderer Ansatz. Es ist offensichtlich, dass iOS 7 aus verschiedenen Gründen den Inhalt der folgenden Ansichten nicht erfasst:

  1. Das Durchführen einiger grober Tests, das Aufnehmen eines Screenshots der folgenden Ansichten und das Anwenden eines CIGaussianBlur-Filters mit einem Radius, der groß genug ist, um den Unschärfestil von iOS 7 nachzuahmen, dauert selbst in einem Simulator 1-2 Sekunden.
  2. Die iOS 7-Weichzeichnungsansicht kann dynamische Ansichten, wie z. B. Videos oder Animationen, ohne nennenswerte Verzögerung weichzeichnen.

Kann jemand eine Hypothese aufstellen, welche Frameworks verwendet werden könnten, um diesen Effekt zu erzielen, und ob es möglich ist, mit aktuellen öffentlichen APIs einen ähnlichen Effekt zu erzielen?

Edit: (aus Kommentar) Wir wissen nicht genau, wie Apple tut es, aber gibt es eine grundlegende Annahmen, die wir machen können? Wir können davon ausgehen, dass sie Hardware verwenden, oder?

Ist der Effekt in jeder Ansicht eigenständig, sodass der Effekt tatsächlich nicht weiß, was dahintersteckt? Oder muss basierend auf der Funktionsweise von Unschärfen der hinter der Unschärfe stehende Inhalt berücksichtigt werden?

Wenn der Inhalt hinter dem Effekt relevant ist, können wir davon ausgehen, dass Apple empfängt einen "Feed" des Inhalts unten und rendert sie kontinuierlich mit einer Unschärfe?

218
Snowman

Warum sollte man sich die Mühe machen, den Effekt zu wiederholen? Zeichnen Sie einfach eine UIToolbar hinter Ihre Ansicht.

myView.backgroundColor = [UIColor clearColor];
UIToolbar* bgToolbar = [[UIToolbar alloc] initWithFrame:myView.frame];
bgToolbar.barStyle = UIBarStyleDefault;
[myView.superview insertSubview:bgToolbar belowSubview:myView];
133
user2342340

Apple hat bei WWDC Code als Kategorie auf UIImage veröffentlicht, der diese Funktionalität enthält. Wenn Sie über ein Entwicklerkonto verfügen, können Sie die UIImage-Kategorie (und den Rest des Beispielcodes) unter folgendem Link aufrufen: https: // developer.Apple.com/wwdc/schedule/ und suchen Sie nach Abschnitt 226 und klicken Sie auf Details. Ich habe noch nicht damit herumgespielt, aber ich denke, dass der Effekt unter iOS 6 viel langsamer sein wird. Es gibt einige Verbesserungen an iOS 7, die das Aufnehmen des ersten Screenshots, der als Eingabe für die Unschärfe verwendet wird, viel schneller machen.

Direkter Link: https://developer.Apple.com/downloads/download.action?path=wwdc_2013/wwdc_2013_sample_code/ios_uiimageeffects.Zip

64
FreaknBigPanda

Eigentlich würde ich wetten, dass dies ziemlich einfach zu erreichen wäre. Es würde wahrscheinlich nicht funktionieren oder genau so aussehen wie das, was Apple los ist, könnte aber sehr nahe sein.

Zunächst müssen Sie das CGRect der UIView bestimmen, die Sie präsentieren werden. Sobald Sie festgestellt haben, dass Sie nur ein Bild des Teils der Benutzeroberfläche aufnehmen müssen, damit es unscharf wird. Etwas wie das...

- (UIImage*)getBlurredImage {
    // You will want to calculate this in code based on the view you will be presenting.
    CGSize size = CGSizeMake(200,200);

    UIGraphicsBeginImageContext(size);
    [view drawViewHierarchyInRect:(CGRect){CGPointZero, w, h} afterScreenUpdates:YES]; // view is the view you are grabbing the screen shot of. The view that is to be blurred.
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    // Gaussian Blur
    image = [image applyLightEffect];

    // Box Blur
    // image = [image boxblurImageWithBlur:0.2f];

    return image;
}

Gaußsche Unschärfe - Empfohlen

Wenn Sie die von Apple bereitgestellte Kategorie UIImage+ImageEffects Verwenden hier , erhalten Sie eine Gaußsche Unschärfe, die der Unschärfe in iOS 7 sehr ähnlich sieht.

Box Unschärfe

Sie können auch eine Rahmenunschärfe mit der folgenden UIImage-Kategorie boxBlurImageWithBlur: Verwenden. Dies basiert auf einem Algorithmus, den Sie finden können hier .

@implementation UIImage (Blur)

-(UIImage *)boxblurImageWithBlur:(CGFloat)blur {
    if (blur < 0.f || blur > 1.f) {
        blur = 0.5f;
    }
    int boxSize = (int)(blur * 50);
    boxSize = boxSize - (boxSize % 2) + 1;

    CGImageRef img = self.CGImage;

    vImage_Buffer inBuffer, outBuffer;

    vImage_Error error;

    void *pixelBuffer;

    CGDataProviderRef inProvider = CGImageGetDataProvider(img);
    CFDataRef inBitmapData = CGDataProviderCopyData(inProvider);

    inBuffer.width = CGImageGetWidth(img);
    inBuffer.height = CGImageGetHeight(img);
    inBuffer.rowBytes = CGImageGetBytesPerRow(img);

    inBuffer.data = (void*)CFDataGetBytePtr(inBitmapData);

    pixelBuffer = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img));

    if(pixelBuffer == NULL)
        NSLog(@"No pixelbuffer");

    outBuffer.data = pixelBuffer;
    outBuffer.width = CGImageGetWidth(img);
    outBuffer.height = CGImageGetHeight(img);
    outBuffer.rowBytes = CGImageGetBytesPerRow(img);

    error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);

    if (error) {
        NSLog(@"JFDepthView: error from convolution %ld", error);
    }

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef ctx = CGBitmapContextCreate(outBuffer.data,
                                         outBuffer.width,
                                         outBuffer.height,
                                         8,
                                         outBuffer.rowBytes,
                                         colorSpace,
                                         kCGImageAlphaNoneSkipLast);
    CGImageRef imageRef = CGBitmapContextCreateImage (ctx);
    UIImage *returnImage = [UIImage imageWithCGImage:imageRef];

    //clean up
    CGContextRelease(ctx);
    CGColorSpaceRelease(colorSpace);

    free(pixelBuffer);
    CFRelease(inBitmapData);

    CGImageRelease(imageRef);

    return returnImage;
}

@end

Nachdem Sie den zu verwischenden Bildschirmbereich berechnet, in die Kategorie "Verwischen" überführt und ein UIImage zurückerhalten haben, müssen Sie das verwischte Bild nur noch als Hintergrund für die Ansicht festlegen, die Sie präsentieren möchten. Wie ich schon sagte, dies wird nicht perfekt zu dem passen, was Apple tut, aber es sollte trotzdem ziemlich cool aussehen.

Ich hoffe es hilft.

37
Jeremy Fox

iOS8 hat diese Fragen beantwortet.

UIVisualEffect

- (instancetype)initWithEffect:(UIVisualEffect *)effect

oder Swift:

init(effect effect: UIVisualEffect)

24
Adam Waite

Ich habe gerade meine kleine Unterklasse von UIView geschrieben, die in der Lage ist, native iOS 7-Unschärfen in jeder benutzerdefinierten Ansicht zu erzeugen. Es verwendet UIToolbar, aber auf sichere Weise können Rahmen, Grenzen, Farbe und Alpha mithilfe von Echtzeitanimationen geändert werden.

Bitte lassen Sie mich wissen, wenn Sie Probleme bemerken.

https://github.com/ivoleko/ILTranslucentView

ILTranslucentView examples

19
Ivo Leko

Es gibt ein Gerücht, dass Apple Ingenieure behaupteten, um diese Performance zu erreichen, lesen sie direkt aus dem GPU-Puffer, was Sicherheitsprobleme aufwirft. Aus diesem Grund gibt es noch keine öffentliche API, um dies zu tun.

10
Cody C

Dies ist eine Lösung, die Sie in den Videos des WWDC sehen können. Sie müssen eine Gaußsche Unschärfe machen, also müssen Sie zuerst eine neue .m- und .h-Datei mit dem Code, den ich hier schreibe, hinzufügen, dann müssen Sie einen Screenshot machen, den gewünschten Effekt verwenden und Fügen Sie es Ihrer Ansicht hinzu, dann Ihrer UITable UIView oder was auch immer transparent sein muss, Sie können mit applyBlurWithRadius spielen, um den gewünschten Effekt zu archivieren. Dieser Aufruf funktioniert mit jedem UIImage.

Am Ende ist das verwaschene Bild der Hintergrund und der Rest der Steuerelemente muss transparent sein.

Damit dies funktioniert, müssen Sie die nächsten Bibliotheken hinzufügen:

Acelerate.framework, UIKit.framework, CoreGraphics.framework

Ich hoffe du magst es.

Fröhliches Codieren.

    //Screen capture.
    UIGraphicsBeginImageContext(self.view.bounds.size);

    CGContextRef c = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(c, 0, 0);
    [self.view.layer renderInContext:c];

    UIImage* viewImage = UIGraphicsGetImageFromCurrentImageContext();
    viewImage = [viewImage applyLightEffect];

    UIGraphicsEndImageContext();

    //.h FILE
    #import <UIKit/UIKit.h>

    @interface UIImage (ImageEffects)

   - (UIImage *)applyLightEffect;
   - (UIImage *)applyExtraLightEffect;
   - (UIImage *)applyDarkEffect;
   - (UIImage *)applyTintEffectWithColor:(UIColor *)tintColor;

   - (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage;

   @end

    //.m FILE
    #import "cGaussianEffect.h"
    #import <Accelerate/Accelerate.h>
    #import <float.h>


     @implementation UIImage (ImageEffects)


    - (UIImage *)applyLightEffect
    {
        UIColor *tintColor = [UIColor colorWithWhite:1.0 alpha:0.3];
        return [self applyBlurWithRadius:1 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
    }


    - (UIImage *)applyExtraLightEffect
    {
        UIColor *tintColor = [UIColor colorWithWhite:0.97 alpha:0.82];
        return [self applyBlurWithRadius:1 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
    }


    - (UIImage *)applyDarkEffect
    {
        UIColor *tintColor = [UIColor colorWithWhite:0.11 alpha:0.73];
        return [self applyBlurWithRadius:1 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
    }


    - (UIImage *)applyTintEffectWithColor:(UIColor *)tintColor
    {
        const CGFloat EffectColorAlpha = 0.6;
        UIColor *effectColor = tintColor;
        int componentCount = CGColorGetNumberOfComponents(tintColor.CGColor);
        if (componentCount == 2) {
            CGFloat b;
            if ([tintColor getWhite:&b alpha:NULL]) {
                effectColor = [UIColor colorWithWhite:b alpha:EffectColorAlpha];
            }
        }
        else {
            CGFloat r, g, b;
            if ([tintColor getRed:&r green:&g blue:&b alpha:NULL]) {
                effectColor = [UIColor colorWithRed:r green:g blue:b alpha:EffectColorAlpha];
            }
        }
        return [self applyBlurWithRadius:10 tintColor:effectColor saturationDeltaFactor:-1.0 maskImage:nil];
    }


    - (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage
    {
        if (self.size.width < 1 || self.size.height < 1) {
            NSLog (@"*** error: invalid size: (%.2f x %.2f). Both dimensions must be >= 1: %@", self.size.width, self.size.height, self);
            return nil;
        }
        if (!self.CGImage) {
            NSLog (@"*** error: image must be backed by a CGImage: %@", self);
            return nil;
        }
        if (maskImage && !maskImage.CGImage) {
            NSLog (@"*** error: maskImage must be backed by a CGImage: %@", maskImage);
            return nil;
        }

        CGRect imageRect = { CGPointZero, self.size };
        UIImage *effectImage = self;

        BOOL hasBlur = blurRadius > __FLT_EPSILON__;
        BOOL hasSaturationChange = fabs(saturationDeltaFactor - 1.) > __FLT_EPSILON__;
        if (hasBlur || hasSaturationChange) {
            UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
            CGContextRef effectInContext = UIGraphicsGetCurrentContext();
            CGContextScaleCTM(effectInContext, 1.0, -1.0);
            CGContextTranslateCTM(effectInContext, 0, -self.size.height);
            CGContextDrawImage(effectInContext, imageRect, self.CGImage);

            vImage_Buffer effectInBuffer;
            effectInBuffer.data     = CGBitmapContextGetData(effectInContext);
            effectInBuffer.width    = CGBitmapContextGetWidth(effectInContext);
            effectInBuffer.height   = CGBitmapContextGetHeight(effectInContext);
            effectInBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectInContext);

            UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
            CGContextRef effectOutContext = UIGraphicsGetCurrentContext();
            vImage_Buffer effectOutBuffer;
            effectOutBuffer.data     = CGBitmapContextGetData(effectOutContext);
            effectOutBuffer.width    = CGBitmapContextGetWidth(effectOutContext);
            effectOutBuffer.height   = CGBitmapContextGetHeight(effectOutContext);
            effectOutBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectOutContext);

            if (hasBlur) {
                CGFloat inputRadius = blurRadius * [[UIScreen mainScreen] scale];
                NSUInteger radius = floor(inputRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5);
                if (radius % 2 != 1) {
                    radius += 1;
                }
                vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
                vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
                vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
            }
            BOOL effectImageBuffersAreSwapped = NO;
            if (hasSaturationChange) {
                CGFloat s = saturationDeltaFactor;
                CGFloat floatingPointSaturationMatrix[] = {
                    0.0722 + 0.9278 * s,  0.0722 - 0.0722 * s,  0.0722 - 0.0722 * s,  0,
                    0.7152 - 0.7152 * s,  0.7152 + 0.2848 * s,  0.7152 - 0.7152 * s,  0,
                    0.2126 - 0.2126 * s,  0.2126 - 0.2126 * s,  0.2126 + 0.7873 * s,  0,
                                  0,                    0,                    0,  1,
                };
                const int32_t divisor = 256;
                NSUInteger matrixSize = sizeof(floatingPointSaturationMatrix)/sizeof(floatingPointSaturationMatrix[0]);
                int16_t saturationMatrix[matrixSize];
                for (NSUInteger i = 0; i < matrixSize; ++i) {
                    saturationMatrix[i] = (int16_t)roundf(floatingPointSaturationMatrix[i] * divisor);
                }
                if (hasBlur) {
                    vImageMatrixMultiply_ARGB8888(&effectOutBuffer, &effectInBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags);
                    effectImageBuffersAreSwapped = YES;
                }
                else {
                    vImageMatrixMultiply_ARGB8888(&effectInBuffer, &effectOutBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags);
                }
            }
            if (!effectImageBuffersAreSwapped)
                effectImage = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();

            if (effectImageBuffersAreSwapped)
                effectImage = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
        }

        UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
        CGContextRef outputContext = UIGraphicsGetCurrentContext();
        CGContextScaleCTM(outputContext, 1.0, -1.0);
        CGContextTranslateCTM(outputContext, 0, -self.size.height);

        CGContextDrawImage(outputContext, imageRect, self.CGImage);

        if (hasBlur) {
            CGContextSaveGState(outputContext);
            if (maskImage) {
                CGContextClipToMask(outputContext, imageRect, maskImage.CGImage);
            }
            CGContextDrawImage(outputContext, imageRect, effectImage.CGImage);
            CGContextRestoreGState(outputContext);
        }

        if (tintColor) {
            CGContextSaveGState(outputContext);
            CGContextSetFillColorWithColor(outputContext, tintColor.CGColor);
            CGContextFillRect(outputContext, imageRect);
            CGContextRestoreGState(outputContext);
        }

        UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();

        return outputImage;
    }

Sie können Ihre Lösung von Apples DEMO auf dieser Seite finden: WWDC 201 , finden Sie den UIImageEffects-Beispielcode heraus und laden Sie ihn herunter.

Dann mit @ Jeremy Foxs Code. Ich habe es in geändert

- (UIImage*)getDarkBlurredImageWithTargetView:(UIView *)targetView
{
    CGSize size = targetView.frame.size;

    UIGraphicsBeginImageContext(size);
    CGContextRef c = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(c, 0, 0);
    [targetView.layer renderInContext:c]; // view is the view you are grabbing the screen shot of. The view that is to be blurred.
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return [image applyDarkEffect];
}

Hoffe das wird dir helfen.

7
Senry

Das geht ganz einfach: https://github.com/JagCesar/iOS-blur

Kopieren Sie einfach die Ebene von UIToolbar und Sie haben es geschafft, AMBlurView erledigt es für Sie. Okay, es ist nicht so verschwommen wie das Kontrollzentrum, aber es ist genug verschwommen.

Denken Sie daran, dass iOS7 unter NDA steht.

7
Simon

In jeder Antwort wird vImageBoxConvolve_ARGB8888 verwendet. Diese Funktion ist sehr, sehr langsam. Das ist in Ordnung, wenn die Leistung keine hohe Priorität hat, aber Sie Wenn Sie dies zum Wechseln zwischen zwei View Controllern verwenden (z. B.), bedeutet dieser Ansatz, dass die Benutzererfahrung Ihrer Anwendung länger als 1 Sekunde ist.

Wenn Sie es vorziehen, die gesamte Bildverarbeitung der GPU zu überlassen (und Sie sollten es tun), können Sie einen viel besseren Effekt erzielen und auch fantastische Zeiten von rund 50 ms erzielen (vorausgesetzt, Sie haben beim ersten Versuch eine Zeit von 1 Sekunde) .

Laden Sie zuerst das GPUImage Framework (BSD Licensed) hier herunter.

Fügen Sie als Nächstes die folgenden Klassen (.m und .h) aus dem GPUImage hinzu (ich bin nicht sicher, ob dies das Minimum ist, das nur für den Weichzeichnungseffekt benötigt wird).

  • GPUImage.h
  • GPUImageAlphaBlendFilter
  • GPUImageFilter
  • GPUImageFilterGroup
  • GPUImageGaussianBlurPositionFilter
  • GPUImageGaussianSelectiveBlurFilter
  • GPUImageLuminanceRangeFilter
  • GPUImageOutput
  • GPUImageTwoInputFilter
  • GLProgram
  • GPUImageBoxBlurFilter
  • GPUImageGaussianBlurFilter
  • GPUImageiOSBlurFilter
  • GPUImageSaturationFilter
  • GPUImageSolidColorGenerator
  • GPUImageTwoPassFilter
  • GPUImageTwoPassTextureSamplingFilter

  • iOS/GPUImage-Prefix.pch

  • iOS/GPUImageContext
  • iOS/GPUImageMovieWriter
  • iOS/GPUImagePicture
  • iOS/GPUImageView

Erstellen Sie als Nächstes eine Kategorie in UIImage, die einem vorhandenen UIImage einen Weichzeichnungseffekt hinzufügt:

#import "UIImage+Utils.h"

#import "GPUImagePicture.h"
#import "GPUImageSolidColorGenerator.h"
#import "GPUImageAlphaBlendFilter.h"
#import "GPUImageBoxBlurFilter.h"

@implementation UIImage (Utils)

- (UIImage*) GPUBlurredImage
{
    GPUImagePicture *source =[[GPUImagePicture alloc] initWithImage:self];

    CGSize size = CGSizeMake(self.size.width * self.scale, self.size.height * self.scale);

    GPUImageBoxBlurFilter *blur = [[GPUImageBoxBlurFilter alloc] init];
    [blur setBlurRadiusInPixels:4.0f];
    [blur setBlurPasses:2.0f];
    [blur forceProcessingAtSize:size];
    [source addTarget:blur];

    GPUImageSolidColorGenerator * white = [[GPUImageSolidColorGenerator alloc] init];

    [white setColorRed:1.0f green:1.0f blue:1.0f alpha:0.1f];
    [white forceProcessingAtSize:size];

    GPUImageAlphaBlendFilter * blend = [[GPUImageAlphaBlendFilter alloc] init];
    blend.mix = 0.9f;

    [blur addTarget:blend];
    [white addTarget:blend];

    [blend forceProcessingAtSize:size];
    [source processImage];

    return [blend imageFromCurrentlyProcessedOutput];
}

@end

Zuletzt fügen Sie Ihrem Projekt die folgenden Frameworks hinzu:

AVFoundation CoreMedia CoreVideo OpenGLES

Ja, hab Spaß mit diesem viel schnelleren Ansatz;)

6
D33pN16h7

Sie können versuchen, meine benutzerdefinierte Ansicht zu verwenden, mit der der Hintergrund unscharf dargestellt werden kann. Dies geschieht, indem vorgetäuschte Schnappschüsse des Hintergrunds erstellt und verwischt werden, genau wie im WWDC-Code von Apple. Es ist sehr einfach zu bedienen.

Ich habe auch einige Verbesserungen vorgenommen, um die dynamische Unschärfe vorzutäuschen, ohne die Leistung zu beeinträchtigen. Der Hintergrund meiner Ansicht ist eine Bildlaufansicht, die mit der Ansicht einen Bildlauf durchführt und somit den Unschärfeeffekt für den Rest der Übersicht bereitstellt.

Siehe das Beispiel und den Code auf mein GitHub

4
Byte

Core Background implementiert den gewünschten iOS 7-Effekt.

https://github.com/justinmfischer/core-background

Haftungsausschluss: Ich bin der Autor dieses Projekts

2
Justin Fischer