美文网首页IOS Crash
ios 视图越界与事件触发

ios 视图越界与事件触发

作者: 愤斗的小蚂蚁 | 来源:发表于2020-01-10 17:32 被阅读0次

    索引
    1-解决子视图越界部分的事件不被触发
    2-hitTest:withEvent:
    3-关于rect和point的转换总结
    4-rect与point的交集,包含

    子视图越界部分的事件不被触发

    hitTest001.png

    前提:redView里有blueView和yellowBtn,blueView里有greenBtn,
    问题:greenBtn超出了父视图blueView的边界,正常情况下超出区域的动作将失效

    【不完善解决方法】:重写方法-pointInside:withEvent:,直接返回YES

    - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
         return YES; 
    }
    

    【抛出潜在问题】如果按顺序A能解决上述问题,但如果按顺序B会导致yellowBtn点击动作被greenBtn所拦截,所以需要优化完善方法-pointInside:withEvent:

    - (void) setup {
       
        [blueView addSubView:greenBtn];
    
        //  添加子视图顺序-A
        [redView addSubView:blueView];
        [redView addSubVIew:yellowBtn];
    
        //  添加子视图顺序-B
        [redView addSubVIew:yellowBtn];
        [redView addSubView:blueView];
    }
    

    【完善解决方法】


    hitTest001.png
    - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
            
        // blueViewde 区域 || greenBtn超出部分的区域
        if ( CGRectContainsPoint(CGRectMake(0, 0, 200, 120),point) ||
             CGRectContainsPoint(CGRectMake(70, 120, 100, 20), point) ) {
            return YES;
        }
        return NO;
    }
    

    【原理】顺序A和B的区别,归因于事件分发机制 hit-Testing,其作用是找出屏幕的触摸点是属于哪个view的。

    寻找逻辑是:
    先父视图后子视图;
    最后添加的子视图到先添加的子视图;
    递归寻找,直到找到view。

    分别说明顺序A和B:
    A:
    1-检查redView,发现触摸点在redView内,继续检查子视图yellowView
    2-如果发现触摸点在yellowView内,继续递归检查yellowView
    3-如果发现触摸点不在yellowView内,继续递归检查blueView
    B:
    1-检查redView,发现触摸点在redView内,继续检查子视图blueView
    2-如果发现触摸点在blueView内,继续递归检查blueView
    3-如果发现触摸点不在blueView内,继续递归检查blueView

    - hitTest:withEvent:

    // 可以根据自己的需求,重写方法
    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {    
        
       [super hitTest:point withEvent:event];
        
        // Determine whether you can receive touch events
        if ( self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01 ) {     
            return nil;
        }
        
        // Determine if the touch point is out of reach
        if ( ![self pointInside:point withEvent:event] ) {      
            return nil;
        }
        
        NSInteger count = self.subviews.count;
        for ( NSInteger i = count - 1; i >= 0; i-- ) {
    
            UIView *childView = self.subviews[i];
            CGPoint childPoint = [self convertPoint:point toView:childView];//
            UIView *fitView = [childView hitTest:childPoint withEvent:event];
            
            if ( fitView ) {            
                return fitView;
            }
        }
        
        return self;
    }
    

    关于rect和point的转换总结

    toView就是从左往右开始读代码,也是从左往右理解意思
    fromView就是从右往左开始读代码,也是从右往左理解意思

    // rect
    CGRect newRect = [self.yellowView convertRect:self.yellowView.bounds toView:nil];
    在yellowView中,有个和yellowView一样的大小的区域bounds,此区域相对于window的位置

    CGRect newRect = [self.greyView convertRect:self.yellowView.frame toView:nil];
    在greyView中,其子视图yellowView的frame区域,相对于window的位置

    CGRect rec1 = [self.view.window convertRect:self.yellowView.bounds fromView:self.yellowView];
    在yellowView中,有个和yellowView一样大小的区域bounds,此区域相对于window的位置

    上述三种方式,有助于理解frame和bounds的区别

    // point

    • A(CGPoint)convertPoint:B(CGPoint)point toView:C(nullableUIView *)view;
      A区域里面有个坐标B,需要把相对于A的坐标B转换成相对于C的坐标

    • A(CGPoint)convertPoint:B(CGPoint)point fromView:C(nullableUIView *)view;
      从C区域里面转换坐标B,需要把相对于C的坐标转换成相对于A的坐标

    rect与point的交集,包含

    CGRectContainsRect(<#CGRect rect1#>, <#CGRect rect2#>)
    表示rect1和rect2是否相交

    CGRectContainsPoint(<#CGRect rect#>, <#CGPoint point#>)
    表示rect中是否包含point坐标

    CGRectIntersectsRect(<#CGRect rect1#>, <#CGRect rect2#>)
    表示rect1中是否包含rect2

    相关文章

      网友评论

        本文标题:ios 视图越界与事件触发

        本文链接:https://www.haomeiwen.com/subject/ewxmactx.html