前言
彈幕在現在的各類視頻中都有,也是每位開發者們必須會的一個功能,最近在開發中就遇到了一些問題,下面簡單說說彈幕開發碰到的兩個小問題。
正文
在iOS中,屏幕每秒鐘重繪60次。如果動畫時長比60分之一秒要長,Core Animation就需要在設置一次新值和新值生效之間,對屏幕上的圖層進行重新組織。這意味著CALayer除了“真實”值(就是你設置的值)之外,必須要知道當前顯示在屏幕上的屬性值的記錄。
每個圖層屬性的顯示值都被存儲在一個叫做呈現圖層的獨立圖層當中,他可以通過-presentationLayer方法來訪問。這個呈現圖層實際上是模型圖層的復制,但是它的屬性值代表了在任何指定時刻當前外觀效果。換句話說,你可以通過呈現圖層的值來獲取當前屏幕上真正顯示出來的值。
補充:模型圖層在動畫開始的那一刻就已經達到終點位置,響應點擊事件的也是它。
解決辦法:
重寫彈幕容器 view 的 touchesBegan 方法。代碼如下:
@interface ZYYBarrageView ()@property (nonatomic, strong) UIView *redView; // 將要做平移的 subview@end@implementation ZYYBarrageView- (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self commonInit]; } return self;}- (void)commonInit { self.redView = [[UIView alloc] initWithFrame:CGRectMake(0.f, 0.f, 30.f, 30.f)]; self.redView.backgroundColor = [UIColor redColor]; [self addSubview:self.redView];}- (void)touchesBegan:(NSSet<uitouch *> *)touches withEvent:(UIEvent *)event { // 重點開始!!UITouch 獲取在 barrageView 坐標系下的坐標 CGPoint touchPoint = [[touches anyObject] locationInView:self]; // 判斷觸摸點是否在 redView 的呈現樹的框框之中 if ([self.redView.layer.presentationLayer hitTest:touchPoint]) { // 響應紅色塊點擊 return; } else { }}</uitouch *>
進一步的需求:在 ZYYBarrageView 的同一層級,但層次偏后會有 UIButton。正常情況下,因為 ZYYBarrageView 的存在,UIButton 是無法響應點擊事件的。代碼如下:
@property (nonatomic, strong) ZYYBarrageView *barrageView; // 彈幕 view 支持多行 view 在里面進行運動@property (nonatomic, strong) UIButton *yellowBtn; // 靠后的 UIButton- (void)viewDidLoad { [super viewDidLoad]; // self.yellowBtn 位于 self.barrageView 之后 [self.view addSubview:self.yellowBtn]; [self.view addSubview:self.barrageView];}- (ZYYBarrageView *)barrageView { if (!_barrageView) { _barrageView = [[ZYYBarrageView alloc] initWithFrame:CGRectMake(0.f, 30.f, SCREEN_WIDTH, 30.f)]; _barrageView.backgroundColor = [UIColor clearColor]; } return _barrageView;}- (UIButton *)yellowBtn { if (!_yellowBtn) { _yellowBtn = [UIButton buttonWithType:UIButtonTypeCustom]; _yellowBtn.frame = CGRectMake(90.f, 30.f, 80.f, 30.f); _yellowBtn.backgroundColor = [UIColor yellowColor]; [_yellowBtn setTitle:@"黃色按鈕" forState:UIControlStateNormal]; [_yellowBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; [_yellowBtn addTarget:self action:@selector(onYellowBtn:) forControlEvents:UIControlEventTouchUpInside]; } return _yellowBtn;}- (void)onYellowBtn:(id)sender { // 響應黃色按鈕}
怎么辦?
Responder Chain 原理講解:手指點擊屏幕,經過系統響應(之前過程省略不說,文末有參考鏈接),調用 UIApplication 的 sendEvent: 方法,將 UIEvent 傳給 UIWindow, 通過遞歸調用 UIView 層級的 hitTest(_:with:)
,結合 point(inside:with:)
找到 UIEvent 中每一個UITouch 所屬的 UIView(其實是想找到離觸摸事件點最近的那個 UIView)。這個過程是從 UIView 層級的最頂層往最底層遞歸查詢。同一層級的 UIView,會優先深度遍歷界面靠前的 UIView。找到最底層 UIView 后,沿著 Responder Chain 逐步向上傳遞(UIControl 子類默認會攔截傳遞)。
解決思路:重寫 ZYYBarrageView 的 hitTest(_:with:)
方法。代碼如下:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { BOOL isPointInsideSubview = [self.redView.layer.presentationLayer hitTest:point]; if (isPointInsideSubview == NO) { // 如果沒有點擊在移動的 redView 上,返回 nil // 系統會去遍歷位于 ZYYBarrageView 后面的 UIButton,UIButton 能得到響應 return nil; } else { return [super hitTest:point withEvent:event]; }}
如此,可以完美解決啦~
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對武林網的支持。
新聞熱點
疑難解答