iOS开发- 自定义遮罩视图(引导, 功能说明)源码+解析

iOS开发- 自定义遮罩视图(引导, 功能说明)源码+解析

我们平时使用App的时候, 经常在第一次使用的时候, 会有类似”新手教程”之类的东西, 来引导我们应该如何使用这个App。

但是这个”新手教程”不同于常规的引导页(引导页指第一次打开App时候, 弹出的那种介绍视图。 他是静态的, 不需要与用户交互, 可以直接一页页翻, 或者直接跳过。)所谓的”新手教程”, 就是按照App的提示, 一步步跟着完成。

那这个“新手教程”有什么好处呢?

  1. 指导用户了解操作。 比如, 如何调节音量, 如何设置焦距…
  2. 限制用户填充必要信息, 完成必要操作。

现在大家应该都明白这到底是一个什么东西了。为实现这样的功能, 一般是采用遮罩层效果, 今天花了点时间, 封装好了这样一个功能。 同时把源码分享到我的Github, 需要的可以自己下载。

下载链接: YLZHoledView

同时, 还写了一个demo, 效果如下:
技术分享

我封装好的这个文件(YLZHoledView), 主要实现了如下功能:
1. 添加多种样式聚光灯效果。(圆形, 矩形, 圆角矩形, 自定义视图)
2. 点击该区域支持delegate回调
3. UIView 中的控件事件穿透。(demo中的按钮, 是在遮罩层下方, 但是正确响应点击事件, 这就是事件穿透)

好了, 大体介绍就到这里, 下面简单解释下核心代码:

delegate回调

YLZHoledView.h

@class YLZHoledView;
@protocol YLZHoledViewDelegate <NSObject>

- (void)holedView:(YLZHoledView *)holedView didSelectHoleAtIndex:(NSUInteger)index;

@end

YLZHoledView.m

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

这里先在自定义的View中添加手势, 然后在
- (NSUInteger)holeViewIndexForAtPoint:(CGPoint)touchLocation 中判断点击范围是否属于遮罩层中添加的视图, 再发送委托。

然后在我们的实现类中, 也就是实现这个委托的地方, 就可以通过index来做相关的操作。 我demo里面只是简单的打印。

聚光灯效果

我这里封装了几种常见的类型。(圆形, 矩形, 圆角矩形, 自定义视图)

typedef NS_ENUM(NSInteger, YLZHoleType)
 {
     YLZHoleTypeCirle,
     YLZHoleTypeRect,
     YLZHoleTypeRoundedRect,
     YLZHoleTypeCustomRect
 };

然后重写
- (void)drawRect:(CGRect)rect
通过类型判断, 来重写对应的效果。

比如, 圆形的代码:

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

}

事件穿透

在这之前, 提一个方法。

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
// recursively calls -pointInside:withEvent:. point is in the receiver’s coordinate system

只要实现了最顶层的 UIView 的 hitTest 方法,在某些情况返回下层的某个按钮实例,即相当于把那个按钮的事件透出来了,比如在点击落在该按钮上时,不管这个按钮在 UIView 下多少层都可以把它挖出来。

所以, 重写hitTest方法即可。

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

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。