iOS開發- 自訂遮罩視圖(引導, 功能說明)源碼+解析
iOS開發- 自訂遮罩視圖(引導, 功能說明)源碼+解析
我們平時使用App的時候, 經常在第一次使用的時候, 會有類似”新手教程”之類的東西, 來引導我們應該如何使用這個App。
但是這個”新手教程”不同於常規的引導頁(引導頁指第一次開啟App時候, 彈出的那種介紹視圖。 他是靜態, 不需要與使用者互動, 可以直接一頁頁翻, 或者直接跳過。)所謂的”新手教程”, 就是按照App的提示, 一步步跟著完成。
那這個“新手教程”有什麼好處呢?
指導使用者瞭解操作。 比如, 如何調節音量, 如何設定焦距… 限制使用者填充必要資訊, 完成必要操作。
現在大家應該都明白這到底是一個什麼東西了。為實現這樣的功能, 一般是採用遮罩層效果, 今天花了點時間, 封裝好了這樣一個功能。 同時把源碼分享到我的Github, 需要的可以自己下載。
下載連結: YLZHoledView
同時, 還寫了一個demo, 效果如下:
我封裝好的這個檔案(YLZHoledView), 主要實現了如下功能:
1. 添加多種樣式聚光燈效果。(圓形, 矩形, 圓角矩形, 自訂視圖)
2. 點擊該地區支援delegate回調
3. UIView 中的控制項事件穿透。(demo中的按鈕, 是在遮罩層下方, 但是正確響應點擊事件, 這就是事件穿透)
好了, 大體介紹就到這裡, 下面簡單解釋下核心代碼:
delegate回調
YLZHoledView.h
@class YLZHoledView;@protocol YLZHoledViewDelegate - (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; }