webentwicklung-frage-antwort-db.com.de

Form mit Animation ausschneiden

Ich möchte etwas Ähnliches machen:

Wie kann ich ein Bild in IOS sdk maskieren?

Ich möchte den gesamten Bildschirm mit durchscheinendem Schwarz abdecken. Dann möchte ich einen Kreis aus der durchscheinenden schwarzen Abdeckung herausschneiden, damit Sie klar durchschauen können. Ich mache das, um Teile des Bildschirms für ein Tutorial hervorzuheben.

Ich möchte dann den ausgeschnittenen Kreis zu anderen Teilen des Bildschirms animieren. Ich möchte auch in der Lage sein, den ausgeschnittenen Kreis horizontal und vertikal zu strecken, wie Sie es bei einem generischen Schaltflächenhintergrund tun würden.

28
ma11hew28

(UPDATE: Bitte sehen Sie auch meine andere Antwort , die beschreibt, wie Sie mehrere unabhängige, sich überlappende Löcher einrichten.)

Wir verwenden einen einfachen alten UIViewmit einem backgroundColorvon durchscheinendem Schwarz und geben seiner Ebene eine Maske, die ein Loch aus der Mitte schneidet. Wir benötigen eine Instanzvariable, um auf die Bohrungsansicht zu verweisen:

@implementation ViewController {
    UIView *holeView;
}

Nach dem Laden der Hauptansicht möchten wir die Bohrungsansicht als Unteransicht hinzufügen:

- (void)viewDidLoad {
    [super viewDidLoad];
    [self addHoleSubview];
}

Da wir das Loch verschieben möchten, ist es sinnvoll, die Lochansicht sehr groß zu gestalten, sodass der Rest des Inhalts unabhängig von seiner Position abgedeckt wird. Wir werden es 10000x10000 machen. (Dies nimmt keinen Speicherplatz mehr in Anspruch, da iOS der Ansicht nicht automatisch eine Bitmap zuordnet.)

- (void)addHoleSubview {
    holeView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10000, 10000)];
    holeView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.5];
    holeView.autoresizingMask = 0;
    [self.view addSubview:holeView];
    [self addMaskToHoleView];
}

Jetzt müssen wir die Maske hinzufügen, die ein Loch aus der Lochansicht herausschneidet. Dazu erstellen wir einen zusammengesetzten Pfad, der aus einem großen Rechteck mit einem kleineren Kreis in der Mitte besteht. Wir füllen den Pfad mit Schwarz, sodass der Kreis nicht ausgefüllt und daher transparent bleibt. Der schwarze Teil hat Alpha = 1,0, sodass die Hintergrundfarbe der Lochansicht angezeigt wird. Der transparente Teil hat alpha = 0,0, sodass der Teil der Lochansicht ebenfalls transparent ist.

- (void)addMaskToHoleView {
    CGRect bounds = holeView.bounds;
    CAShapeLayer *maskLayer = [CAShapeLayer layer];
    maskLayer.frame = bounds;
    maskLayer.fillColor = [UIColor blackColor].CGColor;

    static CGFloat const kRadius = 100;
    CGRect const circleRect = CGRectMake(CGRectGetMidX(bounds) - kRadius,
        CGRectGetMidY(bounds) - kRadius,
        2 * kRadius, 2 * kRadius);
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:circleRect];
    [path appendPath:[UIBezierPath bezierPathWithRect:bounds]];
    maskLayer.path = path.CGPath;
    maskLayer.fillRule = kCAFillRuleEvenOdd;

    holeView.layer.mask = maskLayer;
}

Beachten Sie, dass ich den Kreis in die Mitte der 10000x10000-Ansicht gestellt habe. Dies bedeutet, dass wir einfach holeView.center setzen können, um den Mittelpunkt des Kreises relativ zum anderen Inhalt festzulegen. So können wir es beispielsweise leicht über die Hauptansicht auf und ab animieren:

- (void)viewDidLayoutSubviews {
    CGRect const bounds = self.view.bounds;
    holeView.center = CGPointMake(CGRectGetMidX(bounds), 0);

    // Defer this because `viewDidLayoutSubviews` can happen inside an
    // autorotation animation block, which overrides the duration I set.
    dispatch_async(dispatch_get_main_queue(), ^{
        [UIView animateWithDuration:2 delay:0
            options:UIViewAnimationOptionRepeat
                | UIViewAnimationOptionAutoreverse
            animations:^{
                holeView.center = CGPointMake(CGRectGetMidX(bounds),
                    CGRectGetMaxY(bounds));
            } completion:nil];
    });
}

So sieht es aus:

hole animation

Aber im wirklichen Leben ist es ruhiger.

Sie finden ein komplettes Arbeitstestprojekt in diesem Github-Repository .

58
rob mayoff

Dies ist nicht einfach. Ich kann dir einen guten Weg dorthin bringen. Das Animieren ist knifflig. Hier ist die Ausgabe eines Codes, den ich zusammengeworfen habe:

Inverted Mask Layer

Der Code ist wie folgt:

- (void)viewDidLoad
{
  [super viewDidLoad];

  // Create a containing layer and set it contents with an image
  CALayer *containerLayer = [CALayer layer];
  [containerLayer setBounds:CGRectMake(0.0f, 0.0f, 500.0f, 320.0f)];
  [containerLayer setPosition:[[self view] center]];
  UIImage *image = [UIImage imageNamed:@"cool"];
  [containerLayer setContents:(id)[image CGImage]];

  // Create your translucent black layer and set its opacity
  CALayer *translucentBlackLayer = [CALayer layer];
  [translucentBlackLayer setBounds:[containerLayer bounds]];
  [translucentBlackLayer setPosition:
                     CGPointMake([containerLayer bounds].size.width/2.0f, 
                                 [containerLayer bounds].size.height/2.0f)];
  [translucentBlackLayer setBackgroundColor:[[UIColor blackColor] CGColor]];
  [translucentBlackLayer setOpacity:0.45];
  [containerLayer addSublayer:translucentBlackLayer];

  // Create a mask layer with a shape layer that has a circle path
  CAShapeLayer *maskLayer = [CAShapeLayer layer];
  [maskLayer setBorderColor:[[UIColor purpleColor] CGColor]];
  [maskLayer setBorderWidth:5.0f];
  [maskLayer setBounds:[containerLayer bounds]];

  // When you create a path, remember that Origin is in upper left hand
  // corner, so you have to treat it as if it has an anchor point of 0.0, 
  // 0.0
  UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:
        CGRectMake([translucentBlackLayer bounds].size.width/2.0f - 100.0f, 
                   [translucentBlackLayer bounds].size.height/2.0f - 100.0f, 
                   200.0f, 200.0f)];

  // Append a rectangular path around the mask layer so that
  // we can use the even/odd fill rule to invert the mask
  [path appendPath:[UIBezierPath bezierPathWithRect:[maskLayer bounds]]];

  // Set the path's fill color since layer masks depend on alpha
  [maskLayer setFillColor:[[UIColor blackColor] CGColor]];
  [maskLayer setPath:[path CGPath]];

  // Center the mask layer in the translucent black layer
  [maskLayer setPosition:
                CGPointMake([translucentBlackLayer bounds].size.width/2.0f, 
                            [translucentBlackLayer bounds].size.height/2.0f)];

  // Set the fill rule to even odd
  [maskLayer setFillRule:kCAFillRuleEvenOdd];
  // Set the translucent black layer's mask property
  [translucentBlackLayer setMask:maskLayer];

  // Add the container layer to the view so we can see it
  [[[self view] layer] addSublayer:containerLayer];
}

Sie müssten die Maskenebene animieren, die Sie auf der Grundlage von Benutzereingaben aufbauen könnten, aber dies ist etwas schwierig. Beachten Sie die Linien, an denen ich einen rechteckigen Pfad an den Kreispfad anhänge, und dann die Füllregel einige Zeilen später in der Formebene festlegen. Dies macht die invertierte Maske möglich. Wenn Sie diese weglassen, zeigen Sie stattdessen das durchscheinende Schwarz in der Mitte des Kreises und dann nichts auf dem äußeren Teil (falls dies sinnvoll ist).

Versuchen Sie vielleicht, ein bisschen mit diesem Code zu spielen und sehen Sie, ob Sie ihn animieren können. Ich werde ein bisschen mehr damit spielen, da ich Zeit habe, aber das ist ein ziemlich interessantes Problem. Würde gerne eine Komplettlösung sehen.


UPDATE: Hier ist ein weiterer Stich. Das Problem hierbei ist, dass diese Maske die durchscheinende Maske weiß statt schwarz erscheinen lässt, aber der Vorteil ist, dass der Kreis ziemlich leicht animiert werden kann.

Diese erstellt eine zusammengesetzte Ebene, wobei die durchscheinende Ebene und die Kreisebene Geschwister innerhalb einer übergeordneten Ebene sind, die als Maske verwendet wird.

Composite Mask

Ich habe dieser Animation eine grundlegende Animation hinzugefügt, damit wir die Kreisebene animieren können.

- (void)viewDidLoad
{
  [super viewDidLoad];

  CGRect baseRect = CGRectMake(0.0f, 0.0f, 500.0f, 320.0f);

  CALayer *containerLayer = [CALayer layer];
  [containerLayer setBounds:baseRect];
  [containerLayer setPosition:[[self view] center]];

  UIImage *image = [UIImage imageNamed:@"cool"];
  [containerLayer setContents:(id)[image CGImage]];

  CALayer *compositeMaskLayer = [CALayer layer];
  [compositeMaskLayer setBounds:baseRect];
  [compositeMaskLayer setPosition:CGPointMake([containerLayer bounds].size.width/2.0f, [containerLayer bounds].size.height/2.0f)];

  CALayer *translucentLayer = [CALayer layer];
  [translucentLayer setBounds:baseRect];
  [translucentLayer setBackgroundColor:[[UIColor blackColor] CGColor]];
  [translucentLayer setPosition:CGPointMake([containerLayer bounds].size.width/2.0f, [containerLayer bounds].size.height/2.0f)];
  [translucentLayer setOpacity:0.35];

  [compositeMaskLayer addSublayer:translucentLayer];

  CAShapeLayer *circleLayer = [CAShapeLayer layer];
  UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0.0f, 0.0f, 200.0f, 200.0f)];
  [circleLayer setBounds:CGRectMake(0.0f, 0.0f, 200.0f, 200.0f)];
  [circleLayer setPosition:CGPointMake([containerLayer bounds].size.width/2.0f, [containerLayer bounds].size.height/2.0f)];
  [circleLayer setPath:[circlePath CGPath]];
  [circleLayer setFillColor:[[UIColor blackColor] CGColor]];

  [compositeMaskLayer addSublayer:circleLayer];

  [containerLayer setMask:compositeMaskLayer];

  [[[self view] layer] addSublayer:containerLayer];

  CABasicAnimation *posAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
  [posAnimation setFromValue:[NSValue valueWithCGPoint:[circleLayer position]]];
  [posAnimation setToValue:[NSValue valueWithCGPoint:CGPointMake([circleLayer position].x + 100.0f, [circleLayer position].y + 100)]];
  [posAnimation setDuration:1.0f];
  [posAnimation setRepeatCount:INFINITY];
  [posAnimation setAutoreverses:YES];

  [circleLayer addAnimation:posAnimation forKey:@"position"];

}
18
Matt Long

Hier ist eine Antwort, die mit mehreren unabhängigen, möglicherweise überlappenden Scheinwerfern funktioniert.

Ich werde meine Ansichtshierarchie folgendermaßen einrichten:

SpotlightsView with black background
    UIImageView with `alpha`=.5 (“dim view”)
    UIImageView with shape layer mask (“bright view”)

Die gedämpfte Ansicht erscheint abgeblendet, da das Alpha-Bild des Bildes mit dem Schwarz der Ansicht der obersten Ebene gemischt wird.

Die helle Ansicht ist nicht abgeblendet, sondern zeigt nur an, wo ihre Maske dies zulässt. Also habe ich die Maske so eingestellt, dass sie die Spotlight-Bereiche und sonst nirgends enthält.

So sieht es aus:

enter image description here

Ich werde es als Unterklasse von UIView mit dieser Schnittstelle implementieren:

// SpotlightsView.h

#import <UIKit/UIKit.h>

@interface SpotlightsView : UIView

@property (nonatomic, strong) UIImage *image;

- (void)addDraggableSpotlightWithCenter:(CGPoint)center radius:(CGFloat)radius;

@end

Ich brauche QuartzCore (auch Core-Animation genannt) und die Objective-C-Laufzeit, um es zu implementieren:

// SpotlightsView.m

#import "SpotlightsView.h"
#import <QuartzCore/QuartzCore.h>
#import <objc/runtime.h>

Ich benötige Instanzvariablen für die Unteransichten, die Maskenebene und ein Array aus einzelnen Spotlight-Pfaden:

@implementation SpotlightsView {
    UIImageView *_dimImageView;
    UIImageView *_brightImageView;
    CAShapeLayer *_mask;
    NSMutableArray *_spotlightPaths;
}

Um die image-Eigenschaft zu implementieren, gebe ich sie einfach an Ihre Image-Subviews weiter:

#pragma mark - Public API

- (void)setImage:(UIImage *)image {
    _dimImageView.image = image;
    _brightImageView.image = image;
}

- (UIImage *)image {
    return _dimImageView.image;
}

Um ein ziehbares Spotlight hinzuzufügen, erstelle ich einen Pfad, der das Spotlight umreißt, füge ihn dem Array hinzu und kennzeichne mich als Layout:

- (void)addDraggableSpotlightWithCenter:(CGPoint)center radius:(CGFloat)radius {
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(center.x - radius, center.y - radius, 2 * radius, 2 * radius)];
    [_spotlightPaths addObject:path];
    [self setNeedsLayout];
}

Ich muss einige Methoden von UIView überschreiben, um die Initialisierung und das Layout zu handhaben. Ich werde entweder programmgesteuert oder in einem Xib oder Storyboard erstellt werden, indem ich den allgemeinen Initialisierungscode an eine private Methode delegiere:

#pragma mark - UIView overrides

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        [self commonInit];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super initWithCoder:aDecoder]) {
        [self commonInit];
    }
    return self;
}

Ich werde das Layout für jede Unteransicht in separaten Hilfsmethoden behandeln:

- (void)layoutSubviews {
    [super layoutSubviews];
    [self layoutDimImageView];
    [self layoutBrightImageView];
}

Um die Scheinwerfer zu ziehen, wenn sie berührt werden, muss ich einige UIResponder-Methoden überschreiben. Ich möchte jede Berührung separat behandeln, also verwende ich einfach die aktualisierten Berührungen und gebe jede an eine Hilfsmethode weiter:

#pragma mark - UIResponder overrides

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    for (UITouch *touch in touches){
        [self touchBegan:touch];
    }
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    for (UITouch *touch in touches){
        [self touchMoved:touch];
    }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    for (UITouch *touch in touches) {
        [self touchEnded:touch];
    }
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    for (UITouch *touch in touches) {
        [self touchEnded:touch];
    }
}

Jetzt werde ich die privaten Erscheinungsbild- und Layoutmethoden implementieren.

#pragma mark - Implementation details - appearance/layout

Zuerst mache ich den allgemeinen Initialisierungscode. Ich möchte meine Hintergrundfarbe auf Schwarz setzen, da dies dazu gehört, dass die abgeblendete Bildansicht gedimmt wird, und ich möchte mehrere Berührungen unterstützen:

- (void)commonInit {
    self.backgroundColor = [UIColor blackColor];
    self.multipleTouchEnabled = YES;
    [self initDimImageView];
    [self initBrightImageView];
    _spotlightPaths = [NSMutableArray array];
}

Meine beiden Bildunteransichten werden größtenteils auf die gleiche Weise konfiguriert. Daher rufe ich eine andere private Methode zum Erstellen der dunklen Bildansicht auf.

- (void)initDimImageView {
    _dimImageView = [self newImageSubview];
    _dimImageView.alpha = 0.5;
}

Ich rufe dieselbe Hilfsmethode auf, um die helle Ansicht zu erstellen, und füge dann ihre Maskenunterschicht hinzu:

- (void)initBrightImageView {
    _brightImageView = [self newImageSubview];
    _mask = [CAShapeLayer layer];
    _brightImageView.layer.mask = _mask;
}

Die Hilfsmethode, die beide Bildansichten erstellt, legt den Inhaltsmodus fest und fügt die neue Ansicht als Unteransicht hinzu:

- (UIImageView *)newImageSubview {
    UIImageView *subview = [[UIImageView alloc] init];
    subview.contentMode = UIViewContentModeScaleAspectFill;
    [self addSubview:subview];
    return subview;
}

Um die dunkle Bildansicht zu gestalten, muss ich nur den Rahmen an meine Grenzen stellen:

- (void)layoutDimImageView {
    _dimImageView.frame = self.bounds;
}

Um die helle Bildansicht zu gestalten, muss ich den Rahmen an meine Grenzen setzen und den Pfad der Maskenebene aktualisieren, um die Vereinigung der einzelnen Spotlight-Pfade zu sein:

- (void)layoutBrightImageView {
    _brightImageView.frame = self.bounds;
    UIBezierPath *unionPath = [UIBezierPath bezierPath];
    for (UIBezierPath *path in _spotlightPaths) {
        [unionPath appendPath:path];
    }
    _mask.path = unionPath.CGPath;
}

Beachten Sie, dass dies keine echte Einheit ist, die jeden Punkt einmal einschließt. Es verwendet den Füllmodus (Standardeinstellung kCAFillRuleNonZero), um sicherzustellen, dass die Maske wiederholt eingeschlossene Punkte enthält.

Als nächstes berühren Sie die Handhabung.

#pragma mark - Implementation details - touch handling

Wenn UIKit mir eine neue Berührung schickt, finde ich den einzelnen Spotlight-Pfad, der die Berührung enthält, und füge den Pfad der Berührung als zugehöriges Objekt hinzu. Das heißt, ich brauche einen zugehörigen Objektschlüssel, der nur eine private Sache sein muss, an die ich mich wenden kann:

static char kSpotlightPathAssociatedObjectKey;

Hier finde ich tatsächlich den Pfad und füge ihn der Berührung an. Wenn sich die Berührung außerhalb eines meiner Spotlight-Pfade befindet, ignoriere ich sie:

- (void)touchBegan:(UITouch *)touch {
    UIBezierPath *path = [self firstSpotlightPathContainingTouch:touch];
    if (path == nil)
        return;
    objc_setAssociatedObject(touch, &kSpotlightPathAssociatedObjectKey,
        path, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

Wenn UIKit sagt, dass sich eine Berührung bewegt hat, sehe ich, ob an der Berührung ein Pfad angehängt ist. Wenn ja, übersetze ich den Pfad um den Betrag, um den sich die Berührung bewegt hat, seit ich ihn das letzte Mal gesehen habe. Dann markiere ich mich für das Layout:

- (void)touchMoved:(UITouch *)touch {
    UIBezierPath *path = objc_getAssociatedObject(touch,
        &kSpotlightPathAssociatedObjectKey);
    if (path == nil)
        return;
    CGPoint point = [touch locationInView:self];
    CGPoint priorPoint = [touch previousLocationInView:self];
    [path applyTransform:CGAffineTransformMakeTranslation(
        point.x - priorPoint.x, point.y - priorPoint.y)];
    [self setNeedsLayout];
}

Ich muss eigentlich nichts tun, wenn die Berührung endet oder abgebrochen wird. Die Objective-C-Laufzeitumgebung entfernt automatisch den angefügten Pfad (sofern vorhanden):

- (void)touchEnded:(UITouch *)touch {
    // Nothing to do
}

Um den Pfad zu finden, der eine Berührung enthält, gehe ich einfach über die Spotlight-Pfade und frage jeden, ob er die Berührung enthält:

- (UIBezierPath *)firstSpotlightPathContainingTouch:(UITouch *)touch {
    CGPoint point = [touch locationInView:self];
    for (UIBezierPath *path in _spotlightPaths) {
        if ([path containsPoint:point])
            return path;
    }
    return nil;
}

@end

Ich habe eine vollständige Demo zu github hochgeladen.

8
rob mayoff

Wenn Sie nur etwas Plug-and-Play wünschen, habe ich CocoaPods um eine Bibliothek erweitert, mit der Sie Überlagerungen mit rechteckigen/kreisförmigen Löchern erstellen können, sodass der Benutzer mit den Ansichten hinter der Überlagerung interagieren kann. Es ist eine schnelle Implementierung ähnlicher Strategien, die in anderen Antworten verwendet werden. Ich habe es benutzt, um dieses Tutorial für eine unserer Apps zu erstellen:

Tutorial using the TAOverlayView

Die Bibliothek heißt TAOverlayView und ist unter Apache 2.0 Open Source.

Hinweis: Ich habe noch keine beweglichen Löcher implementiert (es sei denn, Sie verschieben die gesamte Überlagerung wie in anderen Antworten).

0
Nick Yap

Ich habe mit dem gleichen Problem gekämpft und hier bei SO eine große Hilfe gefunden. Ich dachte, ich würde meine Lösung mit einigen verschiedenen Ideen teilen, die ich online gefunden habe. Eine zusätzliche Funktion, die ich hinzugefügt habe, bestand darin, dass der Ausschnitt einen Gradienteneffekt hatte. Der zusätzliche Vorteil dieser Lösung besteht darin, dass sie mit jeder UIView und nicht nur mit Bildern funktioniert.

Erste Unterklasse UIView, um alles außer den Rahmen auszublenden, die Sie ausschneiden möchten:

// BlackOutView.h
@interface BlackOutView : UIView

@property (nonatomic, retain) UIColor *fillColor;
@property (nonatomic, retain) NSArray *framesToCutOut;

@end

// BlackOutView.m
@implementation BlackOutView

- (void)drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetBlendMode(context, kCGBlendModeDestinationOut);

    for (NSValue *value in self.framesToCutOut) {
        CGRect pathRect = [value CGRectValue];
        UIBezierPath *path = [UIBezierPath bezierPathWithRect:pathRect];

        // change to this path for a circular cutout if you don't want a gradient 
        // UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:pathRect];

        [path fill];
    }

    CGContextSetBlendMode(context, kCGBlendModeNormal);
}
@end

Wenn Sie den Unschärfeeffekt nicht möchten, können Sie den Pfad zum ovalen Pfad wechseln und die Unschärfemaske unten überspringen. Andernfalls ist der Ausschnitt quadratisch und mit einem kreisförmigen Verlauf gefüllt. 

Erstellen Sie eine Verlaufsform, wobei die Mitte transparent ist und in Schwarz langsam verblasst:

// BlurFilterMask.h
@interface BlurFilterMask : CAShapeLayer

@property (assign) CGPoint Origin;
@property (assign) CGFloat diameter;
@property (assign) CGFloat gradient;

@end

// BlurFilterMask.m
@implementation CRBlurFilterMask

- (void)drawInContext:(CGContextRef)context
{
    CGFloat gradientWidth = self.diameter * 0.5f;
    CGFloat clearRegionRadius = self.diameter * 0.25f;
    CGFloat blurRegionRadius = clearRegionRadius + gradientWidth;

    CGColorSpaceRef baseColorSpace = CGColorSpaceCreateDeviceRGB();
    CGFloat colors[8] = { 0.0f, 0.0f, 0.0f, 0.0f,     // Clear region colour.
        0.0f, 0.0f, 0.0f, self.gradient };   // Blur region colour.
    CGFloat colorLocations[2] = { 0.0f, 0.4f };
    CGGradientRef gradient = CGGradientCreateWithColorComponents (baseColorSpace, colors,     colorLocations, 2);

    CGContextDrawRadialGradient(context, gradient, self.Origin, clearRegionRadius, self.Origin, blurRegionRadius, kCGGradientDrawsAfterEndLocation);

    CGColorSpaceRelease(baseColorSpace);
    CGGradientRelease(gradient);
}

@end

Jetzt müssen Sie nur noch diese beiden zusammen aufrufen und die UIViews übergeben, die Sie ausschneiden möchten

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    [self addMaskInViews:@[self.viewCutout1, self.viewCutout2]];
}

- (void) addMaskInViews:(NSArray *)viewsToCutOut
{
    NSMutableArray *frames = [NSMutableArray new];
    for (UIView *view in viewsToCutOut) {
        view.hidden = YES; // hide the view since we only use their bounds
        [frames addObject:[NSValue valueWithCGRect:view.frame]];
    }

    // Create the overlay passing in the frames we want to cut out
    BlackOutView *overlay = [[BlackOutView alloc] initWithFrame:self.view.frame];
    overlay.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.8];
    overlay.framesToCutOut = frames;
    [self.view insertSubview:overlay atIndex:0];

    // add a circular gradients inside each view
    for (UIView *maskView in viewsToCutOut)
    {
        BlurFilterMask *blurFilterMask = [BlurFilterMask layer];
        blurFilterMask.frame = maskView.frame;
        blurFilterMask.gradient = 0.8f;
        blurFilterMask.diameter = MIN(maskView.frame.size.width, maskView.frame.size.height);
        blurFilterMask.Origin = CGPointMake(maskView.frame.size.width / 2, maskView.frame.size.height / 2);
        [self.view.layer addSublayer:blurFilterMask];
        [blurFilterMask setNeedsDisplay];
    }
}
0
Oren