瀑布流

瀑布流

瀑布流

几条线下来, 没列中类似每行的尺寸都不一样, 应该自己些一个继承自scrollView的瀑布流控件
设置框架, 麻烦点不要仅, 要紧的是健壮.
模仿tableView, 做一个瀑布流控件:
    * 数据源和代理的协议要有, 和tableView差不多, 就是只用看说哪个就够了,
    * 自己实现的重要的说reload方法, 调用这个方法时, 会重新调用所有必须的数据源和代理方法, 根据返回的数据计算出每个cell的位置展示出来, 一次性计算好所有cell的frame, 返回多少个和多少列后, 就计算好他们的frame保存起来
    * 设置私有方法, 判断数据源和代理的方法是否有返回值, 没有的话就返回一个默认值
    * 类似算九宫格一样, 先算好几个相对固定的值,
    * 注意: 和九宫格不一样的是, 并非说从左到右排列cell, 而是看哪个的高度比较小就往那里排列, 不然如果高度大的都排到了一起, 就很不协调了
    * 用一个数组来保存每列的最大y值, 在计算应该在哪列放置cell的时候, 选取y值最小的那列: 用一个C数组来保存每列中的最大y值, 在选列的时候, 拿出数组的y值比较, 选出最小的列, 用最小的列和其最大y值来计算cell的x,y. 在放入cell后, 更新当组的最大y值, 并将算好的frame保存起来
    * 最后拿到这一个位置对应的cell即遍历计算到的i, 将frame赋值过去, 并将cell添加到self(瀑布流控件)

需要用时在创建cell

之前创建出来的说一口气就创建列所有的cell, 这样性能很不好, 应该想tableView一样要用到了才去创建:
    * 不要将自己的代理设置为自己, 这样占据列代理后别人就不能设置代理或者会覆盖掉自己的代理
    * 需要看当前显示的屏幕区域中有没有和算出来的frames有交叠的, 交叠的frame就说要显示出来的.
    * scrollView和别的控件有点不同就是在滚动的时候也会调用layoutSubviews方法, 我们可以用这个方法监听滚动
    * 在滚动的时候遍历所有的frame.maxY > offset.y && frame.y < offset.y + screenH, 就说要显示的frame,拿到这些位置的, 向数据源拿到对应位置的cell, 将frame赋值给cell
    * 这步完成列会有个问题, 在微滚动的时候, 会继续遍历, 继续判断到已经显示的frame符合条件, 拿cell并赋值, 数据源方法会一直调用即使是已经显示的cell, 应该判断是否已经显示出来, 显示了的就不要在向数据源拿了.
    * 创建一个字典来保存已经显示的cell : i(位置) : cell. 一个位置i对应一个cell
    * 拿出i对应的显示的cell, 遍历frames, 发现交叠显示的时候判断cell=nil?, 没有的正在显示的话向数据源拿, 即为新进入视野的cell, cell=!nil的话就说已经在显示的了, 不用管.也不去数据源拿.

循环利用

以上完成后, 微调不会重复创建显示在屏幕上的cell, 但是显示后就不做处理了, 导致滚完后, 有多少个cell都留在内存里, 应该把不显示的cell扔到缓存池中, 拿来循环利用.
* 创建一个NSMutableSet作为缓存池存放移出屏幕的cell,
* 遍历的时候判断出不再屏幕的时候在判断是否在显示中的cell, 不在屏幕+显示中=在上面那些添加到veiw后又不在屏幕显示中的cell, 说这种移出屏幕的cell的话, 就把它加到缓存池中, 并从view中移除, 也从显示cell字典中移除
* 提供方法给外界, 数据源决定从哪里加载cell(创建OR缓存池), 方法返回根据重用标识的cell(遍历缓存池, 找到这个标识的cell), 在遍历判断显示的时候, 要显示的依然是从数据源中拿, (数据源有可能返回的是缓存池或者新创建的)
* 至此可以在数据源返回cell的时候打印地址, 就能知道是不是循环利用

事件处理, 处理cell的点击

当点击时, 如果点到cell, 应该通知代理
    * 在touched方法里监听屏幕的点击,
    * 点击中, 拿到当前的点击点, 遍历当前屏幕上存在的cell(即保存显示cell的字典), 当cell.frame contain这个点时, 就返回这个cell的key即索引, 并*stop=yes;
    * 在拿到触摸点前判断有没有实现这个代理方法, 没有就不用做这么多事
    * 拿到索引后, 看有没有值(点中空白说没有值的), 有值才通知代理

自定义瀑布流后之简单应用

* 应该将自己写好的当作一个框架, 不满意的话就自定义一个, 不要改原来的结构, 增加自己想要的东西
* 向之前使用tableView一样使用这个框架
* 注意瀑布流需要服务器返回图片的宽高, veiw里宽说固定的, 为了比例一样, 需要算出显示出来的高度, 按照原来的宽高比
* 上蜡刷新加载更多---可以用框架, 或者把以前自己做的简单封装起来
*  if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation))判断当前控制器的方向

发现: 当操作到子控件时, 会调用到自身的laypoutSubViews方法, 如果在reloadData里没有操作子控件, 要手动调用, 不然reloaddata算出各frame后, 没有判断子控件的 位置来加载, 也是没有反应的.

bug: -[__NSArrayI removeFromSuperview]: unrecognized selector sent to instance 0x10a50f150’

    //  makeObjectsPerformSelector:@selector(removeFromSuperview) 让数组里的对象执行方法
//  performSelector: 自己执行方法