美文网首页iOS开发自己尝试等装一下牛叉的demo
VTMagic 搭建一个iOS新闻类APP及使用心得

VTMagic 搭建一个iOS新闻类APP及使用心得

作者: Andi | 来源:发表于2018-06-15 11:36 被阅读62次
    • 还记得那是2年前的 6月份, 那时的我早上上班第一件事就是上简书 看看最新的技术文章,因为那时候每天平均都有10几篇iOS技术文章新鲜出炉,可惜自从XXX事件后,就没那种盛景了,好想写一首长恨歌。
      2016.06.05 当九流书生 发布VTMagic的使用介绍 第一时间下载下来,运行后真的感觉高大尚,本人也做过新闻类APP ,也封装过类似的框架级控件,但就如 九流书生 文章里所说的一样:大多都比较粗糙,不利于后续维护和扩展.
    • 我也研究了很多 同类型的库,最终还是选择了VTMagic,所以研究一阵 直接重构了项目 并且稳定运行1年多了

    对VTMagic 有一些 使用上的心得 写个新闻类demo 跟大家分享一下
    点击 ZBNews 下载 查看代码
    demo 所用接口是网络抓取 可能会在未来某个时候无法使用

    1. 因为作者九流书生 很长时间没有更新pods了,VTMagic 现在pods版本是1.2.4,并没有适配iOS11。 所以你如果使用pods 引用VTMagic 建议要用以下格式. 会多个属性displayCentered 和适配iOS11
    pod 'VTMagic', :git => 'https://github.com/tianzhuo112/VTMagic.git'
    
    1. 上面提到displayCentered 属性 是被选中的menuItem是否居中显示 ,老版本VTMagic 被选中的menuItem 是不居中的。而是靠previewItems 属性控制的
    /**
    *  被选中的menuItem是否居中显示,默认NO
    */
    @property (nonatomic, assign) BOOL displayCentered;
    
    /**
    *  导航菜单item的预览数,默认为1
    *
    *  @warning displayCentered为YES时,该属性无效
    */
    @property (nonatomic, assign) NSUInteger previewItems;
    
    1. 如果 你的APP 要隐藏导航栏 ,并且还需要系统的返回滑动手势,不要使用
      [self.navigationController setNavigationBarHidden:YES animated:YES]; 此方法会屏蔽系统返回滑动手势。
      要使用 self.navigationController.navigationBar.hidden = YES;
      在要跳转的页面 有需要显示导航栏的页面
      在viewWillAppear 方法里self.navigationController.navigationBar.hidden = NO;即可
    //MainViewController
    - (void)viewWillAppear:(BOOL)animated {
       [super viewWillAppear:animated];
       //此方法会使 系统侧滑返回手势失效
       //[self.navigationController setNavigationBarHidden:YES animated:YES];
       self.navigationController.navigationBar.hidden = YES;
    }
    
    1. 看各大新闻APP 如果把子视图比做ABC.. .都是在浏览A 界面自动加载数据后 在往右滑动到B界面 或是多个界面,在回到A界面时数据不会自动加载数据了,除非下拉刷新,或是过了一定时间在回到A界面才能在自动加载数据。 VTMagic是在在子视图控制器中加载数据的请求前 要加入此判断.
      另外建议在 viewWillDisappear 做取消网络请求和结束刷新的操作
    - (void)viewDidAppear:(BOOL)animated {
       [super viewDidAppear:animated];
      NSTimeInterval currentStamp = [[NSDate date] timeIntervalSince1970];
       if (self.dataArray.count&& currentStamp - _mainModel.lastTime < timeOut) {
           return; //必须加此判断  否则会出现数据重复加载
       }
       _mainModel.lastTime = currentStamp;
          //网络请求方法 或是 进入刷新状态
    }
    
    - (void)viewWillDisappear:(BOOL)animated {
       [super viewWillDisappear:animated];
       [self.viewModel cancelRequestWithMenuInfo:_menuInfo]; // 取消不必要的网络请求
       [self endRefresh];
    }
    
    1. 预加载 这是VTMagic 尤为突出的 功能,但是部分人并不会使用此功能 ,包括作者的VTMagic Demo 也没有完全发挥此功能。
      我说的前提是 needPreloading=YES;//预加载开关 为开的状态 ,如果不用预加载的 请忽略本条。
      如果对VTMagic有了解的同学都知道 VTMagic 的子视图 都是在 viewDidAppear 进行网络请求,
      可我们在进行页面左右滑动的时候并没有 视觉上并没有感受到预加载,感觉还是从A滑动到了B页面显示全部后B才去加载数据的,如果在加上自动触发下拉刷新的动画 B页面很长时间才显示UI。 这和理想状态不符。
    • 我们先在viewDidAppear 进行带自动刷新的网络请求
    - (void)viewDidAppear:(BOOL)animated {
       [super viewDidAppear:animated];
       self.collectionView.scrollsToTop = YES;
       NSInteger pageIndex = [self vtm_pageIndex];
       SLog(@"当前页面索引: %ld", (long)pageIndex);
       [self requestListData];//加载数据
    }
    
    loading111GIF.gif

    健康栏目 滑动到 命理栏目 命理栏目已经全部出来 之后才去加载的。这是预加载吗? 你看到预加载了吗? 肯定不是。

    • 下面我来实现真正的预加载
      如果使用VTMagic的同学知道 每个子视图 都会引用 MenuInfo
    .h
    @class MenuInfo;
    @interface RACChannelViewController : BaseViewController
    /**
    *  菜单信息
    */
    @property (nonatomic, strong) MenuInfo *menuInfo;
    @end
    
    

    在.m 重写 大家在VTMagic Demo里看到 setMenuInfo 都是看里面写的 读缓存的逻辑
    其实 这个方法才是预加载的入口 当我们加载A界面 时,B界面的这个方法也会触发 依次触发 。就可以实现预加载了。我们可以先看下log

    .m
    #pragma mark - accessor methods
    - (void)setMenuInfo:(MenuInfo *)menuInfo {
       _menuInfo = menuInfo;
        NSLog(@"预加载: %@",_menuInfo.title);
       //VTMagic官方Dome 加载列表缓存的方法  [self loadLocalData];
    }
    
    loading222Gif.gif

    log 显示 当我们在移动到一个栏目时 会预加载当前栏目的下一个栏目

    下面我们就 - (void)setMenuInfo:(MenuInfo *)menuInfo 中加入网络请求并在请求成功后刷新列表

    #pragma mark - accessor methods
    - (void)setMenuInfo:(MenuInfo *)menuInfo {
       _menuInfo = menuInfo;
       _page=1;
       [self.viewModel requestListDataWithPage:_page menuInfo:_menuInfo requestType:ZBRequestTypeRefresh];
       SLog(@"预加载: %@",_menuInfo.title);
    
       //VTMagic官方Dome  加载列表缓存的方法  [self loadLocalData]; 
    }
    
    loading333GIF.gif

    可以看到在setMenuInfo 因为会预加载下一个栏目,我们在当前栏目时,下一个栏目进行了网络请求 并在请求成功后刷新列表 滑动可看到已经提前加载好下一个栏目的页面了

    1. 页面重用 缓存
      这个重用机制 作者已经说的很清楚了 看下图


      1F74CB23-2A2A-44CF-A515-28D55A34CC5C.png

    但是因为重用机制 使得页面在左右滑动俩个子视图页面后,会出现页面数据混乱的问题,这个作者也给出了解决方案

    - (void)viewDidDisappear:(BOOL)animated {
       [super viewDidDisappear:animated];
        [self savePageInfo];  // //保存页面数据 VTMagic官方Dome 的方法
    }
    
    #pragma mark - accessor methods
    - (void)setMenuInfo:(MenuInfo *)menuInfo {
       _menuInfo = menuInfo;
       _page=1;
       SLog(@"预加载: %@",_menuInfo.title);
       [self loadLocalData];// VTMagic官方Dome 加载列表缓存的方法
    }
    
    //VTMagic官方Dome 的方法
    - (void)savePageInfo {
        [[DataManager sharedInstance] savePageInfo:self.dataArray menuId:_menuInfo.menu_id];
    }
    - (void)loadLocalData {
        NSArray *cacheList = [[DataManager sharedInstance] pageInfoWithMenuId:_menuInfo.menu_id];
        [self.dataArray addObjectsFromArray:cacheList];
        [self.tableView reloadData];
    }
    

    就是在A页面 消失时 触发viewDidDisappear 保存当前页面的列表数组,在用户滑动回 A 页面时 提前触发setMenuInfo 取出列表数组 重新刷新列表
    虽然这个方法运行没有问题,但是并不是最优的解决方案

    下面放出我的解决方案,也许作者本人没有这么 做

    - (void)viewDidAppear:(BOOL)animated {
       [super viewDidAppear:animated];
       self.tableView.scrollsToTop = YES;
        _page=1;
       [self request:_page requestType:ZBRequestTypeRefresh];//重新数据
    }
    
    #pragma mark - accessor methods
    - (void)setMenuInfo:(MenuInfo *)menuInfo {
       _menuInfo = menuInfo;
    /*
       //伪代码
         if(有缓存){
         //读取缓存 刷新列表  
         }else{
          //重新请求 刷新列表 存储缓存
         }
    */
       SLog(@"预加载: %@",_menuInfo.title);
       _page=1;
        [self request:_page requestType:ZBRequestTypeCache];     //如有缓存使用缓存,无缓存重新加载
    }
    

    在viewDidAppear 重新请求或者调用下拉刷新
    在setMenuInfo 方法里 进行(如果有缓存使用缓存,无缓存重新加载)策略的请求 这样既满足的预加载, 有兼顾了缓存。

    进一步解释步骤:当栏目在A时 , 栏目B已经执行setMenuInfo ,假如B没有缓存 进行了重新请求并存储缓存,栏目A浏览一会 滑动到栏目B, 栏目B执行viewDidAppear 进行刷新请求 并更新了缓存, 同时栏目C执行setMenuInfo ,依次滑动并都请求成功A->B->C->D->E->F,都是同样的步骤.
    当用户网往回滑动A<-B<-C<-D<-E<-F 也是先执行 setMenuInfo 方法,因为都浏览过都请求成功了 都有了缓存,setMenuInfo 里面会执行读取缓存 的步骤。

    另外在设置 页面可以清除缓存 ,之后在回到主页进行反复调试

    request: requestType: 方法 最终调到是我封装的网络请求 ZBNetworking 里面就有这个策略 //如有缓存使用缓存,无缓存重新加载,简直是完美搭配 看最终效果

    loading444GIF.gif
    1. 对于现在比较流行嵌套滚动 VTMagic 框架不支持 它的headerView 和底部容器是加载在一个UIView上面没法进行嵌套滚动 只有一个隐藏headerView的方法
      在子视图 调用一下
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
       CGFloat yOffset = scrollView.contentOffset.y;
       if (yOffset>0) {
           self.magicController.magicView.againstStatusBar = YES;
           [self.magicController.magicView setHeaderHidden:YES duration:0.35];
       }else{
           self.magicController.magicView.againstStatusBar = NO;
           [self.magicController.magicView setHeaderHidden:NO duration:0.35];
       }
    }
    
    loading555GIF.gif

    先介绍这么多,以后再有领悟会补充。另外ZBNews中还实现了阅读日历 ,收藏,离线下载 功能 当然没有细致的制作UI 只是功能的展示 最后点击 ZBNews 下载

    一直在努力学习中,大家多多包涵。

    相关文章

      网友评论

      • cloud_sky_z:你好,你这里VTMagic如何才能调用scrollViewDidScroll获取偏移量
        Andi:@topStarSky 你可以给VTMagicView添加一个代理方法。在其.m文件里的scrollViewDidScroll获取偏移量 通过代理传出去。在外面调用。
        cloud_sky_z:@大苏Andi 我这边有个需求是当界面向右滑动到第一个时,再往右滑时,展示bounce效果,并且pop出界面。现在获取不到偏移量
        Andi:无法直接调用,VTMagicView.m 里的 VTContentView 是scrollView ,并没有暴露出来。逻辑判断在VTMagicViewDelegate 的里生命周期方法里基本都能实现

      本文标题:VTMagic 搭建一个iOS新闻类APP及使用心得

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