美文网首页iOSiOS学习专题ios
UITableView和UIScrollView分析

UITableView和UIScrollView分析

作者: CoderZb | 来源:发表于2016-07-31 08:06 被阅读3054次

    解决添加到ScrollView上的UITableView控件自动向下偏移64像素的问题

    • 首先理解1:即使UITableView没有添加到ScrollView上,UIableView也会向下偏移20像素

      • 原因: 这是历史遗留问题。就是说只要代码中出现了UIableView,那么他被创建出来时,默认坐标为(0,20)
    • 首先理解2:添加到ScrollView上的UITableView控件在20的基础之上,还会再向下偏移64像素

      • 原因: 自动向下偏移64是由于系统的automaticallyAdjustsScrollViewInsets属性默认开启造成的。开启之后,就会自动调整布局。所以会自动向下偏移xx像素。有时候不一定为64像素。但是都是这个属性导致的。所以我们如果想自己修改布局,必须将属性设置为NO。
    • 首先理解3:偏移的这些像素对于UITableView来说都是内边距(ContentInset),即都是UITableView的ContentSize的内边距。因为UITableView的ContentSize的计算不包括内边距,所以这些像素不是ContentSize的内容

    • 添加到ScrollView上的UITableView控件

      • 如果ScrollView上没有导航栏,就向下偏移20像素+xxx像素。xxx像素不确定多少,具体根据你添加了什么控件决定。确定的是有导航栏的情况下

      • 如果ScrollView上有导航栏,那么ScrollView上的控件在默认偏移20像素的基础上,再向下移动64像素。即20像素+64像素。这个64像素是确定的。

      • 不让控件向下移动64像素的做法如代码1:(只是解决了偏移64像素的问题,运行时,UITableView仍然会向下偏移20像素)
        //不要控制器去自动调整scrollView的内边距
        self.automaticallyAdjustsScrollViewInsets = NO;
        官方解释:automaticallyAdjustsScrollViewInsets根据按所在界面的status bar,navigationbar,与tabbar的高度,自动调整scrollview的 inset。
        我们不让UIViewController调整整scrollview的inset,设置为no,然后我们再自己修改布局即可

      • 由于状态栏的存在,控件仍然会向下移动20像素,不让控件向下移动20像素的做法代码2:

        //把子控件的y值设置为0,UITableView显示时y坐标距屏幕顶部为0,解决了UITableView向下偏移20像素的问题
        for (NSInteger i = 0; i < 5; i++) {
          UIViewController *childVcView = self.childViewControllers[i];//取控制器
          childVcView.view.frame = CGRectMake(i * scrollView.zb_width,0, scrollView.zb_width, scrollView.zb_height);//设置控制器的view的frame
          [scrollView addSubview:childVcView.view];//将控制器的view添加到scrollView上面
        
        
    • 总结:仅仅利用代码2将y坐标设置为0是不能够将TableView显示在屏幕顶部的。只有代码1+代码2结合起来才能将TableView显示在顶部。因为你不将代码1自动调整设置为NO,系统仍然会根据当前界面上的控件来自动调整内边距,调整为64,即TableView向下移动64像素。【已验证】-->代码1+代码2在开发中成对出现,缺一不可。


    ScrollView的frame和的contentSize的区别

    • ScrollView的frame的作用:
      • 控制可见范围,超出可见范围看不见
    • ScrollView的contentSize的作用:
      • 限制滚动的范围(contentSize就是他们说的内容或者尺寸或者内容尺寸)

    情况不同决定了UITableView是否需要设置宽高

    • 不是利用alloc init方式创建出来的UITableView,而从控制器的数组中取出来的UITableView本身就有宽高,不需要手动设置宽高。(注意:取出来的是UITableView,因为代码中有.view)
       UIView *childVcView = self.childViewControllers[i].view;
       childVcView.zb_X = i * scrollView.zb_width;
       [scrollView addSubview:childVcView];
            
    
    • 利用alloc init方式创建出来的UITableView必须自己手动设置宽高
    UITableView *tableView = [[UITableView alloc] init];
     tableView.zb_height = scrollView.zb_height;
     tableView.zb_width = scrollView.zb_width;
    

    思想:一个TableView对应一个控制器(本质是类)。一个TableView有自己专门的数据源。

    • 5个TableView对应一个类
      • 因为TableView要显示数据,必须得执行数据源方法才能显示数据到UITableView上。
      • 如果5个TableView对应1个类(即5个TableView都是在同一个类中创建的),那么5个TableView的数据源都是这一个类
    • 那么这个类执行数据源方法的时候,如果在numberOfRowsInSection中不通过tableView.tag进行判断是哪个TableView的话,那么默认5个TableView中的行数都一样。
    • 如果在numberOfRowsInSection中已经通过tag判断出是哪个TableView的前提下,又执行了cellForRowAtIndexPath,这个时候如果不再次进行判断的话,那么5个TableView的cell的内容肯定都一样。
    • 正常情况下,每个TableView都有自己的特点,显示怎么样的cell对于每个TableView来说,肯定都是不一样的。如果都一样,做社交类app,电商类app,当进行切换标题栏的时候,每个TableView的内容都一样,这还看什么劲啊。
    • 通过tag进行很多的if else来判断是哪个TableView的做法非常繁琐,且代码界面混乱,所有的代码都写在一个类中,这个类会类似的。
    • 1个TableView对应1个类
      • 每个TableView都有自己专门的数据源,这样各个TableView显示的cell就会互不影响。也避免了上述利用tag来进行判断是哪个TableView的问题。
      • 优点:每个模块的功能和逻辑让对应的类去管理,后续开发的扩展性强

    5个TableView对应一个类(用于衬托1个TableView对应1个类)

    #import "ViewController.h"
    #import "UIView+Frame.h"
    
    #define ZBColor(r,g,b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1]
    #define ZBRandomColor ZBColor(arc4random_uniform(255), arc4random_uniform(255), arc4random_uniform(255))
    
    
    @interface ViewController ()<UITableViewDataSource>
    //@property (nonatomic,weak)UIScrollView *scrollView;
    @property(nonatomic,strong)UITableViewCell *cell;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        //将scrollView添加到控制器的view上
        UIScrollView *scrollView = [[UIScrollView alloc] init];
        scrollView.frame = self.view.bounds;
        scrollView.backgroundColor = [UIColor redColor];
        [self.view addSubview:scrollView];
        //将5个TableView添加到scrollView上。scrollView的contentSize为5个TableView的宽度
        for (NSInteger i =0; i < 5 ; i++) {
            UITableView *tableView = [[UITableView alloc] init];
            tableView.backgroundColor = ZBRandomColor;
    
            tableView.zb_height = scrollView.zb_height;
            tableView.zb_width = scrollView.zb_width;
            tableView.zb_X = i *tableView.zb_width;
            //5个tableView的数据源都是1个控制器,所以5个TableView上都显示一样的数据
            tableView.dataSource =self;
            tableView.tag=i;
            [scrollView addSubview: tableView];
        }
        scrollView.contentSize = CGSizeMake(5 *self.view.bounds.size.width, 0);
        scrollView.pagingEnabled =YES;
        
    }
    
    
    -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
        if (tableView.tag == 0) return 5;
        if (tableView.tag == 1) return 10;
        if (tableView.tag == 2) return 15;
        if (tableView.tag == 3) return 20;
        else
            return 25;
        
    }
    static NSString * const ID = @"ce";
    -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
        
    if(tableView.tag == 0){
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        if (cell == nil) {
            cell = [[UITableViewCell  alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
            cell.backgroundColor = [UIColor clearColor];
        }
            cell.textLabel.text = [NSString stringWithFormat:@"第%ld个tableView-第%zd个cell",tableView.tag,indexPath.row];
             return cell;
    }else if(tableView.tag == 1){
        
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        if (cell == nil) {
            cell = [[UITableViewCell  alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
            cell.backgroundColor = [UIColor clearColor];
        }
        cell.textLabel.text = [NSString stringWithFormat:@"第%ld个tableView-第%zd个cell",tableView.tag,indexPath.row];
        return cell;
    
    }else if(tableView.tag == 2){
         UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    
        if (cell == nil) {
            cell = [[UITableViewCell  alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
            cell.backgroundColor = [UIColor clearColor];
        }
        cell.textLabel.text = [NSString stringWithFormat:@"第%ld个tableView-第%zd个cell",tableView.tag,indexPath.row];
        return cell;
    }else if(tableView.tag == 3){
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        
        if (cell == nil) {
            cell = [[UITableViewCell  alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
            cell.backgroundColor = [UIColor clearColor];
        }
        cell.textLabel.text = [NSString stringWithFormat:@"第%ld个tableView-第%zd个cell",tableView.tag,indexPath.row];
        return cell;
    }else{
    
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        
        if (cell == nil) {
            cell = [[UITableViewCell  alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
            cell.backgroundColor = [UIColor clearColor];
        }
        cell.textLabel.text = [NSString stringWithFormat:@"第%ld个tableView-第%zd个cell",tableView.tag,indexPath.row];
        return cell;
    
    
    }
    }
    @end
    
    
    • 效果图:
    41-04.gif

    1个TableView对应1个类

    ViewController.h文件

    #import "ViewController.h"
    #import "UIView+Frame.h"
    #import "ZBAllViewController.h"
    #import "ZBPictureViewController.h"
    #import "ZBVideoViewController.h"
    #import "ZBVoiceViewController.h"
    #import "ZBWordViewController.h"
    
    #define ZBColor(r,g,b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1]
    #define ZBRandomColor ZBColor(arc4random_uniform(255), arc4random_uniform(255), arc4random_uniform(255))
    
    
    @interface ViewController ()<UITableViewDataSource>
    //@property (nonatomic,weak)UIScrollView *scrollView;
    @property(nonatomic,strong)UITableViewCell *cell;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
       [super viewDidLoad];
       // 初始化子控制器
       [self setupChildVcs];
       
       // scrollView
       [self setupScrollView];
       
    }
    
    /**
    *  初始化子控制器
    */
    - (void)setupChildVcs
    {
       [self addChildViewController:[[ZBAllViewController alloc] init]];
       [self addChildViewController:[[ZBVideoViewController alloc] init]];
       [self addChildViewController:[[ZBVoiceViewController alloc] init]];
       [self addChildViewController:[[ZBPictureViewController alloc] init]];
       [self addChildViewController:[[ZBWordViewController alloc] init]];
    }
    
    /**
    *  scrollView
    */
    - (void)setupScrollView
    {
       UIScrollView *scrollView = [[UIScrollView alloc] init];
       scrollView.frame = self.view.bounds;
       [self.view addSubview:scrollView];
       
       // 添加5个模块
       for (NSInteger i = 0; i < 5; i++) {
           /*
            3个方法的总结:前提:控件添加到UIScrollView上。
                        方法1没有设置y坐标,所以UITableView显示时会向下偏移20像素
                        方法2和方法3设置了y坐标,并且设置为0,使UITableView显示时y距屏幕顶部为0,所以UITableView显示时会向下偏移20像素
           
            */
           //方法1:仅仅是设置childVcView的x坐标,childVcView的y坐标没有设置。这样会导致程序运行时,childVcView也就是UITableView不是填充整个屏幕,而是向下偏移20像素的距离(状态栏的原因)
           UIView *childVcView = self.childViewControllers[i].view;
           childVcView.zb_X = i * scrollView.zb_width;
           [scrollView addSubview:childVcView];
           
           
           /*  方法2:
           //注意:childVcView指针存着的是控制器的view,而不是控制器,因为等号右侧有.view,所以childVcView这时候代表的是一个view。.所以后面两行代码childVcView都不用加上.view。
           UIView *childVcView = self.childViewControllers[i].view;//取控制器的view
            
            childVcView.frame = CGRectMake(i * scrollView.zb_width, 0, scrollView.zb_width, scrollView.zb_height);//设置视图的frame。
           [scrollView addSubview:childVcView];
           */
           /* 方法3:
           //childViewControllers中的子控制器真实是UITableViewController,因为UITableViewController继承UIViewController,所以childVcView的类型也可以写成UIViewController
           //childVcView指针存着的是控制器,因为没有等号右侧没有.view。所以childVcView这时候代表的是一个控制器,后两行代码childVcView后面必须加上.view。因为我们最终是要把view添加到scrollView上的。
           UIViewController *childVcView = self.childViewControllers[i];//取控制器
           childVcView.view.frame = CGRectMake(i * scrollView.zb_width, 20, scrollView.zb_width, scrollView.zb_height);//设置控制器的view的frame
           [scrollView addSubview:childVcView.view];//将控制器的view添加到scrollView上面
            */
       }
       
       // 其他设置
       scrollView.contentSize = CGSizeMake(5 * scrollView.zb_width, 0);
       scrollView.pagingEnabled = YES;
    }
    
    @end
    

    ZBPictureViewController文件,

    • 其余控制器的代码和以下代码一样,就不一一列出了
    import "ZBPictureViewController.h"
    #define ZBColor(r,g,b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1]
    #define ZBRandomColor ZBColor(arc4random_uniform(255), arc4random_uniform(255), arc4random_uniform(255))
    #import "ZBConst.h"
    @interface ZBPictureViewController ()
    
    @end
    
    @implementation ZBPictureViewController
    
    - (void)viewDidLoad {
       [super viewDidLoad];
       
       self.tableView.backgroundColor = ZBRandomColor;
    }
    #pragma mark - UITableViewDataSource
    
    -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
       
       return 30;
       
    }
    static NSString *ID = @"ce";
    -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
       
       UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:ID];
       if (cell == nil) {
           cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
           cell.backgroundColor = [UIColor clearColor];
       }
       
       cell.textLabel.text = [NSString stringWithFormat:@"%@ - %zd",self.class,indexPath.row];
       return cell;
    }
    
    
    @end
    
    
    • 效果图
    41-03.gif

    addChildViewController和addSubview区分

    • 除了下面的解释,还有一个做法来解决让正确的对象添加到正确的对象身上。
      • addChildViewController是指:后面的参数是一个子控制器,要添加到前面的对象中
      • addSubview是指:后面的参数是一个子View,要添加到前面的对象中
      • 拓展:给某个对象添加颜色backgroundColor,一定要在view上添加
    精华例子1:
    //控制器才能添加到控制器中。因为self代表了控制器,如果某个对象要添加到控制器中,那么这个对象必须是控制器。【苹果公司规定】
     ZBAllViewController *vc1 = [[ZBAllViewController alloc] init];
        [self addChildViewController:vc1];
        
    //View才能添加到View中。因为self.scrollView本身就是个View,它不是控制器,所以如果某个对象要添加到self.scrollView这个view中时,这个而对象必须是View,因为childVC是控制器的对象,所以必须把childVC再进行细化,拿到childVc的view。即使childVc.view
     UIViewController *childVc = self.childViewControllers[index];
     [self.scrollView addSubview:childVc.view];
    
    精华例子2:
    • 超级经典,注意对比
    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        //以下演示的给对象添加backgroundColor,frame等等属性,这些属性一定要在view上进行。
        
        UIView *zb  = [[UIView alloc]init];
        //zb是个view,已经精确到了view,所以直接可以访问backgroundColor,frame属性
        zb.backgroundColor = [UIColor redColor];
        zb.frame =CGRectMake(80, 80, 100,100);
        //self是控制器,必须精确到控制器的view,才能将zb添加到控制器的view上,否则报错。
        [self.view addSubview:zb];
        
        //虽说下面的显示不出运行结果,但是我们的目的达到了
        
        UIViewController *vc = [[UIViewController alloc] init];
        //vc是个控制器,必须精确到vc.view,才能访问backgroundColor,frame属性
        vc.view.backgroundColor = [UIColor yellowColor];
        vc.view.frame =CGRectMake(300, 80, 100, 100) ;
        //vc是个控制器,所以可以添加到self上。这个时候再用self.view就会报错
        [self addChildViewController:vc];
    }
    
    
    
    
    @end
    
    

    UIViewController和UITableViewController创建出来时,y的坐标分别为多少

    40-26.png

    创建TableView的3中方法

    • 方式1.利用自带的ViewController类,继承UIViewController
      • 然后在ViewController.m,遵守协议<UITableViewDataSource, UITableViewDelegate>
      • 设置数据源和代理 tableView.dataSource = self; tableView.delegate = self;
      • 实现数据源方法和代理方法即可
    • 方式2.利用自带的ViewController类,将继承的UIViewController类改为UITableViewController类
      • 在Storyboard选中名字为View的View,然后将Class改为UITableVIew
      • 然后在ViewController.m,直接实现数据源方法和代理方法,不要遵守协议,不需要设置数据源和代理
      • 注意:如果没有继承UITableViewController,而是继承UIViewController,在执行那三个步骤,虽然能显示TableView的样子,但是TableView的cell里面没有任何内容,即使你设置了cell的内容
    • 方式3.利用自带的ViewController类,将继承的UIViewController类改为UITableViewController类
      • 在Storyboard中删除默认的UIViewController控制器,增加一个UITableViewController控制器(别忘记加箭头哦)
      • 然后选中Table View Controller,将class改为ViewController。这就实现了让ViewController类来管理Table View Controller.
        • 如果UIViewController不继承UITableViewController,而是继承UIViewController,那么无法将将class改为ViewController。
        • 因为只有相同类型才能进行管理,storyboard中的Table View Controller是UITableViewController类型的,怎么能让UIViewController类型的类来管理它呢
      • 然后在ViewController.m,直接实现数据源方法和代理方法,不要遵守协议,不需要设置数据源和代理
    方法1代码+截图
    • ViewController.h文件
    #import <UIKit/UIKit.h>
    
    @interface ViewController : UIViewController
    
    
    @end
    
    
    
    • ViewController.m文件
    #import "ViewController.h"
    
    @interface ViewController () <UITableViewDataSource, UITableViewDelegate>
    @property (nonatomic, weak) UITableView *tableView;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        UITableView *tableView = [[UITableView alloc] init];
        tableView.frame = CGRectMake(80, 100, 200, 300);
        tableView.backgroundColor = [UIColor grayColor];
        //数据源
        tableView.dataSource = self;
        //代理
        tableView.delegate = self;
        
        [self.view addSubview:tableView];
        
    
    }
    #pragma mark - <UITableViewDelegate>
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        NSLog(@"contentOffset.y = %f", tableView.contentOffset.y);
        NSLog(@"contentSize.height = %f", tableView.contentSize.height);
    }
    
    #pragma mark - UITableViewDataSource
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return 20;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *ID = @"cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
            cell.backgroundColor = [UIColor redColor];
        }
        cell.textLabel.text = [NSString stringWithFormat:@"%@-%zd", self.class, indexPath.row];
        return cell;
    }
    @end
    
    
    
    • 截图
    41-05.gif
    方法2代码+截图
    • ViewController.h文件
    #import <UIKit/UIKit.h>
    
    @interface ViewController : UITableViewController
    
    
    @end
    
    
    • ViewController.m文件
    
    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
    }
    
    
    #pragma mark -代理方法
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        NSLog(@"contentOffset.y = %f", tableView.contentOffset.y);
        NSLog(@"contentSize.height = %f", tableView.contentSize.height);
    }
    
    #pragma mark - 数据源方法
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return 20;
    }
    #pragma mark - 数据源方法
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *ID = @"cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
            cell.backgroundColor = [UIColor yellowColor];
        }
        cell.textLabel.text = [NSString stringWithFormat:@"%@-%zd", self.class, indexPath.row];
        return cell;
    }
    @end
    
    
    • 截图
    41-02.png
    方法3代码+截图
    • ViewController.h文件
    #import <UIKit/UIKit.h>
    
    @interface ViewController : UITableViewController
    
    
    @end
    
    
    • ViewController.m文件
    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
    }
    
    
    #pragma mark -代理方法
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        NSLog(@"contentOffset.y = %f", tableView.contentOffset.y);
        NSLog(@"contentSize.height = %f", tableView.contentSize.height);
    }
    
    #pragma mark - 数据源方法
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return 20;
    }
    #pragma mark - 数据源方法
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *ID = @"cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
            cell.backgroundColor = [UIColor redColor];
        }
        cell.textLabel.text = [NSString stringWithFormat:@"%@-%zd", self.class, indexPath.row];
        return cell;
    }
    @end
    
    
    
    • 截图效果和方法2一模一样

    UIScrollView的滚动

    • 详看第六天 UIScrollView和UITableView滚动都是一样的。
    • ContentOffset: UIScrollView控件frame距内容尺寸的偏移量. 往左偏移为正数,往右偏移为负数


      41-26.png

    UIScrollView和UITableView的偏移量正负详解

    • UIScrollView偏移量:scrollView的左上角的值减去contentSize的左上角,就是contentOffset,偏移量(有正负)。contentSize(内容尺寸)相对于scrollView向左移动,那么contentOffset就为正数.向右移动,那么contentOffset就为负数。向上移动,为正数,向下移动为负数
    • 计算正负数技巧:
    • contentSize(内容尺寸)向左移动:contentOffset.x = 0 - (-100) = 100.
    • contentSize(内容尺寸)向右移动:contentOffset.x = 0 - 200 = -200.
    • contentSize(内容尺寸)向上移动:contentOffset.y = 0 - (-300) = 300.
    • contentSize(内容尺寸)向上移动:contentOffset.y = 0 - 400 = - 400.
    • UITableView的偏移量:tableView的左上角的值减去contentSize的左上角,就是contentOffset,偏移量(有正负)。contentSize(内容尺寸)相对于tableView向左移动,那么contentOffset就为正数.向右移动,那么contentOffset就为负数。向上移动,为正数,向下移动为负数
    • 计算正负数技巧:
    • 同上

    UIScrollView和UITableView滚动的是内容而不是控件

    
      1.把图片添加到UIScrollView上.本质上是把图片添加到了UIScrollView的内容上。
      我们滚动屏幕时,滚动的不是UIScrollView这个控件(窗口),滚动的是内容.
      我们手动滚动内容时(也可以利用代码控制contentOffset的偏移量实现滚动内容),图片会经过这个UIScrollView(窗口),经过窗口的内容会被我们看到。
      如果你认为图片添加到了UIScrollView这个控件上,假设图片添加到了UIScrollView控件的外侧(屏幕的外侧),这样我们无论怎么滚动,也永远看不到这张图片。因为UIScrollView此时就是一个供我们看东西的窗口,超过窗口的东西我们永远看不到,你把图片添加到了这个窗口的外侧,而不是添加到了内容上,你滚动内容时,内容里面并没有图片,内容经过窗口时,怎么会显示这张图片(UIScrollView就是窗口,通过这个窗口我们才能看到图片,超过这个窗口的图片,我们永远看不到。类比井底之蛙)
    
      2.为什么能滚动?
      contentSize不能滚动,contentSize仅仅是提供了一个供我们滚动的矩形范围。scrollView的内容能滚动(前提:我们必须用手滚内容啊,只要触摸到了内容,就能滚,底层帮我们做好了,不要问为什么)。
      
      3. UIScrollView的内容是啥,包括啥? 内容是虚拟的,我们看不到,但是实际存在,如果你把2张图片通过[self.scrollView addSubview:imageV];的形式添加到了内容中,那么内容就是两张图片。即,执行addSubview:的操作,执行addSubview:方法的参数就是内容,
    
      4.如果你现在仍理解为把图片添加到UIScrollView上,你就无法理解滚动的谁?为什么能滚动?窗口是谁?contentSize的深层次含义?
      
      5.看UITableView动态截图+ppt截图你就理解UIScrollView了:UITableView控件并不能滚动,而是内容能滚动。
    

    UITableView的滚动

    • 前言:不受任何干扰来验证TableView的ContentSize的范围的做法是取消弹簧效果,这样能滚动多少范围就能滚动多少范围。代码:self.tableView.bounces =NO;
    • 以后见到任意一款app,如果里面有滚动的内容,你要想到之所以能滚动,是内容滚动造成的,而不是UITableView的滚动,也不是UIScrollView的滚动,也不是UICollectionView的滚动。他们三个只是提供一个窗口让你滚动,超出这个而窗口我们就看不到窗口之外的内容了。就像井底之蛙一样,井口就是UITableView,是UIScrollView,是UICollectionView,是固定不动的,天上的云彩就是内容,青蛙可以控制云彩的移动,青蛙就是人的手。内容尺寸仅仅是提供了内容所能滚动的范围,
    • UITableView不设置ContentSize也能滚动,系统内部帮我们算好了.所以开发中一般不设置
    • UIScrollView必须设置ContentSize才能滚动
    • ContentSize:内容的 大小/尺寸,这个尺寸构成了矩形。就比如设置UIView的frame就决定了UIView的尺寸和位置
    • 内容尺寸小于UITableView的尺寸(frame)是无法滚动的。你拖动时时能滚动,但是松开手又弹回来了。这不是滚动哦。必须大于UITableView的尺寸才可以滚动的
    //本质:所能滚动的内容尺寸就是一个宽为100,高位200的矩形
        tableView.contentSize = CGSizeMake(100, 200);
        
    //类比UIScrollView设置滚动的内容尺寸
        scrollView.contentSize = CGSizeMake(100, 200);
    
    
    • scrollView添加到控制器的view上.tableView添加到scrollView上
      • 两个都是可以滚动的。
      • 可以实现scrollView负责左右滚动,tableView负责上下滚动。当移动scrollView时,里面的tableView会被移动到屏幕外,当又移到屏幕内时,可以保证这个tableView的内容仍是之前的内容
      • 为什么tableView添加到scrollView上?实际上是添加到scrollView的内容尺寸上。
        • 只有将5个tableView添加到scrollView上,左右滚动scrollView的内容尺寸时,才能显示加到scrollView的内容尺寸中TableView,如果不加到scrollView上,那么左右滚动scrollView的内容尺寸时,因为scrollView中没有添加任何数据,所以只会显示水平指示条,垂直指示条。你不加到scrollView的内容尺寸上,scrollView中凭什么要展示你??

    UITableView的属性:frame,ContentOffset,ContentInset,ContentSize区分

    • 理解的前提:我们滚动的是内容(cell,TableHeadView的高度,TableFooterView高度等等),滚动的范围是ContentSize的frame所形成的矩形。

    • frame:UITableView控件自身的尺寸(矩形)

    • ContentOffset: 内容尺寸距离UITableView控件frame的偏移量,ContentSize(矩形)往上偏移为正数,往下偏移为负数

    • ContentInset:内边距,即内容尺寸周围的间距,即内容尺寸周围添加的边距,是无缝粘着内容尺寸的边缘。例如顶部增加100像素,当拖动TableView的时候,顶部可多拖动100像素,并且是永久多出100像素。不是拖到不能再拖的时候,又弹回来的那种。

    • ContentSize:内容尺寸的大小(宽,高)比如高为100,那么最终只能滚动100的范围,超出滚动的范围在停止拖拽之后由于弹簧效果会被弹回来(可以通过self.tableView.bounces =NO;禁止弹簧效果来验证只能滚动100的范围)。UITableView不设置ContentSize也能滚动

      • 它的内容尺寸的高度=所有cell的总高度+TableHeadView的高度+TableFooterView高度+sectionHeader的高度+sectionFooter的高度.记住:就是不包括内边距
      • ContentInset和TableHeadView和TableFooterView都是紧紧粘着内容尺寸的边缘,如果都存在,那么ContentInset紧贴着TableHeadView的上部,紧贴着TableFooterView的底部。因为TableHeadView和TableFooterView属于内容尺寸的一部分
      • 在手机上用户滚动时,我们拿着6s Plus在(414, 736)尺寸的屏幕上滚动,滚动的是ContentSize所形成的矩形
      • 注意:ContentSize只是用来设置矩形的大小的。我们滚动的形成的这个矩形,这个矩形是看不到的,所以会误让我们以为滚动的是UITableView。
    • UITableView的frame所形成的是个矩形,ContentSize形成的也是个矩形。这两个矩形的左上角的差值就是偏移量

    • ContentSize和内边距是分开的
      • 之所以让内容尺寸的高度等于内容的高度的原因:
        • 内容尺寸决定滚动的范围,只有两者相等,才能确保内容都能全部让我们滚动到。本质上内容尺寸和内容高度一点关系都没有,只是我们手让他们产生了关系
      • 计算tableView内容尺寸的大小(congtentsize)和内边距一点关系都没有,内边距不参与计算tableView内容的大小
        • 根据上面的公式:内容的高度=所有cell的总高度+TableHeadView的高度+TableFooterView高度+sectionHeader的高度+sectionFooter的高度.记住:就是不包括内边距。
        • 通过 内容尺寸的高度=所有cell的总高度+TableHeadView的高度+TableFooterView高度+sectionHeader的高度+sectionFooter的高度.记住:就是不包括内边距。 来确保内容都能全部让我们滚动到
      • 计算偏移量(contentoffset)和内边距(contentInset)没有关系,内边距不参与计算偏移量的多少。
        • 因为偏移量是=内容距UITableView控件frame的偏移量,又因为内容不包括内边距,UITableView控件frame只和自己的本身有关系,所以说偏移量和内边距一点关系都没有

    没有cell,没有contentInset,没有TableHeadView、TableHeaderView的情况

    • 内容的高度=所有cell的总高度+TableHeadView的高度+TableFooterView高度+sectionHeader的高度+sectionFooter的高度.记住:就是不包括内边距。
    • 通过 内容尺寸的高度=所有cell的总高度+TableHeadView的高度+TableFooterView高度+sectionHeader的高度+sectionFooter的高度.记住:就是不包括内边距。 来确保内容都能全部让我们滚动到
    #import "ViewController.h"
    
    @interface ViewController () <UITableViewDelegate>
    @property (nonatomic, weak) UITableView *tableView;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        UITableView *tableView = [[UITableView alloc] init];
        tableView.frame = CGRectMake(100, 100, 200, 300);
        tableView.backgroundColor = [UIColor grayColor];
        tableView.delegate = self;
        tableView.rowHeight = 40;
        [self.view addSubview:tableView];
        self.tableView = tableView;
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        NSLog(@"contentSize.height = %f", self.tableView.contentSize.height);
    }
    #pragma mark - UITableViewDelegate
    -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    
        return 0;
    
    }
    @end
    
    
    • 截图
    41-06.gif
    • ppt截图
    41-16.png

    有cell,没有contentInset,没有TableHeadView、TableHeaderView的情况

    • 内容的高度=所有cell的总高度+TableHeadView的高度+TableFooterView高度+sectionHeader的高度+sectionFooter的高度.记住:就是不包括内边距。
    • 通过 内容尺寸的高度=所有cell的总高度+TableHeadView的高度+TableFooterView高度+sectionHeader的高度+sectionFooter的高度.记住:就是不包括内边距。 来确保内容都能全部让我们滚动到
    #import "ViewController.h"
    
    @interface ViewController () <UITableViewDataSource, UITableViewDelegate>
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        UITableView *tableView = [[UITableView alloc] init];
        tableView.frame = CGRectMake(100, 100, 200, 300);
        tableView.backgroundColor = [UIColor grayColor];
        tableView.dataSource = self;
        tableView.delegate = self;
        tableView.rowHeight = 40;
        
        [self.view addSubview:tableView];
        
        
    }
    
    
    #pragma mark - <UITableViewDelegate>
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        NSLog(@"\t内容尺寸的偏移量y:contentOffset.y = %f", tableView.contentOffset.y);
        NSLog(@"\t内容尺寸的高度:contentSize.height = %f", tableView.contentSize.height);
    }
    
    #pragma mark - 数据源
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        
        return 20;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *ID = @"cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
            cell.backgroundColor = [UIColor redColor];
        }
        cell.textLabel.text = [NSString stringWithFormat:@"%@-%zd", self.class, indexPath.row];
        return cell;
    }
    @end
    
    
    • 运行结果
      • 点击cell,提示偏移量为0,内容尺寸的高度为800.这个800是20个cell 的总高度
    41-07.gif
    • ppt截图
    41-17.png

    有cell,有contentInset,没有TableHeadView、TableHeaderView的情况

    #import "ViewController.h"
    
    @interface ViewController () <UITableViewDataSource, UITableViewDelegate>
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        UITableView *tableView = [[UITableView alloc] init];
        tableView.frame = CGRectMake(100, 100, 200, 300);
        tableView.backgroundColor = [UIColor grayColor];
        tableView.dataSource = self;
        tableView.delegate = self;
        tableView.rowHeight = 40;
        [self.view addSubview:tableView];
        
        // 内边距
        //距tableView内容尺寸顶部的距离为64,距tableView内容尺寸底部的距离为49。即停止拖动时候顶部多出64的距离,底部多出49的距离,永久性的多出这些距离。
        tableView.contentInset = UIEdgeInsetsMake(64, 0, 49, 0);
        
    
    
    }
    
    #pragma mark - <UITableViewDelegate>
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        NSLog(@"\t内容尺寸的偏移量y:contentOffset.y = %f", tableView.contentOffset.y);
        NSLog(@"\t内容尺寸的高度:contentSize.height = %f", tableView.contentSize.height);
    }
    
    #pragma mark - 数据源
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        
        return 20;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *ID = @"cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
            cell.backgroundColor = [UIColor redColor];
        }
        cell.textLabel.text = [NSString stringWithFormat:@"%@-%zd", self.class, indexPath.row];
        return cell;
    }
    @end
    
    
    
    
    
    
    • 截图
    41-08.gif
    • ppt截图
    41-18.png
    详情分析:
    • tableView.contentInset = UIEdgeInsetsMake(64, 0, 49, 0);//顶部内边距64,底部内边距49
    • 程序运行时,因为距tableView内容尺寸顶部的距离为64,距tableView内容尺寸底部的距离为49。即停止拖动时候顶部多出64的距离,底部多出49的距离(永久多出的距离,不恢复)。程序初始运行时显示的控件的状态也就相当于停止拖动的时候的状态。
    • 先把控件显示到指定的位置上,然后才计算偏移量和内容尺寸的高度
      • 点击cell 提示800.说明self.tableView.contentSize.height这个内容尺寸的高度的计算不包括计算contentInset,
      • 点击cell,执行 NSLog(@"contentOffset.y = %f", tableView.contentOffset.y);打印偏移量为-64.因为contentInse不是contentSize的一部分。再加上UITableView矩形框的左上角和contentSize矩形框的左上角不重合,即有差值,所以有偏移量。

    有cell,没有contentInset,有TableHeadView、TableHeaderView的情况

    #import "ViewController.h"
    
    @interface ViewController () <UITableViewDataSource, UITableViewDelegate>
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        UITableView *tableView = [[UITableView alloc] init];
        tableView.frame = CGRectMake(100, 100, 200, 300);
        tableView.backgroundColor = [UIColor grayColor];
        tableView.dataSource = self;
        tableView.delegate = self;
        tableView.rowHeight = 40;
        [self.view addSubview:tableView];
        
        // header - footer
        UIView *header = [[UIView alloc] init];
        header.frame = CGRectMake(0, 0, tableView.frame.size.width, 64);
        header.backgroundColor = [UIColor yellowColor];
        tableView.tableHeaderView = header;
        
        UIView *footer = [[UIView alloc] init];
        footer.frame = CGRectMake(0, 0, tableView.frame.size.width, 49);
        footer.backgroundColor = [UIColor greenColor];
        tableView.tableFooterView = footer;
    
    }
    
    
    
    #pragma mark - <UITableViewDelegate>
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        NSLog(@"\t内容尺寸的偏移量y:contentOffset.y = %f", tableView.contentOffset.y);
        NSLog(@"\t内容尺寸的高度:contentSize.height = %f", tableView.contentSize.height);
    }
    
    #pragma mark - 数据源
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        
        return 20;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *ID = @"cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
            cell.backgroundColor = [UIColor redColor];
        }
        cell.textLabel.text = [NSString stringWithFormat:@"%@-%zd", self.class, indexPath.row];
        return cell;
    }
    @end
    
    
    
    • 截图
    41-09.gif
    • ppt截图
    41-19.png
    详情分析:
    • 点击cell,提示913,说明800+64+49=913,说明self.tableView.contentSize.height这个内容尺寸的高度的计算包括计算tableHeaderView和tableFooterView的高度
    • 点击cell,执行 NSLog(@"contentOffset.y = %f", tableView.contentOffset.y);打印偏移量为0.说明因为tableHeaderView和tableFooterView都是contentSize的一部分

    有cell,有contentInset,有TableHeadView、TableHeaderView的情况

    #import "ViewController.h"
    
    @interface ViewController () <UITableViewDataSource, UITableViewDelegate>
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        UITableView *tableView = [[UITableView alloc] init];
        tableView.frame = CGRectMake(100, 100, 200, 300);
        tableView.backgroundColor = [UIColor grayColor];
        tableView.dataSource = self;
        tableView.delegate = self;
        tableView.rowHeight = 40;
        [self.view addSubview:tableView];
    
        
        // 内边距
        //距tableView内容尺寸顶部的距离为64,距tableView内容尺寸底部的距离为49。即停止拖动时候顶部多出64的距离,底部多出49的距离。
        tableView.contentInset = UIEdgeInsetsMake(64, 0, 49, 0);
        
        // header - footer
        UIView *header = [[UIView alloc] init];
        header.frame = CGRectMake(0, 0, tableView.frame.size.width, 64);
        header.backgroundColor = [UIColor yellowColor];
        tableView.tableHeaderView = header;
        
        UIView *footer = [[UIView alloc] init];
        footer.frame = CGRectMake(0, 0, tableView.frame.size.width, 49);
        footer.backgroundColor = [UIColor greenColor];
        tableView.tableFooterView = footer;
        
    }
    
    
    #pragma mark - <UITableViewDelegate>
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        NSLog(@"\t内容尺寸的偏移量y:contentOffset.y = %f", tableView.contentOffset.y);
        NSLog(@"\t内容尺寸的高度:contentSize.height = %f", tableView.contentSize.height);
    }
    
    #pragma mark - 数据源
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        
        return 20;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *ID = @"cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
            cell.backgroundColor = [UIColor redColor];
        }
        cell.textLabel.text = [NSString stringWithFormat:@"%@-%zd", self.class, indexPath.row];
        return cell;
    }
    @end
    
    
    • 截图
    41-10.gif
    • ppt截图
    41-20.png
    详情分析:
    • 点击cell,偏移量为-64,内容尺寸的高度为913.只是UIScrollVIew的位置和之前的位置不一样了。

    有cell,没有contentInset,没有TableHeadView、TableHeaderView,有额外添加的子控件{0, -40, 375, 40}

    #import "ViewController.h"
    
    @interface ViewController () <UITableViewDataSource, UITableViewDelegate>
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        UITableView *tableView = [[UITableView alloc] init];
        tableView.frame = CGRectMake(100, 100, 200, 300);
        tableView.backgroundColor = [UIColor grayColor];
        tableView.dataSource = self;
        tableView.delegate = self;
        tableView.rowHeight = 40;
        
        [self.view addSubview:tableView];
        
        
        // 额外添加的子控件 会显示在TableVIew的内部
        UIView *zb = [[UIView alloc] init];
        zb.frame = CGRectMake(0, -40, tableView.frame.size.width, 40);
        zb.backgroundColor = [UIColor blueColor];
        [tableView addSubview:zb];
    }
    
    #pragma mark - <UITableViewDelegate>
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        NSLog(@"\t内容尺寸的偏移量y:contentOffset.y = %f", tableView.contentOffset.y);
        NSLog(@"\t内容尺寸的高度:contentSize.height = %f", tableView.contentSize.height);
    }
    
    #pragma mark - 数据源
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        
        return 20;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *ID = @"cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
            cell.backgroundColor = [UIColor redColor];
        }
        cell.textLabel.text = [NSString stringWithFormat:@"%@-%zd", self.class, indexPath.row];
        return cell;
    }
    @end
    
    
    • 截图
    41-11.gif
    • ppt截图
    41-21.png
    详情分析:
    • zb这个view添加到TableView上,那么zb的父控件是UITableView还是UITableView的内容呢?
    • frame是指:以父控件的内容尺寸的左上角为坐标原点。所以zb是添加到UITableView的内容上。即zb是以父控件的内容尺寸的左上角为坐标原点
      所以当我们滚动内容尺寸的时候,添加的这个zb着滚动,如果我们认为是添加到UITableView上,UIableView始终是固定不变的,zb以父控件的坐标原点为原点,因为UITableView一直不变,所以UITableView的坐标原点也一致不变,那么zb一直不变。当我们滚动内容尺寸的时候,zb应当是不移动的。 但是真实情况是,zb会移动,所以我们认为的是错的。所zb到内容尺寸中
    • 向下拖动+gif动画超级好演示 可以看到拖出来一个蓝色的view,就是zb,松手之后又弹回去了,看不见了
    • 偏移量为0,内容尺寸为800.所以内容尺寸不计算子控件的40。可以看看内容尺寸的计算公式,公式中没有子控件这个变量,所以不计算。以后跟着这个公司来就很好理解滚动的TableView了

    有cell,有contentInset,没有TableHeadView、TableHeaderView,有额外添加的子控件{0, -40, 375, 40}

    #import "ViewController.h"
    
    @interface ViewController () <UITableViewDataSource, UITableViewDelegate>
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        UITableView *tableView = [[UITableView alloc] init];
        tableView.frame = CGRectMake(100, 100, 200, 300);
        tableView.backgroundColor = [UIColor grayColor];
        tableView.dataSource = self;
        tableView.delegate = self;
        tableView.rowHeight = 40;
        [self.view addSubview:tableView];
        // 内边距
        //距tableView内容尺寸顶部的距离为64,距tableView内容尺寸底部的距离为49。即停止拖动时候顶部多出64的距离,底部多出49的距离。
        tableView.contentInset = UIEdgeInsetsMake(64, 0, 49, 0);
      
        // 额外添加的子控件 会显示在TableVIew的内部
        UIView *header = [[UIView alloc] init];
        header.frame = CGRectMake(0, -40, tableView.frame.size.width, 40);
        header.backgroundColor = [UIColor blueColor];
        [tableView addSubview:header];
    }
    
    #pragma mark - <UITableViewDelegate>
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        NSLog(@"\t内容尺寸的偏移量y:contentOffset.y = %f", tableView.contentOffset.y);
        NSLog(@"\t内容尺寸的高度:contentSize.height = %f", tableView.contentSize.height);
    }
    
    #pragma mark - 数据源
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        
        return 20;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *ID = @"cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
            cell.backgroundColor = [UIColor redColor];
        }
        cell.textLabel.text = [NSString stringWithFormat:@"%@-%zd", self.class, indexPath.row];
        return cell;
    }
    @end
    
    • 截图


      41-12.gif
    • ppt截图
    41-22.png
    详细分析:
    • 运行时,顶部内边距我64,有40被蓝色的子控件挡住
    • 精华理解:contentInset先占据64,把ContentSize挤下去64,然后蓝色的子控件添加到ContentSize的顶部(被挤下去的顶部)

    有cell,没有contentInset,有TableHeadView、TableHeaderView,有额外添加的子控件{0, -40, 375, 40}

    
    #import "ViewController.h"
    
    @interface ViewController () <UITableViewDataSource, UITableViewDelegate>
    @property (nonatomic, weak) UITableView *tableView;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        UITableView *tableView = [[UITableView alloc] init];
        tableView.frame = CGRectMake(100, 100, 200, 300);
        tableView.backgroundColor = [UIColor grayColor];
        tableView.dataSource = self;
        tableView.delegate = self;
        tableView.rowHeight = 40;
    
        [self.view addSubview:tableView];
        self.tableView = tableView;
    
        // header - footer
        UIView *header = [[UIView alloc] init];
        header.frame = CGRectMake(0, 0, tableView.frame.size.width, 64);
        header.backgroundColor = [UIColor yellowColor];
        tableView.tableHeaderView = header;
        
        UIView *footer = [[UIView alloc] init];
        footer.frame = CGRectMake(0, 0, tableView.frame.size.width, 49);
        footer.backgroundColor = [UIColor greenColor];
        tableView.tableFooterView = footer;
        
        // 额外添加的子控件 会显示在TableVIew的内部
        UIView *zb = [[UIView alloc] init];
        zb.frame = CGRectMake(0, -40, tableView.frame.size.width, 40);
        zb.backgroundColor = [UIColor blueColor];
        [tableView addSubview:zb];
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        NSLog(@"\n \n 内容尺寸的高度:contentSize.height = %f", self.tableView.contentSize.height);
    }
    
    #pragma mark - <UITableViewDelegate>
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        NSLog(@"\t内容尺寸的偏移量y:contentOffset.y = %f", tableView.contentOffset.y);
        NSLog(@"\t内容尺寸的高度:contentSize.height = %f", tableView.contentSize.height);
    }
    
    #pragma mark - 数据源
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        
        return 20;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *ID = @"cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
            cell.backgroundColor = [UIColor redColor];
        }
        cell.textLabel.text = [NSString stringWithFormat:@"%@-%zd", self.class, indexPath.row];
        return cell;
    }
    @end
    
    
    
    • 截图
    41-13.gif
    • ppt截图
    41-23.png

    有cell,有contentInset,有TableHeadView、TableHeaderView,有额外添加的子控件{0, -40, 375, 40}

    #import "ViewController.h"
    
    @interface ViewController () <UITableViewDataSource, UITableViewDelegate>
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        UITableView *tableView = [[UITableView alloc] init];
        tableView.frame = CGRectMake(100, 100, 200, 300);
        tableView.backgroundColor = [UIColor grayColor];
        tableView.dataSource = self;
        tableView.delegate = self;
        tableView.rowHeight = 40;
        [self.view addSubview:tableView];
    
        
        // 内边距
            //距tableView内容尺寸顶部的距离为64,距tableView内容尺寸底部的距离为49。即停止拖动时候顶部多出64的距离,底部多出49的距离。
        tableView.contentInset = UIEdgeInsetsMake(64, 0, 49, 0);
        
        // header - footer
        UIView *header = [[UIView alloc] init];
        header.frame = CGRectMake(0, 0, tableView.frame.size.width, 64);
        header.backgroundColor = [UIColor yellowColor];
        tableView.tableHeaderView = header;
        
        UIView *footer = [[UIView alloc] init];
        footer.frame = CGRectMake(0, 0, tableView.frame.size.width, 49);
        footer.backgroundColor = [UIColor greenColor];
        tableView.tableFooterView = footer;
    
        // 额外添加的子控件 会显示在TableVIew的内部
        UIView *zb = [[UIView alloc] init];
        zb.frame = CGRectMake(0, -40, tableView.frame.size.width, 40);
        zb.backgroundColor = [UIColor blueColor];
        [tableView addSubview:zb];
    }
    
    
    
    #pragma mark - <UITableViewDelegate>
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        NSLog(@"\t内容尺寸的偏移量y:contentOffset.y = %f", tableView.contentOffset.y);
        NSLog(@"\t内容尺寸的高度:contentSize.height = %f", tableView.contentSize.height);}
    
    #pragma mark - 数据源
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
       
        return 20;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *ID = @"cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
            cell.backgroundColor = [UIColor redColor];
        }
        cell.textLabel.text = [NSString stringWithFormat:@"%@-%zd", self.class, indexPath.row];
        return cell;
    }
    @end
    
    • 截图
    41-15.gif
    • ppt截图
    41-24.png

    没有cell,没有contentInset,有TableHeadView、TableHeaderView,没有有额外添加的子控件{0, -40, 375, 40}

    #import "ViewController.h"
    
    @interface ViewController ()
    @property (nonatomic, weak) UITableView *tableView;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
      [super viewDidLoad];
      
      UITableView *tableView = [[UITableView alloc] init];
      tableView.frame = CGRectMake(100, 100, 200, 300);
      tableView.backgroundColor = [UIColor grayColor];
      tableView.rowHeight = 40;
    
      [self.view addSubview:tableView];
      self.tableView = tableView;
    
      // header - footer
      UIView *header = [[UIView alloc] init];
      header.frame = CGRectMake(0, 0, tableView.frame.size.width, 64);
      header.backgroundColor = [UIColor yellowColor];
      tableView.tableHeaderView = header;
      
      UIView *footer = [[UIView alloc] init];
      footer.frame = CGRectMake(0, 0, tableView.frame.size.width, 49);
      footer.backgroundColor = [UIColor greenColor];
      tableView.tableFooterView = footer;
    
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
      NSLog(@"\t内容尺寸的高度:contentSize.height = %f", self.tableView.contentSize.height);
    }
    
    @end
    
    
    • 截图
    41-14.gif
    • ppt截图
    41-25.png

    详细分析:

    点击控制器的touchbegan 打印113,就是64+49=113. 并且拖动内容尺寸,脱完松手,又会弹回来,因为内容尺寸小于UITableView的尺寸


    精华截图动画

    43-01.gif

    全屏穿透效果知识点:

    • 让TableView的frame为窗口的大小。

    • 为每个TableView在顶部添加内间距,距离为=状态栏+导航栏+标题栏。为每个TableView在底部添加内边距,距离为=tabBar的高度(49)
      self.tableView.contentInset = UIEdgeInsetsMake(ZBNavBarMaxY+ZBTitlesViewH, 0, ZBTabBarH, 0);

    • 隐藏滚动条
      self.tableView.scrollIndicatorInsets = self.tableView.contentInset;

    • 既能穿透导航栏,又能让底部的tabbar不挡住最下面的cell的做法
      给tableView在上下增加内边距,滑动到最下方,由下方的内边距顶着,所以最下面的cell不会被挡住

    相关文章

      网友评论

      本文标题:UITableView和UIScrollView分析

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