diff --git a/HoledViewTest.xcodeproj/project.pbxproj b/HoledViewTest.xcodeproj/project.pbxproj index f1acd98..d028e11 100644 --- a/HoledViewTest.xcodeproj/project.pbxproj +++ b/HoledViewTest.xcodeproj/project.pbxproj @@ -21,6 +21,8 @@ 1861B5E51AD3BC0700453C08 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1861B5C71AD3BC0700453C08 /* UIKit.framework */; }; 1861B5ED1AD3BC0700453C08 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1861B5EB1AD3BC0700453C08 /* InfoPlist.strings */; }; 1861B5EF1AD3BC0700453C08 /* HoledViewTestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1861B5EE1AD3BC0700453C08 /* HoledViewTestTests.m */; }; + 1861B5FB1AD3BEFD00453C08 /* YLZHoledView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1861B5FA1AD3BEFD00453C08 /* YLZHoledView.m */; }; + 1861B5FD1AD3CAC000453C08 /* background@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1861B5FC1AD3CAC000453C08 /* background@2x.png */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -53,6 +55,9 @@ 1861B5EA1AD3BC0700453C08 /* HoledViewTestTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "HoledViewTestTests-Info.plist"; sourceTree = ""; }; 1861B5EC1AD3BC0700453C08 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 1861B5EE1AD3BC0700453C08 /* HoledViewTestTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HoledViewTestTests.m; sourceTree = ""; }; + 1861B5F91AD3BEFD00453C08 /* YLZHoledView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLZHoledView.h; sourceTree = ""; }; + 1861B5FA1AD3BEFD00453C08 /* YLZHoledView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLZHoledView.m; sourceTree = ""; }; + 1861B5FC1AD3CAC000453C08 /* background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "background@2x.png"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -112,11 +117,13 @@ 1861B5C91AD3BC0700453C08 /* HoledViewTest */ = { isa = PBXGroup; children = ( + 1861B5F81AD3BC0F00453C08 /* YLZHoledView */, 1861B5D21AD3BC0700453C08 /* AppDelegate.h */, 1861B5D31AD3BC0700453C08 /* AppDelegate.m */, 1861B5D51AD3BC0700453C08 /* Main.storyboard */, 1861B5D81AD3BC0700453C08 /* ViewController.h */, 1861B5D91AD3BC0700453C08 /* ViewController.m */, + 1861B5FC1AD3CAC000453C08 /* background@2x.png */, 1861B5DB1AD3BC0700453C08 /* Images.xcassets */, 1861B5CA1AD3BC0700453C08 /* Supporting Files */, ); @@ -152,6 +159,15 @@ name = "Supporting Files"; sourceTree = ""; }; + 1861B5F81AD3BC0F00453C08 /* YLZHoledView */ = { + isa = PBXGroup; + children = ( + 1861B5F91AD3BEFD00453C08 /* YLZHoledView.h */, + 1861B5FA1AD3BEFD00453C08 /* YLZHoledView.m */, + ); + name = YLZHoledView; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -229,6 +245,7 @@ buildActionMask = 2147483647; files = ( 1861B5DC1AD3BC0700453C08 /* Images.xcassets in Resources */, + 1861B5FD1AD3CAC000453C08 /* background@2x.png in Resources */, 1861B5CE1AD3BC0700453C08 /* InfoPlist.strings in Resources */, 1861B5D71AD3BC0700453C08 /* Main.storyboard in Resources */, ); @@ -252,6 +269,7 @@ 1861B5DA1AD3BC0700453C08 /* ViewController.m in Sources */, 1861B5D41AD3BC0700453C08 /* AppDelegate.m in Sources */, 1861B5D01AD3BC0700453C08 /* main.m in Sources */, + 1861B5FB1AD3BEFD00453C08 /* YLZHoledView.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/HoledViewTest.xcodeproj/xcuserdata/apple.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/HoledViewTest.xcodeproj/xcuserdata/apple.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..fe2b454 --- /dev/null +++ b/HoledViewTest.xcodeproj/xcuserdata/apple.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,5 @@ + + + diff --git a/HoledViewTest/ViewController.m b/HoledViewTest/ViewController.m index 2b18c4f..7a25206 100644 --- a/HoledViewTest/ViewController.m +++ b/HoledViewTest/ViewController.m @@ -7,8 +7,12 @@ // #import "ViewController.h" +#import "YLZHoledView.h" -@interface ViewController () +@interface ViewController () + +@property (nonatomic, strong) YLZHoledView *holedView; +@property (nonatomic, strong) UIButton *myButton; @end @@ -18,12 +22,86 @@ - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. + + [self setupTestUI]; + + [self performSelector:@selector(addTestHoledView) withObject:self afterDelay:3.0f]; +} + + +- (void)setupTestUI +{ + + //设置背景图 + self.view.backgroundColor = [UIColor colorWithPatternImage: [UIImage imageNamed:@"background"] ]; + + //添加按钮 + self.myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + self.myButton.frame = CGRectMake(120, 400, 80, 40); + [self.myButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; + self.myButton.layer.borderWidth = 1; + self.myButton.layer.cornerRadius = 4.0; + self.myButton.layer.masksToBounds = YES; + [self.myButton setAdjustsImageWhenHighlighted:NO]; + self.myButton.backgroundColor = [UIColor colorWithRed:92/255.0 green:184/255.0 blue:92/255.0 alpha:1]; + self.myButton.layer.borderColor = [[UIColor colorWithRed:76/255.0 green:174/255.0 blue:76/255.0 alpha:1] CGColor]; + self.myButton.layer.cornerRadius = 20.0; + [self.myButton setTitle:@"点我!" forState:UIControlStateNormal]; + [self.myButton addTarget:self + action:@selector(onButtonTapped:) + forControlEvents:UIControlEventTouchUpInside]; + + [self.view addSubview:self.myButton]; +} + +- (void)onButtonTapped:(UIButton *)sender +{ + NSLog(@"Button Tapped!"); + [self.holedView removeHoles]; + [self.holedView removeFromSuperview]; +} + +- (void)addTestHoledView +{ + + //添加遮罩指示 + self.holedView = [[YLZHoledView alloc]initWithFrame:self.view.frame]; + self.holedView.holeViewDelegate = self; + [self.view addSubview:_holedView]; + + + // 按钮 - 圆角矩形 + [self.holedView addHoleRoundedRectOnRect:_myButton.frame withCornerRadius:_myButton.frame.size.width/2]; + [self.holedView addFocusView:_myButton]; + + // 圆形 + [self.holedView addHoleCircleCenteredOnPosition:CGPointMake(190, 110) andDiameter:130]; + + // 文字说明 + [self.holedView addHCustomView:[self viewForDemo] onRect:CGRectMake(20.0f, 250.0f, 200.0f, 50.0f)]; + +} + +#pragma marl - helper + +- (UIView *)viewForDemo +{ + UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20.0f, 250.0F, 200.0f, 50.0f)]; + [label setBackgroundColor:[UIColor clearColor]]; + label.layer.borderColor = [UIColor whiteColor].CGColor; + label.layer.borderWidth = 1.0f; + label.layer.cornerRadius = 10.0f; + [label setTextColor:[UIColor whiteColor]]; + label.text = @"点击按钮, 退出遮罩层."; + label.numberOfLines = 2; + label.font = [UIFont systemFontOfSize:18.0f]; + label.textAlignment = NSTextAlignmentCenter; + return label; } -- (void)didReceiveMemoryWarning +- (void)holedView:(YLZHoledView *)holedView didSelectHoleAtIndex:(NSUInteger)index { - [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. + NSLog(@"%s %ld", __PRETTY_FUNCTION__,(long)index); } @end diff --git a/HoledViewTest/YLZHoledView.h b/HoledViewTest/YLZHoledView.h new file mode 100644 index 0000000..49d52e1 --- /dev/null +++ b/HoledViewTest/YLZHoledView.h @@ -0,0 +1,43 @@ +// +// YLZHoledView.h +// HoledViewTest +// +// Created by Colin on 15/4/7. +// Copyright (c) 2015年 icephone. All rights reserved. +// + +#import + +typedef NS_ENUM(NSInteger, YLZHoleType) +{ + YLZHoleTypeCirle, + YLZHoleTypeRect, + YLZHoleTypeRoundedRect, + YLZHoleTypeCustomRect +}; + + +@class YLZHoledView; +@protocol YLZHoledViewDelegate + +- (void)holedView:(YLZHoledView *)holedView didSelectHoleAtIndex:(NSUInteger)index; + +@end + + +@interface YLZHoledView : UIView + +@property (strong, nonatomic) UIColor *dimingColor; +@property (weak, nonatomic) id holeViewDelegate; + +- (NSInteger)addHoleCircleCenteredOnPosition:(CGPoint)centerPoint andDiameter:(CGFloat)diamter; +- (NSInteger)addHoleRectOnRect:(CGRect)rect; +- (NSInteger)addHoleRoundedRectOnRect:(CGRect)rect withCornerRadius:(CGFloat)cornerRadius; +- (NSInteger)addHCustomView:(UIView *)customView onRect:(CGRect)rect; + +- (void)addFocusView:(UIView *)focus; + +- (void)removeHoles; + + +@end diff --git a/HoledViewTest/YLZHoledView.m b/HoledViewTest/YLZHoledView.m new file mode 100644 index 0000000..0b53b9f --- /dev/null +++ b/HoledViewTest/YLZHoledView.m @@ -0,0 +1,292 @@ +// +// YLZHoledView.m +// HoledViewTest +// +// Created by Colin on 15/4/7. +// Copyright (c) 2015年 icephone. All rights reserved. +// + +#import "YLZHoledView.h" + + +#pragma mark - holes objects + +@interface YLZHole : NSObject +@property (assign) YLZHoleType holeType; +@end + +@implementation YLZHole +@end + +@interface YLZCircleHole : YLZHole +@property (assign) CGPoint holeCenterPoint; +@property (assign) CGFloat holeDiameter; +@end + +@implementation YLZCircleHole +@end + +@interface YLZRectHole : YLZHole +@property (assign) CGRect holeRect; +@end + +@implementation YLZRectHole +@end + +@interface YLZRoundedRectHole : YLZRectHole +@property (assign) CGFloat holeCornerRadius; +@end + +@implementation YLZRoundedRectHole +@end + +@interface YLZCustomRectHole : YLZRectHole +@property (strong) UIView *customView; +@end + +@implementation YLZCustomRectHole +@end + +@interface YLZHoledView () +@property (strong, nonatomic) NSMutableArray *holes; //Array of YLZHole +@property (strong, nonatomic) NSMutableArray *focusView; // Array of focus +@end + +@implementation YLZHoledView + +#pragma mark - LifeCycle + +- (void)awakeFromNib +{ + [super awakeFromNib]; + [self setup]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + if (self) { + [self setup]; + } + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + [self setup]; + } + return self; +} + +- (void)setup +{ + _holes = [NSMutableArray new]; + _focusView = [NSMutableArray new]; + self.backgroundColor = [UIColor clearColor]; + _dimingColor = [[UIColor blackColor] colorWithAlphaComponent:0.75f]; + + UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureDetectedForGesture:)]; + [self addGestureRecognizer:tapGesture]; +} + + +#pragma mark - UIView Overrides + +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event +{ + UIView *hitView = [super hitTest:point withEvent:event]; + + if (hitView == self) + { + for (UIView *focus in self.focusView) { + if (CGRectContainsPoint(focus.frame, point)) + { + return focus; + } + } + } + + return hitView; +} + + +- (void)drawRect:(CGRect)rect +{ + [self removeCustomViews]; + CGContextRef context = UIGraphicsGetCurrentContext(); + if (context == nil) { + return; + } + + [self.dimingColor setFill]; + UIRectFill(rect); + + for (YLZHole* hole in self.holes) { + + [[UIColor clearColor] setFill]; + + if (hole.holeType == YLZHoleTypeRoundedRect) { + YLZRoundedRectHole *rectHole = (YLZRoundedRectHole *)hole; + CGRect holeRectIntersection = CGRectIntersection( rectHole.holeRect, self.frame); + UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:holeRectIntersection + cornerRadius:rectHole.holeCornerRadius]; + + CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), [[UIColor clearColor] CGColor]); + CGContextAddPath(UIGraphicsGetCurrentContext(), bezierPath.CGPath); + CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear); + CGContextFillPath(UIGraphicsGetCurrentContext()); + + } else if (hole.holeType == YLZHoleTypeRect) { + YLZRectHole *rectHole = (YLZRectHole *)hole; + CGRect holeRectIntersection = CGRectIntersection( rectHole.holeRect, self.frame); + UIRectFill( holeRectIntersection ); + + } else if (hole.holeType == YLZHoleTypeCirle) { + YLZCircleHole *circleHole = (YLZCircleHole *)hole; + CGRect rectInView = CGRectMake(floorf(circleHole.holeCenterPoint.x - circleHole.holeDiameter*0.5f), + floorf(circleHole.holeCenterPoint.y - circleHole.holeDiameter*0.5f), + circleHole.holeDiameter, + circleHole.holeDiameter); + CGContextSetFillColorWithColor( context, [UIColor clearColor].CGColor ); + CGContextSetBlendMode(context, kCGBlendModeClear); + CGContextFillEllipseInRect( context, rectInView ); + } + } + + [self addCustomViews]; +} + +#pragma mark - Add methods + +- (NSInteger)addHoleCircleCenteredOnPosition:(CGPoint)centerPoint andDiameter:(CGFloat)diameter +{ + YLZCircleHole *circleHole = [YLZCircleHole new]; + circleHole.holeCenterPoint = centerPoint; + circleHole.holeDiameter = diameter; + circleHole.holeType = YLZHoleTypeCirle; + [self.holes addObject:circleHole]; + [self setNeedsDisplay]; + + return [self.holes indexOfObject:circleHole]; +} + +- (NSInteger)addHoleRectOnRect:(CGRect)rect +{ + YLZRectHole *rectHole = [YLZRectHole new]; + rectHole.holeRect = rect; + rectHole.holeType = YLZHoleTypeRect; + [self.holes addObject:rectHole]; + [self setNeedsDisplay]; + + return [self.holes indexOfObject:rectHole]; +} + +- (NSInteger)addHoleRoundedRectOnRect:(CGRect)rect withCornerRadius:(CGFloat)cornerRadius +{ + YLZRoundedRectHole *rectHole = [YLZRoundedRectHole new]; + rectHole.holeRect = rect; + rectHole.holeCornerRadius = cornerRadius; + rectHole.holeType = YLZHoleTypeRoundedRect; + [self.holes addObject:rectHole]; + [self setNeedsDisplay]; + + return [self.holes indexOfObject:rectHole]; +} + +- (NSInteger)addHCustomView:(UIView *)customView onRect:(CGRect)rect +{ + YLZCustomRectHole *customHole = [YLZCustomRectHole new]; + customHole.holeRect = rect; + customHole.customView = customView; + customHole.holeType = YLZHoleTypeCustomRect; + [self.holes addObject:customHole]; + [self setNeedsDisplay]; + + return [self.holes indexOfObject:customHole]; +} + +- (void)addFocusView:(UIView *)focus +{ + [self.focusView addObject:focus]; +} + +- (void)removeHoles +{ + [self.holes removeAllObjects]; + [self removeCustomViews]; + [self setNeedsDisplay]; +} + +#pragma mark - Overided setter + +- (void)setDimingColor:(UIColor *)dimingColor +{ + _dimingColor = dimingColor; + [self setNeedsDisplay]; +} + +#pragma mark - Tap Gesture + +- (void)tapGestureDetectedForGesture:(UITapGestureRecognizer *)gesture +{ + if ([self.holeViewDelegate respondsToSelector:@selector(holedView:didSelectHoleAtIndex:)]) { + CGPoint touchLocation = [gesture locationInView:self]; + [self.holeViewDelegate holedView:self didSelectHoleAtIndex:[self holeViewIndexForAtPoint:touchLocation]]; + } +} + +- (NSUInteger)holeViewIndexForAtPoint:(CGPoint)touchLocation +{ + __block NSUInteger idxToReturn = NSNotFound; + [self.holes enumerateObjectsUsingBlock:^(YLZHole *hole, NSUInteger idx, BOOL *stop) { + if (hole.holeType == YLZHoleTypeRoundedRect || + hole.holeType == YLZHoleTypeRect || + hole.holeType == YLZHoleTypeCustomRect) { + YLZRectHole *rectHole = (YLZRectHole *)hole; + if (CGRectContainsPoint(rectHole.holeRect, touchLocation)) { + idxToReturn = idx; + *stop = YES; + } + + } else if (hole.holeType == YLZHoleTypeCirle) { + YLZCircleHole *circleHole = (YLZCircleHole *)hole; + CGRect rectInView = CGRectMake(floorf(circleHole.holeCenterPoint.x - circleHole.holeDiameter*0.5f), + floorf(circleHole.holeCenterPoint.x - circleHole.holeDiameter*0.5f), + circleHole.holeDiameter, + circleHole.holeDiameter); + if (CGRectContainsPoint(rectInView, touchLocation)) { + idxToReturn = idx; + *stop = YES; + } + } + }]; + + return idxToReturn; +} + +#pragma mark - Custom Views + +- (void)removeCustomViews +{ + [self.holes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + if ([obj isKindOfClass:[YLZCustomRectHole class]]) { + YLZCustomRectHole *hole = (YLZCustomRectHole *)obj; + [hole.customView removeFromSuperview]; + } + }]; +} + +- (void)addCustomViews +{ + [self.holes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + if ([obj isKindOfClass:[YLZCustomRectHole class]]) { + YLZCustomRectHole *hole = (YLZCustomRectHole *)obj; + [hole.customView setFrame:hole.holeRect]; + [self addSubview:hole.customView]; + } + }]; +} + +@end diff --git a/HoledViewTest/background@2x.png b/HoledViewTest/background@2x.png new file mode 100644 index 0000000..0789fa4 Binary files /dev/null and b/HoledViewTest/background@2x.png differ