UI视图面试相关(上)

UITableView相关

重用机制

cell = [tableView dequeueReusableCellWithIdentifier:identifier];

上面代码就是设置重用标识符:UITableView的cell当滚出屏幕的时候就会加入到重用池,当下一个cell滑入屏幕的时候,从重用池里面取出

UITableView数据源同步

例如主线程在执行删除一行cell操作,子线程执行加载更多数据的操作,两个线程同时操作tableView,就涉及数据源同步的问题。

解决方法有2种:

并行访问 数据拷贝(主线程和子线程都同步做一次删除操作)消耗内存

串行访问(主线程删除cell,等待子线程加载完数据,在串行队列同步数据删除,再刷新UI)消耗时间

UIView和CALayer

(单一职责原则的设计)

UIView包含一个CALayer,UIView为其提供内容,以及负责处理触摸等事件,参与响应链

CALayer负责显示内容contents

事件传递链

跟2个方法有关

//返回响应事件的视图
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{ }
//点击的点是否在所在视图
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{ }

点击屏幕->UIApplication->UIWindow->hitTest->pointInside->倒序遍历subViews(最后一个添加的视图最优先被遍历到)->

每一个subview都会调用hitTest方法(每一个subview的子视图也会调用hitTest方法,类似于递归调用)->hit != nil->结束

hitTest内部实现:

view不隐藏不透明可交互的情况下->pointInside-> 倒序遍历view.subviews->里面再调用hitTest->v != nil->return v;

实现矩形按钮 圆形范围内可以点击

自定义按钮,重写2个方法

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    //该按钮可视可交互
    if (!self.userInteractionEnabled ||
        [self isHidden] ||
        self.alpha <= 0.01) {
        return nil;
    }
    //判断点是否在规定范围l内
    if ([self pointInside:point withEvent:event]) {
        //遍历当前对象的子视图
        __block UIView *hit = nil;
        //有子视图就 倒序遍历子视图
        [self.subviews enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            // 坐标转换
            CGPoint vonvertPoint = [self convertPoint:point toView:obj];
            //调用子视图的hittest方法
            hit = [obj hitTest:vonvertPoint withEvent:event];
            // 如果找到了接受事件的对象,则停止遍历
            if (hit) {
                *stop = YES;
            }
        }];
        
        if (hit) {
            return hit;
        }
        else{
            //没有子视图就返回本按钮
            return self;
        }
    }
    else{
        return nil;
    }
}

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    //为屏幕的frame为基点
    CGFloat x1 = point.x;
    CGFloat y1 = point.y;
    
    //view.bounds
    CGFloat x2 = self.frame.size.width / 2;
    CGFloat y2 = self.frame.size.height / 2;
    //判断以该按钮一般的宽为圆形的可点击范围
    double dis = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    // 67.923
    if (dis <= self.frame.size.width / 2) {
        return YES;
    }
    else{
        return NO;
    }
}

 视图响应者

同级可交互的控件->控件父视图UIView->vc的视图UIView->有vc的话就是UIViewController->UIWindow->UIApplication->UIApplicationDelegate

UIApplicationDelegate如果不响应 那就忽略事件

视图事件响应

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
}
原文地址:https://www.cnblogs.com/WellLin/p/12672836.html