UI

作者: Coder_JdHo | 来源:发表于2020-12-15 09:59 被阅读0次

    # 事件传递和响应链

    **事件传递:**

    UIApplication->UIWindow->UIViewController->View->subView (从UIWindow开始会使用hitTest:withEvent方法倒序遍历来寻找最适合响应事件的view)

    **响应者链:**

    如果上面找到的最适合的view不响应事件,就会往下传递,传给下一个响应者。subView->View->UIViewController->UIWindow->UIApplication

    ```

    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event  //此方法返回一个最适合响应事件的view,由最后添加的子视图开始遍历。(另:内部会调用pointInside:withEvent:方法)

    - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event  //此方法返回一个BOOL值,判断点击点是否在当前视图上

    ```

    内部实现:

    ```

    - (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.从后往前遍历子控件数组

      int count = (int)self.subviews.count;

      for (int 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;

          }

      }

      // 4.没有找到更合适的view,也就是没有比自己更合适的view

      return self;

    }

    // 作用:判断下传入过来的点在不在方法调用者的坐标系上

    // point:是方法调用者坐标系上的点

    - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event

    {

      // 判断点在不在区域内

      if (CGRectContainsPoint(self.button.bounds, tempoint))

      {

          return YES ;

        }

    return NO ;

    }

    ```

    # View的刷新

    setNeedsDisplay:会自动调用drawRect方法

    setNeedsLayout:会默认调用layoutSubViews方法

    setNeedsDisplay多用于绘图,setNeedsLayout多用于加载数据后更新UI

    LayoutSubviews在以下情况下会被调用:

    addSubview、改变view的Frame、滚动UIScrollView、旋转屏幕会触发父UIView的layoutSubviews方法、调用setLayoutSubviews、调用LayoutIfNeeded。以上情况都是会触发layoutSubviews方法的。(注:init初始化的时候是不会触发的)

    LayoutIfNeeded 如果,有需要刷新的标记,立即调用layoutSubviews进行布局

    # 扩大按钮响应区

    重写- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 方法

    # touch、gesture、action方法哪个优先调用

    Button的响应优先级touch -> action(UIControlEventTouchDown) -> gesture

    - touchBegin会拦截事件,action不会

    # 离屏渲染

    **概念:**在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。

    **导致离屏渲染的情况:**光栅化、遮罩、圆角、阴影

    - 光栅化是将一个layer预先渲染成位图(bitmap),然后加入缓存中

    - 下面方法添加圆角会造成离屏渲染。(可通过绘图技术,绘制出圆角图片)

    ```

    self.view.layer.cornerRadius = 5.0f;

    self.view.layer.masksToBounds = YES;

    ```

    # TableView方法调用顺序

    ```

    tableView:numberOfRowsInSection:

    tableView:estimatedHeightForRowAtIndexPath:  //如果没有使用预估行高,此次会先调用tableView:heightForRowAtIndexPath:

    tableView:cellForRowAtIndexPath:

    tableView:heightForRowAtIndexPath:

    ```

    #  UITableView 的优化

    1). 正确的复用cell。

    2). 设计统一规格的Cell

    3). 提前计算并缓存好高度(布局),因为heightForRowAtIndexPath:是调用最频繁的方法;

    4). 异步绘制,遇到复杂界面,遇到性能瓶颈时,可能就是突破口;

    5). 减少子视图的层级关系

    6). 尽量使所有的视图不透明化以及做切圆操作。(使用绘图技术来实现圆角)

    7). 不要动态的add 或者 remove 子控件。最好在初始化时就添加完,然后通过hidden来控制是否显示。

    8). 使用调试工具分析问题。(Time Profile)

    # 如何实现cell的动态的行高

    如果希望每条数据显示自身的行高:

    设置预估行高 tableView.estimatedRowHeight = 200。

    设置定义行高 tableView.rowHeight = UITableViewAutomaticDimension。

    另外cell里面的自动布局如果要有高度的约束。

    # ViewController生命周期

    按照执行顺序排列:

    1. init

    2. awakeFromNib:nib文件被加载的时候,会发生一个awakeFromNib的消息到nib文件中的每个对象。  (当有xib文件时)   

    3. loadView:开始加载视图控制器自带的view。

    4. viewDidLoad:视图控制器的view被加载完成。 

    5. viewWillAppear:视图控制器的view将要显示在window上。

    6. updateViewConstraints:视图控制器的view开始更新AutoLayout约束。

    7. viewWillLayoutSubviews:视图控制器的view将要更新内容视图的位置。

    8. viewDidLayoutSubviews:视图控制器的view已经更新视图的位置。

    9. viewDidAppear:视图控制器的view已经展示到window上。

    10. viewWillDisappear:视图控制器的view将要从window上消失。

    11. viewDidDisappear:视图控制器的view已经从window上消失。

    12. dealloc

    # CoreAnimation和CoreGraphics

    **CoreGraphics(核心图形)**

    1. 它是iOS的核心图形库,包含Quartz2D绘图API接口,常用的是point,size,rect等这些图形,都定义在这个框架中,类名以CG开头的都属于CoreGraphics框架,它提供的都是C语言函数接口,是可以在iOS和mac OS 通用的。

    2. iOS系统本身提供了两套绘图的框架,即UIBezierPath 和 Core Graphics。而前者所属UIKit,其实是对Core Graphics框架关于path的进一步封装,所以使用起来比较简单。但是毕竟Core Graphics更接近底层,所以它更加强大。

    **CoreAnimation(核心动画)**

            CoreAnimation翻译过来就是核心动画,一组非常强大的API,用来做动画的,非常的简单,但是效果非常绚丽。

    1. CoreAnimation是跨平台的,既可以支持IOS,也支持MAC OS。

    2. CoreAnimation执行动画是在后台,不会阻塞主线程。

    3. CoreAnimation作用在CALayer,不是UIView。

    4. CoreGraphics和CoreAnimation的关系:它们都是跨iOS和Mac OS 使用的,这点区别于UIKit,并且CoreAnimation中大量使用到CoreGraphics中的类,因为实现动画要用到图形库中的东西。

    5. 可以看出,CoreGraphics是底层绘制框架,我们实际会用到的也就是CG开头的一些底层绘制函数和变量,这是一个纯C语言框架。

    6. QuartzCore(包含CoreAnimation)框架,是iOS系统的基本渲染框架,是一个OC语言框架,是一套基于CoreGraphics的OC语言封装,封装出了基本渲染类CALayer

    # 如何防止截屏。 截屏原理

    配置文件可以使用Apple Configurator 2配置工具(可通过App Store下载)生成,然后通过MDM服务器下发给相关设置,即可实现控制相关系统功能。

    下载Apple Configurator 2后,可通过文件->新建描述文件 生成后缀为.mobileconfig配置文件。

    但是这种方式只使用于企业内部或教育相关机构,前提是企业或相关机构可以控制这些设备的使用行为和使用方式,显然这种方式不适合通过App Store分发应用的情况。

    通过App Store分发的应用目前尚无有效的方式来实现禁止截屏操作。

    不过从iOS7开始Apple提供了UIApplicationUserDidTakeScreenshotNotification通知来告知用户已经进行截屏操作。可以监听该通知并进行相应的提示。

    # scrollview offset和inset

    scrollView.contentOffset = CGPointMake(0, 20); //content向下偏移20

    scrollView.contentInset = UIEdgeInsetsMake(30, 30, 30, 30); //四周增加30可滑动区域

    相关文章

      网友评论

          本文标题:UI

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