Ответ 1
Острая маска
Всякий раз, когда вы хотите нарисовать путь, который состоит из фигуры (или серии фигур) в виде отверстия в другой фигуре, ключ почти всегда использует "правило четной нечетной намотки".
Из раздела " Правила намотки " в Руководстве по рисованию cocoa:
Правило намотки - это просто алгоритм, который отслеживает информацию о каждой смежной области, которая составляет общую область заполнения пути. Луч рисуется из точки внутри данной области в любую точку за пределами границ пути. Общее количество пересеченных линий пути (включая неявные линии) и направление каждой линии пути затем интерпретируются с использованием правил, которые определяют, должна ли область быть заполнена.
Я ценю, что это описание не очень полезно без правил в виде контекста и диаграмм, чтобы его было легче понять, поэтому я призываю вас прочитать ссылки, которые я предоставил выше. Для создания нашего слоя с круговой маской на следующих диаграммах показано, что позволяет нам выполнить правило четного нечетного наматывания:
Нулевое правило намотки
Четное нечетное правило обмотки
Теперь это просто вопрос создания полупрозрачной маски с использованием CAShapeLayer, который можно перемещать, расширять и сокращать посредством взаимодействия с пользователем.
Код
#import <QuartzCore/QuartzCore.h>
@interface ViewController ()
@property (strong, nonatomic) IBOutlet UIImageView *imageView;
@property (strong) CAShapeLayer *blurFilterMask;
@property (assign) CGPoint blurFilterOrigin;
@property (assign) CGFloat blurFilterDiameter;
@end
@implementation ViewController
// begin the blur masking operation.
- (void)beginBlurMasking
{
self.blurFilterOrigin = self.imageView.center;
self.blurFilterDiameter = MIN(CGRectGetWidth(self.imageView.bounds), CGRectGetHeight(self.imageView.bounds));
CAShapeLayer *blurFilterMask = [CAShapeLayer layer];
// Disable implicit animations for the blur filter mask path property.
blurFilterMask.actions = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNull null], @"path", nil];
blurFilterMask.fillColor = [UIColor blackColor].CGColor;
blurFilterMask.fillRule = kCAFillRuleEvenOdd;
blurFilterMask.frame = self.imageView.bounds;
blurFilterMask.opacity = 0.5f;
self.blurFilterMask = blurFilterMask;
[self refreshBlurMask];
[self.imageView.layer addSublayer:blurFilterMask];
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
[self.imageView addGestureRecognizer:tapGesture];
UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)];
[self.imageView addGestureRecognizer:pinchGesture];
}
// Move the origin of the blur mask to the location of the tap.
- (void)handleTap:(UITapGestureRecognizer *)sender
{
self.blurFilterOrigin = [sender locationInView:self.imageView];
[self refreshBlurMask];
}
// Expand and contract the clear region of the blur mask.
- (void)handlePinch:(UIPinchGestureRecognizer *)sender
{
// Use some combination of sender.scale and sender.velocity to determine the rate at which you want the circle to expand/contract.
self.blurFilterDiameter += sender.velocity;
[self refreshBlurMask];
}
// Update the blur mask within the UI.
- (void)refreshBlurMask
{
CGFloat blurFilterRadius = self.blurFilterDiameter * 0.5f;
CGMutablePathRef blurRegionPath = CGPathCreateMutable();
CGPathAddRect(blurRegionPath, NULL, self.imageView.bounds);
CGPathAddEllipseInRect(blurRegionPath, NULL, CGRectMake(self.blurFilterOrigin.x - blurFilterRadius, self.blurFilterOrigin.y - blurFilterRadius, self.blurFilterDiameter, self.blurFilterDiameter));
self.blurFilterMask.path = blurRegionPath;
CGPathRelease(blurRegionPath);
}
...
(Эта диаграмма может помочь понять соглашения об именах в коде)
Градиентная маска
Раздел " Градиенты" в Руководстве по программированию Apple Quartz 2D подробно описывает, как рисовать радиальные градиенты, которые мы можем использовать для создания маски с заостренным краем. Это включает в себя рисование содержимого CALayer напрямую путем создания его подкласса или реализации его делегата рисования. Здесь мы разбиваем его на подклассы, чтобы инкапсулировать связанные с ним данные, т.е. происхождение и диаметр.
Код
BlurFilterMask.h
#import <QuartzCore/QuartzCore.h>
@interface BlurFilterMask : CALayer
@property (assign) CGPoint origin; // The centre of the blur filter mask.
@property (assign) CGFloat diameter; // the diameter of the clear region of the blur filter mask.
@end
BlurFilterMask.m
#import "BlurFilterMask.h"
// The width in points the gradated region of the blur filter mask will span over.
CGFloat const GRADIENT_WIDTH = 50.0f;
@implementation BlurFilterMask
- (void)drawInContext:(CGContextRef)context
{
CGFloat clearRegionRadius = self.diameter * 0.5f;
CGFloat blurRegionRadius = clearRegionRadius + GRADIENT_WIDTH;
CGColorSpaceRef baseColorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat colours[8] = { 0.0f, 0.0f, 0.0f, 0.0f, // Clear region colour.
0.0f, 0.0f, 0.0f, 0.5f }; // Blur region colour.
CGFloat colourLocations[2] = { 0.0f, 0.4f };
CGGradientRef gradient = CGGradientCreateWithColorComponents (baseColorSpace, colours, colourLocations, 2);
CGContextDrawRadialGradient(context, gradient, self.origin, clearRegionRadius, self.origin, blurRegionRadius, kCGGradientDrawsAfterEndLocation);
CGColorSpaceRelease(baseColorSpace);
CGGradientRelease(gradient);
}
@end
ViewController.m (везде, где вы реализуете функцию маскировки размытия файла)
#import "ViewController.h"
#import "BlurFilterMask.h"
#import <QuartzCore/QuartzCore.h>
@interface ViewController ()
@property (strong, nonatomic) IBOutlet UIImageView *imageView;
@property (strong) BlurFilterMask *blurFilterMask;
@end
@implementation ViewController
// Begin the blur filter masking operation.
- (void)beginBlurMasking
{
BlurFilterMask *blurFilterMask = [BlurFilterMask layer];
blurFilterMask.diameter = MIN(CGRectGetWidth(self.imageView.bounds), CGRectGetHeight(self.imageView.bounds));
blurFilterMask.frame = self.imageView.bounds;
blurFilterMask.origin = self.imageView.center;
blurFilterMask.shouldRasterize = YES;
[self.imageView.layer addSublayer:blurFilterMask];
[blurFilterMask setNeedsDisplay];
self.blurFilterMask = blurFilterMask;
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
[self.imageView addGestureRecognizer:tapGesture];
UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)];
[self.imageView addGestureRecognizer:pinchGesture];
}
// Move the origin of the blur mask to the location of the tap.
- (void)handleTap:(UITapGestureRecognizer *)sender
{
self.blurFilterMask.origin = [sender locationInView:self.imageView];
[self.blurFilterMask setNeedsDisplay];
}
// Expand and contract the clear region of the blur mask.
- (void)handlePinch:(UIPinchGestureRecognizer *)sender
{
// Use some combination of sender.scale and sender.velocity to determine the rate at which you want the mask to expand/contract.
self.blurFilterMask.diameter += sender.velocity;
[self.blurFilterMask setNeedsDisplay];
}
...
(Эта диаграмма может помочь понять соглашения об именах в коде)
Заметка
Убедитесь, что multipleTouchEnabled
свойство UIImageView
хостинг изображения устанавливаются в YES
/true
:
Заметка
Для ясности в ответе на вопрос OPs этот ответ продолжает использовать первоначально использованные соглашения об именах. Это может немного вводить в заблуждение других. "Маска" означает, что этот контекст относится не к маске изображения, а к маске в более общем смысле. Этот ответ не использует никаких операций маскировки изображения.