UI相关

2020-05-22  本文已影响0人  天明天

重用机制、数据源同步、图像显示、UI掉帧卡顿、UIView的异步绘制、离屏渲染

截屏2021-01-30 下午5.13.21.png
1.重用机制

cell = [tableView dequeueResuableCellWithIdentifer:@"cell_identifer"]
例子:字母索引条的应用
创建重用池
.h

// 实现重用机制的类
@interface ViewReusePool : NSObject

// 从重用池当中取出一个可重用的view
- (UIView *)dequeueReusableView;

// 向重用池当中添加一个视图
- (void)addUsingView:(UIView *)view;

// 重置方法,将当前使用中的视图移动到可重用队列当中
- (void)reset;
@end

.m

@interface ViewReusePool ()
// 等待使用的队列
@property (nonatomic, strong) NSMutableSet *waitUsedQueue;
// 使用中的队列
@property (nonatomic, strong) NSMutableSet *usingQueue;
@end

@implementation ViewReusePool

- (id)init{
    self = [super init];
    if (self) {
        _waitUsedQueue = [NSMutableSet set];
        _usingQueue = [NSMutableSet set];
    }
    return self;
}

- (UIView *)dequeueReusableView{
    UIView *view = [_waitUsedQueue anyObject];
    if (view == nil) {
        return nil;
    }
    else{
        // 进行队列移动
        [_waitUsedQueue removeObject:view];
        [_usingQueue addObject:view];
        return view;
    }
}

- (void)addUsingView:(UIView *)view
{
    if (view == nil) {
        return;
    }
    
    // 添加视图到使用中的队列
    [_usingQueue addObject:view];
}

- (void)reset{
    UIView *view = nil;
    while ((view = [_usingQueue anyObject])) {
        // 从使用中队列移除
        [_usingQueue removeObject:view];
        // 加入等待使用的队列
        [_waitUsedQueue addObject:view];
    }
}

2.数据源同步问题

问题描述:


截屏2020-05-18 上午10.38.27.png

主线程删除 数据,子线程添加更多数据,实现数据的同步
解决方案:

各自都有优缺点:一个需要比较大的内存开销,需要拷贝数据,一个主线程需要等待。

3.视图的事件传递以及相应

(1) UIViewCALayer 的区别与联系

截屏2020-05-18 上午10.51.32.png
- (UIview*)hitTest:(CGPoint)point withEvevt:(UIEvent*)event;
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event;

注意是 倒序便利子视图,后添加的先调用其对应hitTest 方法

底层具体实现如下 :
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    // 1.判断当前控件能否接收事件
    if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
    // 2. 判断点在不在当前控件
    if ([self pointInside:point withEvent:event] == NO) return nil;
    // 3.从后往前遍历自己的子控件
    NSInteger count = self.subviews.count;
    for (NSInteger i = count - 1; i >= 0; i--) {
        UIView *childView = self.subviews[i];
        // 把当前控件上的坐标系转换成子控件上的坐标系
        CGPoint childP = [self convertPoint:point toView:childView];
        UIView *fitView = [childView hitTest:childP withEvent:event];
        if (fitView) { // 寻找到最合适的view
            return fitView;
        }
    }
    // 循环结束,表示没有比自己更合适的view
    return self;
}

参考链接:https://www.jianshu.com/p/f55b613b564e
(3) 实际应用:

按钮 点击圆形响应,其他地方不响应方法:

//重写hitTest方法,重写pointInstend 方法
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if (!self.userInteractionEnabled ||
        [self isHidden] ||
        self.alpha <= 0.01) {
        return nil;
    }
    
    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
{
    CGFloat x1 = point.x;
    CGFloat y1 = point.y;
    
    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;
    }
}

(4)视图响应机制

主要有三种响应事件

触摸事件
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;

传感器事件
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;

远程控制事件
- (void)remoteControlReceivedWithEvent:(UIEvent *)event;

响应者链条其实就是很多响应者对象(继承自UIResponder的对象)一起组合起来的链条称之为响应者链条
一般默认做法是控件将事件顺着响应者链条向上传递,将事件交给上一个响应者进行处理 (即调用super的touches方法)。

UIApplication-->UIWindow-->递归找到最合适处理的控件-->控件调用touches方法-->判断是否实现touches方法-->没有实现默认会将事件传递给上一个响应者-->找到上一个响应者-->找不到方法作废

一句话总结整个过程是:触摸或者点击一个控件,然后这个事件会从上向下(从父->子)找最合适的view处理,找到这个view之后看他能不能处理,能就处理,不能就按照事件响应链向上(从子->父)传递给父控件
事件的传递和响应的区别:
事件的传递是从上到下(父控件到子控件),事件的响应是从下到上(顺着响应者链条向上传递:子控件到父控件。

# [iOS 子视图超出父视图范围点击事件处理!](https://www.cnblogs.com/yujidewu/p/5684029.html)

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{

 UIView *view = [super hitTest:point withEvent:event];

  NSLog(@"1-----%f------%f",point.x,point.y);
  // 将point的x,y从以self为坐标系转换到以self.fb为坐标系进行参考
  CGPoint buttonPoint = [self.fb convertPoint:point fromView:self];
  NSLog(@"2-----%f------%f",buttonPoint.x,buttonPoint.y);
  if ([self.fb pointInside:buttonPoint withEvent:event]) {
    return self.fb;
  }
  return view;
}
//两者一样

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView *view = [super hitTest:point withEvent:event];
    if (view == nil) {
        CGPoint tempoint = [self.senderBtn convertPoint:point fromView:self];
        if (CGRectContainsPoint(self.senderBtn.bounds, tempoint))
        {
            view = self.senderBtn;
        }
    }
    return view;
}
//此方法写在父视图的方法中

4.图像显示原理

主要用于解决UI掉帧卡段相关面试
UI显示图层流程


截屏2020-05-18 下午2.17.00.png

5.UI卡顿、掉帧的原因

主要是:CPU与GPU的处理时间超过16.7ms,导致缓冲帧没有准备好,下一次Vsync信号到来后 渲染出现延迟。

6.UIView的绘制原理

开辟子线程


截屏2020-05-18 下午2.55.32.png

7.离屏渲染相关

(1) 当屏渲染与离屏渲染

截屏2020-05-18 下午3.12.00.png
GPU相关
(2)触发条件
截屏2020-05-18 下午3.14.16.png

圆角要与maskToBounds 一起设置才起作用
(3)为何要避免离屏渲染
离屏渲染 会增加GPU的工作量,间接导致CPU 与GPU的工作耗时大于16.7ms,导致UI的卡顿跟掉帧

截屏2020-05-18 下午3.18.23.png
截屏2021-01-30 下午5.10.49.png
上一篇 下一篇

猜你喜欢

热点阅读