Вот ответ, который работает с несколькими независимыми, возможно перекрывающимися прожекторами.
Я настроил свой вид иерархии, как это:
SpotlightsView with black background
UIImageView with `alpha`=.5 (“dim view”)
UIImageView with shape layer mask (“bright view”)
тусклый вид будет казаться серым цветом, потому что его альфа смешивает ее образ с сажей точки зрения верхнего уровня.
Яркий вид не затуманен, но он показывает только, где его маска позволяет. Поэтому я просто установил маску, чтобы она содержала области прожектора и больше нигде.
Вот как это выглядит:
Я реализовать как подкласс 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.
Hi Matt !! можете ли вы также рассказать мне, как сохранить изображение в круговой области в виде png. –