2013-03-13 2 views
26

я хочу сделать что-то похожее на следующее:вырезать формы с анимацией

How to mask an image in IOS sdk?

Я хочу, чтобы покрыть весь экран с полупрозрачным черным. Затем я хочу вырезать круг из полупрозрачного черного покрытия, чтобы вы могли ясно видеть. Я делаю это, чтобы выделить части экрана для учебника.

Затем я хочу анимировать круг выреза для других частей экрана. Я также хочу, чтобы можно было растянуть круг выреза горизонтально & по вертикали, как вы бы сделали с общим фоном изображения кнопки.

ответ

17

Это не простой вопрос. Я могу вам немного поработать. Это анимация, которая сложна. Вот выход какой-то код, который я бросил вместе:

Inverted Mask Layer

Код выглядит так:

- (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]; 
} 

Вы бы анимировать маску слоя, который можно построить на основе пользовательского ввода, но это будет немного сложно. Обратите внимание на строки, где я добавляю прямоугольный путь к пути окружности, а затем устанавливаю правило заливки несколькими строками позже на слое формы. Это то, что делает перевернутую маску возможной. Если вы оставите их, вместо этого вы увидите полупрозрачный черный цвет в центре круга, а затем ничего на внешней части (если это имеет смысл).

Возможно, попробуйте сыграть с этим кодом и посмотреть, сможете ли вы его оживить. Я буду играть с ним еще, поскольку у меня есть время, но это довольно интересная проблема. Хотелось бы увидеть полное решение.


UPDATE: Так вот еще один удар в этом. Беда здесь в том, что эта делает полупрозрачную маску белой, а не черной, но вверх - это то, что круг можно легко анимировать.

Этот слой создает композитный слой с полупрозрачным слоем, а слой окружности - это родные братья внутри родительского слоя, который используется в качестве маски.

Composite Mask

Я добавил базовую анимацию к этому, чтобы мы могли видеть одушевленный круг слой.

- (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"]; 

} 
+0

Hi Matt !! можете ли вы также рассказать мне, как сохранить изображение в круговой области в виде png. –

58

(ОБНОВЛЕНИЕ: Пожалуйста, смотрите также my other answer, который описывает, как настроить несколько независимых перекрывающиеся отверстия.)

Давайте использовать обычный старый UIView с backgroundColor из полупрозрачного черного цвета, и дает его слой маску который вырезает отверстие из середины.Нам понадобятся переменной экземпляра, чтобы сослаться на виде отверстия:

@implementation ViewController { 
    UIView *holeView; 
} 

После загрузки основного вида, мы хотим, чтобы добавить вид отверстия как подвид:

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

Поскольку мы хотим, чтобы переместить дыра вокруг, будет удобно сделать вид отверстия очень большим, чтобы он покрывал остальную часть содержимого независимо от того, где он расположен. Мы сделаем это 10000x10000. (Это не занимает больше памяти, так как IOS не автоматически выделять растровое изображение для просмотра.)

- (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]; 
} 

Теперь нам нужно добавить маску, которая отсекает отверстие вне поля зрения отверстия. Мы сделаем это, создав сложный путь, состоящий из огромного прямоугольника с меньшим кругом в его центре. Мы заполним путь черным, оставив круг незаполненным и, следовательно, прозрачным. Черная часть имеет альфа = 1,0, и поэтому она отображает цвет фона отверстия. Прозрачная часть имеет альфа = 0,0, так что часть отверстий также прозрачна.

- (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; 
} 

Обратите внимание, что я поставил круг в центре экрана 10000x10000. Это означает, что мы можем просто установить holeView.center, чтобы установить центр круга относительно другого содержимого. Так, например, мы можем легко оживить его вверх и вниз на главном экране:

- (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]; 
    }); 
} 

Вот как это выглядит:

hole animation

Но это гладко в реальной жизни.

Вы можете найти полный рабочий тестовый проект in this github repository.

+1

Хорошая идея с большим полупрозрачным слоем, Роб. +1 –

+0

Это очень круто. Ключ здесь, для меня, это две строки: [path appendPath: [UIBezierPath bezierPathWithRect: bounds]]; maskLayer.fillRule = kCAFillRuleEvenOdd; Я мог получить только «темные» прожекторы и пытался выяснить, как инвертировать путь, чтобы получить светные прожекторы, как у Роба здесь. Код Роба для добавления пути границ в конце эффективно выполняет инверсию. К сожалению, я хотел несколько прожекторов, и некоторые из них перекрывались, но я не хотел иметь четный эффект на пересечениях этих путей, поэтому для меня это не сработало. Тем не менее, действительно классная техника! –

+0

@SimoneManganelli [Посмотрите мой новый ответ здесь.] (Http://stackoverflow.com/a/22262689/77567) –

7

Вот ответ, который работает с несколькими независимыми, возможно перекрывающимися прожекторами.

Я настроил свой вид иерархии, как это:

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

тусклый вид будет казаться серым цветом, потому что его альфа смешивает ее образ с сажей точки зрения верхнего уровня.

Яркий вид не затуманен, но он показывает только, где его маска позволяет. Поэтому я просто установил маску, чтобы она содержала области прожектора и больше нигде.

Вот как это выглядит:

enter image description here

Я реализовать как подкласс UIView с этим интерфейсом:

// SpotlightsView.h 

#import <UIKit/UIKit.h> 

@interface SpotlightsView : UIView 

@property (nonatomic, strong) UIImage *image; 

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

@end 

мне понадобится QuartzCore (также называется Core Animation) и времени выполнения Objective-C для его реализации:

// SpotlightsView.m 

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

мне понадобится переменный экземпляр для подвидов, маскирующий слой, и множество отдельных путей внимания:

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

Чтобы реализовать image свойства, я просто пропустить его через ваших подобозрение изображения:

#pragma mark - Public API 

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

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

чтобы добавить перемещаемое внимание, создать путь с изложением в центре внимания, добавьте его в массив, и флаг себя нуждающееся расположение:

- (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]; 
} 

Мне нужно переопределить некоторые методы UIView для обработки инициализации и макета. Я разберусь создается программно или в XIb или раскадровку путем делегирования общего кода инициализации частного метода:

#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; 
} 

Я разберусь макет в отдельных вспомогательных методов для каждого подвид:

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

Чтобы перетащить прожекторы, когда они коснулись, мне нужно переопределить некоторые методы UIResponder. Я хочу, чтобы обрабатывать каждый контакт отдельно, так что я просто перебираю обновленные прикосновения, проходя каждый из них к вспомогательному методу:

#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]; 
    } 
} 

Теперь я буду внедрять частные внешний вид и расположение методов.

#pragma mark - Implementation details - appearance/layout 

Сначала я сделаю общий код инициализации.Я хочу, чтобы установить свой цвет фона черный, так как это часть делает затененный вид изображения тусклым, и я хочу, чтобы поддержать несколько прикосновений:

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

Мои два подвиды изображения будут настроены в основном таким же образом, так Я позвоню еще один частный метод, чтобы создать тусклый вид изображения, а затем настроить его на самом деле быть тусклым:

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

Я буду называть один и тот же вспомогательный метод для создания яркого изображения, затем добавьте маску подуровень:

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

Вспомогательный метод, который создает оба представления изображений устанавливает режим содержания и добавляет новый вид как подвид:

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

раскладывать тусклый вид изображения, мне просто нужно, чтобы установить его рамку к моим часам:

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

раскладывать яркий вид изображения, мне нужно, чтобы установить его рамку к моим часам, и мне нужно обновить путь маски слоя, чтобы объединение отдельных путей Spotlight:

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

Обратите внимание, что это не настоящий союз, который каждый раз включает каждую точку. Он полагается на режим заполнения (по умолчанию, kCAFillRuleNonZero), чтобы включить в маску многократно включенные точки.

Вперед, касание обработки.

#pragma mark - Implementation details - touch handling 

Когда UIKit посылает мне новый контакт, я найду индивидуальный путь фару, содержащий прикосновение, и прикрепить путь к контакту в качестве ассоциированного объекта. Это означает, что мне нужно соответствующий ключ объекта, который должен просто быть какие-то частные, что я могу взять адрес:

static char kSpotlightPathAssociatedObjectKey; 

Здесь я на самом деле найти путь и прикрепить его на ощупь. Если сенсорный находится вне какой-либо из моих прожекторов дорожек, я игнорирую его:

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

Когда UIKit говорит мне прикосновение переместилось, я вижу, если прикосновение имеет путь прилагается. Если это так, я переводил (слайд) путь на количество, которое касание тронуло с тех пор, как я его последний раз видел. Затем я обозначаю себя как макет:

- (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]; 
} 

На самом деле мне ничего не нужно делать, когда касание заканчивается или отменяется. Среда Objective-C будет де-ассоциированная прилагаемым путем (если есть один) автоматически:

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

Чтобы найти путь, который содержит прикосновение, я просто петлю над путями прожектора, спрашивая каждого из них, если он содержит прикосновение:

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

@end 

Я загрузил полный демо to github.

+0

Ваше первое решение позволяет только одной маске, а вашему второму решению требуется фоновое изображение. Как я могу получить несколько масок, но без изображения? Думаю, это будет комбинация обоих ваших решений? Мне нужно несколько вырезать поверх моего уже существующего представления. – Oren

+0

Если у вас есть произвольное представление с subviews, и вам нужно несколько прожекторов, это сложно.Мне не хочется писать еще один очень сложный ответ на этот вопрос прямо сейчас. –

+0

Я могу сказать вам, что я бы посмотрел на использование ['- [UIView snapshotViewAfterScreenUpdates:]'] (https://developer.apple.com/library/ios/documentation/uikit/reference/uiview_class/uiview/uiview.html# // apple_ref/occ/instm/UIView/snapshotViewAfterScreenUpdates :) для замены фонового изображения. –

0

Я боролся с этой же проблемой и нашел здесь большую помощь здесь, поэтому я решил поделиться своим решением, сочетающим несколько разных идей, которые я нашел в Интернете. Еще одна особенность, которую я добавил, заключалась в том, чтобы вырезать эффект градиента.Дополнительным преимуществом этого решения является то, что он работает с любым UIView, а не только с изображениями.

Первый подкласс UIView затемнить все, кроме кадров, которые вы хотите вырезать:

// 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 

Если вы не хотите, чтобы эффект размытия, то вы можете поменять пути к овальному один и пропустить маску размытия ниже. В противном случае вырез будет квадратным и заполнен круговым градиентом.

Создание градиентной формы с центром прозрачным и медленно увядать в черный:

// 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 

Теперь вам просто нужно позвонить эти два вместе и передать в UIView с, которые вы хотите рубильник

- (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

Если вы просто хотите что-то подключи и играй, я добавил библиотеку CocoaPods, которая позволяет создавать наложения с прямоугольными/круглыми отверстиями, позволяя пользователю взаимодействовать с представлениями за оверлей. Это быстрая реализация аналогичных стратегий, используемых в других ответах. Я использовал его, чтобы создать этот учебник для одного из наших приложений:

Tutorial using the TAOverlayView

Библиотека называется TAOverlayView, и является открытым исходным кодом под Apache 2.0.

Примечание: Я еще не реализовал движущиеся дыры (если вы не переместите весь оверлей, как в других ответах).

Смежные вопросы