Giter Site home page Giter Site logo

liangdrime / swipetableview Goto Github PK

View Code? Open in Web Editor NEW
2.3K 2.3K 442.0 28.08 MB

Both scroll horizontal and vertical for segment scrollview which have a same header. — 类似半糖、美丽说主页与QQ音乐歌曲列表布局效果,实现不同菜单的左右滑动切换,同时支持类似tableview的顶部工具栏悬停(既可以左右滑动,又可以上下滑动)。兼容下拉刷新,自定义 collectionview实现自适应 contentSize 还可实现瀑布流功能

License: MIT License

Objective-C 98.94% Ruby 1.06%
bantang collectionview commonheader swipeview tableview

swipetableview's Introduction

LOGO

CocoaPods Platforms License

功能类似半糖首页菜单与QQ音乐歌曲列表页面。即支持UITableview的上下滚动,同时也支持不同列表之间的滑动切换。同时可以设置顶部header view与列表切换功能bar,使用方式类似于原生UITableview的tableHeaderView的方式。 Engilish→


预览

OverView1

OverView1

OverView1

使用 Cocoapods 导入

SwipeTableView is available on CocoaPods. Add the following to your Podfile:

pod 'SwipeTableView'

目录

  1. 实现原理
  2. 基本用法
  3. 下拉刷新
  4. 混合模式
  5. 示例代码
  6. Demo介绍

实现的原理

为了兼容下拉刷新,采用了两种实现方式,但基本构造都是一样的

Mode 1

Mode 1

  1. 使用UICollectionView作为item的载体,实现左右滑动的功能。

  2. 在支持左右滑动之后,最关键的问题就是是滑动后相邻item的对齐问题。

为实现前后item对齐,需要在itemView重用的时候,比较前后两个itemView的contentOffset,然后设置后一个itemView的contentOffset与前一个相同。这样就实现了左右滑动后前后itemView的offset是对齐的。

  1. 由于多个item共用一个header与bar,所以,header与bar必须是根视图的子视图,即与CollectionView一样是SwipeTableView的子视图,并且在CollectionView的图层之上。

headr & bar的滚动与悬停实现是,对当前的itemView的contentOffset进行KVO。然后在当前itemView的contentOffset发生变化时,去改变header与bar的Y坐标值。

  1. 顶部header & bar在图层的最顶部,所以每个itemView的顶部需要做出一个留白来作为header & bar的显示空间。在Mode 1中,采用修改UIScrollView的contentInsets的top值来留出顶部留白。

  2. 由于header在图层的最顶部,所以要实现滑动header的同时使当前itemView跟随滚动,需要根据header的frame的变化回调给当前的itemView来改变contentOffset,同时也要具有ScrollView的弹性等效果。

这里采用UIKit Dynamic物理动画引擎自定义STHeaderView实现自定义UIScrollView效果解决上述问题 参考文章 英文博客

Mode 2

Mode 2

  1. Mode 2中,基本结构与Mode 1一样,唯一的不同在于每个itemView顶部留白的的方式。

通过设置UITabelViewtableHeaderView,来提供顶部的占位留白,CollectionView采用自定义STCollectionViewcollectionHeaderView来实现占位留白。(目前不支持UIScrollView

  1. 如何设置区分Mode 1Mode 2模式?

正常条件下即为Mode 1模式;在SwipeTableView.h中或者在工程PCH文件中设置宏#define ST_PULLTOREFRESH_HEADER_HEIGHT xx设置为Mode 2模式。

使用用法

怎样使用?使用方式类似UITableView

实现 SwipeTableViewDataSource 代理的两个方法:

- (NSInteger)numberOfItemsInSwipeTableView:(SwipeTableView *)swipeView     

返回列表item的个数

- (UIScrollView *)swipeTableView:(SwipeTableView *)swipeView viewForItemAtIndex:(NSInteger)index reusingView:(UIScrollView *)view

返回对应index下的itemView,返回的视图类型需要是UIScrollView及其子类:UITableView或者UICollectionView。这里采用重用机制,需要根据reusingView来创建单一的itemView。

使用的swipeHeaderView必须是STHeaderView及其子类的实例。

如何支持下拉刷新?

下拉刷新有两种实现方式,一种用户自定义下拉刷新组件(局部修改自定义),一种是简单粗暴设置宏:

1. 一行代码支持常用的下拉刷新控件,只需要在项目的PCH文件中或者在SwipeTableView.h文件中设置如下的宏:

#define ST_PULLTOREFRESH_HEADER_HEIGHT xx   

上述宏中的xx要与您使用的第三方下拉刷新控件的refreshHeader高度相同:
MJRefreshMJRefreshHeaderHeightSVPullToRefreshSVPullToRefreshViewHeight(注:此时视图结构为Model 2

新增下拉刷新代理,可以控制每个item下拉临界高度,并自由控制每个item是否支持下拉刷新

- (BOOL)swipeTableView:(SwipeTableView *)swipeTableView shouldPullToRefreshAtIndex:(NSInteger)index

根据item所在index,设置item是否支持下拉刷新。在设置#define ST_PULLTOREFRESH_HEADER_HEIGHT xx的时候默认是YES(全部支持),否则默认为NO。

- (CGFloat)swipeTableView:(SwipeTableView *)swipeTableView heightForRefreshHeaderAtIndex:(NSInteger)index

返回对应item下拉刷新的临界高度,如果没有实现此代理,在设置#define ST_PULLTOREFRESH_HEADER_HEIGHT xx的时候默认是ST_PULLTOREFRESH_HEADER_HEIGHT的高度。如果没有设置宏,并且想要自定义修改下拉刷新,必须实现此代理,提供下拉刷新控件RefreshHeader的高度(RefreshHeader全部露出的高度),来通知SwipeTableView触发下拉刷新。

2. 如果想要更好的扩展性,以及喜欢自己研究的同学,可以尝试修改或者自定义下拉控件来解决下拉刷新的兼容问题,同时这里提供一些思路:

如果下拉刷新控件的frame是固定的(比如header的frame),这样可以在初始化下拉刷新的header或者在数据源的代理中重设下拉header的frame。

获取下拉刷新的header,将header的frame的y值减去swipeHeaderViewswipeHeaderBar的高度和(或者重写RefreshHeader的setFrame方法),就可以消除itemView contentInsets顶部留白top值的影响(否则添加的下拉header是隐藏在底部的)。

- (UIScrollView *)swipeTableView:(SwipeTableView *)swipeView viewForItemAtIndex:(NSInteger)index reusingView:(UIScrollView *)view {
   ...
   STRefreshHeader * header = scrollView.header;
   header.y = - (header.height + (swipeHeaderView.height + swipeHeaderBar.height));
   ...
}
 

or


- (instancetype)initWithFrame:(CGRect)frame {
   ...
   STRefreshHeader * header = [STRefreshHeader headerWithRefreshingBlock:^(STRefreshHeader *header) {

}];
   header.y = - (header.height + (swipeHeaderView.height + swipeHeaderBar.height)); 
   scrollView.header = header;
   ...
}

对于一些下拉刷新控件,RefreshHeader的frame设置可能会在layoutSubviews中,所以,对RefreshHeader frame的修改,需要等执行完layouSubviews之后,在 有效的方法 中操作,比如:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    STRefreshHeader * header = self.header;
    CGFloat orginY = - (header.height + self.swipeTableView.swipeHeaderView.height + self.swipeTableView.swipeHeaderBar.height);
    if (header.y != orginY) {
        header.y = orginY;
    }
}

如何判断下拉刷新的控件的frame是不是固定不变的呢?

一是可以研究源码查看RefreshHeader的frame是否固定不变;另一个简单的方式是,在ScrollView的滚动代理中log RefreshHeader的frame(大部分的下拉控件的frame都是固定的)。

如果使用的下拉刷新控件的frame是变化的(个人感觉极少数),那么只能更深层的修改下拉刷新控件或者自定义下拉刷新。也可以更直接的采用第一种设置宏的方式支持下拉刷新。

混合模式(UItableView & UICollectionView & UIScrollView)

  1. Mode 1模式下,属于最基本的模式,可扩展性也是最强的,此时,支持UITableViewUICollectionViewUIScrollView如果,同时设置shouldAdjustContentSize为YES,实现自适应contentSize,在UICollectionView内容不足的添加下,只能使用STCollectionView及其子类

    UICollectionView不支持通过contentSize属性设置contentSize。

  2. Mode 2模式下,SwipeTableView支持的collectionView必须是STCollectionView及其子类的实例,目前,不支持UIScrollView

示例代码

初始化并设置header与bar

self.swipeTableView = [[SwipeTableView alloc]initWithFrame:[UIScreen mainScreen].bounds];
_swipeTableView.delegate = self;
_swipeTableView.dataSource = self;
_swipeTableView.shouldAdjustContentSize = YES;
_swipeTableView.swipeHeaderView = self.tableViewHeader;
_swipeTableView.swipeHeaderBar = self.segmentBar;

实现数据源代理:

- (NSInteger)numberOfItemsInSwipeTableView:(SwipeTableView *)swipeView {
    return 4;
}

- (UIScrollView *)swipeTableView:(SwipeTableView *)swipeView viewForItemAtIndex:(NSInteger)index reusingView:(UIScrollView *)view {
    UITableView * tableView = view;
    if (nil == tableView) {
        UITableView * tableView = [[UITableView alloc]initWithFrame:swipeView.bounds style:UITableViewStylePlain];
        tableView.backgroundColor = [UIColor whiteColor];
        ...
    }
    // 这里刷新每个item的数据
    [tableVeiw refreshWithData:dataArray];
    ...
    return tableView;
}

STCollectionView使用方法:

MyCollectionView.h

@interface MyCollectionView : STCollectionView

@property (nonatomic, assign) NSInteger numberOfItems;
@property (nonatomic, assign) BOOL isWaterFlow;

@end



MyCollectionView.m

- (instancetype)initWithFrame:(CGRect)frame {

    self = [super initWithFrame:frame];
    if (self) {
        STCollectionViewFlowLayout * layout = self.st_collectionViewLayout;
        layout.minimumInteritemSpacing = 5;
        layout.minimumLineSpacing = 5;
        layout.sectionInset = UIEdgeInsetsMake(5, 5, 5, 5);
        self.stDelegate = self;
        self.stDataSource = self;
        [self registerClass:UICollectionViewCell.class forCellWithReuseIdentifier:@"item"];
        [self registerClass:UICollectionReusableView.class forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"header"];
        [self registerClass:UICollectionReusableView.class forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:@"footer"];
    }
    return self;
}


- (NSInteger)collectionView:(UICollectionView *)collectionView layout:(STCollectionViewFlowLayout *)layout numberOfColumnsInSection:(NSInteger)section {
    return _numberOfColumns;
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    return CGSizeMake(0, 100);
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
    return CGSizeMake(kScreenWidth, 35);
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section {
    return CGSizeMake(kScreenWidth, 35);
}

- (UICollectionReusableView *)stCollectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
    UICollectionReusableView * reusableView = nil;
    if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
        reusableView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"header" forIndexPath:indexPath];
        // custom UI......
    }else if ([kind isEqualToString:UICollectionElementKindSectionFooter]) {
        reusableView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:@"footer" forIndexPath:indexPath];
        // custom UI......
    }
    return reusableView;
}

- (NSInteger)numberOfSectionsInStCollectionView:(UICollectionView *)collectionView {
    return _numberOfSections;
} 

- (NSInteger)stCollectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return _numberOfItems;
}

- (UICollectionViewCell *)stCollectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"item" forIndexPath:indexPath];
    // do something .......
    return cell;
}

如果STCollectionViewFlowLayout已经不能满足UICollectionView的布局的话,用户自定义的flowlayout需要继承自STCollectionViewFlowLayout,并在重写相应方法的时候需要调用父类方法,并需要遵循一定规则,如下:

- (void)prepareLayout {
    [super prepareLayout];
    // do something in sub class......
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSArray * superAttrs = [super layoutAttributesForElementsInRect:rect];
    NSMutableArray * itemAttrs = [superAttrs mutableCopy];

    // filter subClassAttrs to rect
    NSArray * filteredSubClassAttrs = ........;

    [itemAttrs addObjectsFromArray:fittesSubClassAttrs];

    return itemAttrs;
}

- (CGSize)collectionViewContentSize {
    CGSize superSize = [super collectionViewContentSize];

    CGSize subClassSize = .......;
    subClassSize.height += superSize.height;

    // fit mincontentSize
    STCollectionView * collectionView = (STCollectionView *)self.collectionView;
    subClassSize.height = fmax(subClassSize.height, collectionView.minRequireContentSize.height);

    return subClassSize;
}

Demo 介绍

使用的详细用法在SwipeTableViewDemo文件夹中,提供了五种示例:

  • SingleOneKindView
    数据源提供的是单一类型的itemView,这里默认提供的是 CustomTableViewUITableView的子类),并且每一个itemView的数据行数有多有少,因此在滑动到数据少的itemView时,再次触碰界面,当前的itemView会有回弹的动作(由于contentSize小的缘故)。

  • HybridItemViews
    数据源提供的itemView类型是混合的,即 CustomTableViewCustomCollectionViewUICollectionView的子类)。

  • `AdjustContentSize` 自适应调整cotentOffszie属性,这里不同的itemView的数据行数有多有少,当滑动到数据较少的itemView时,再次触碰界面并不会导致当前itemView的回弹,这里当前数据少的itemView已经做了最小contentSize的设置。

    在0.2.3版本中去除了 demo 中的这一模块,默认除了`SingleOneKindView`模式下全部是自适应 contentSize。
  • DisabledBarScroll
    取消顶部控制条的跟随滚动,只有在swipeHeaderView是nil的条件下才能生效。这样可以实现一个类似网易新闻首页的滚动菜单列表的布局。

  • HiddenNavigationBar 隐藏导航。自定义了一个返回按钮(支持手势滑动返回)。

  • Demo支持添加移除header(定义的UIImageView)与bar(自定义的 CutomSegmentControl )的功能。

  • 示例代码新增点击图片全屏查看。

  • Demo中提供简单的自定义下拉刷新控件STRefreshHeader,供参考

License

SwipeTableView is available under the MIT license. See the LICENSE file for more info.

swipetableview's People

Contributors

bryant1410 avatar royleestillway avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swipetableview's Issues

在使用过程中, 下拉刷新有问题, 请指教

没有定义宏ST_PULLTOREFRESH_HEADER_HEIGHT,采用demo中的默认方法.
设置了headerView和headerBar. 同时headerTopInset=0. 无论是使用demo中的下拉刷新,还是MJRefresh. 下拉时, 整个页面都下移且不回弹. 直到拉到屏幕底部, 下拉刷新才触发. 求问一下这是什么原因

請問

請問segment style可以設定為底線的形式嗎?
請問可否設定底線的顏色?
2016-07-18 12 47 21

reusingView为nil

加载过的collectionView再次返回reusingView为nil的可能原因是什么呢

Header悬浮和contentSize不同item滑动的问题。

作者,你好。我的页面导航栏是根据滑动item滑动到一定距离来隐藏的。我把你Demo里的导航栏设置了透明度,发现你是把bar悬浮了,然后把你的segmentBar加到了header里,防止点在bar上面的时候,无法上下滑动。结果还是发现你悬浮了他。
那么,在我的页面里,当向上滑动,达到item的上边缘隐藏导航栏的时候,不想悬浮这个bar怎么办?
untitled
SwipeTableView.zip

tableview被tabbar遮挡的问题

tableview最下面会被tabbar遮挡
尝试设置automaticallyAdjustsScrollViewInsets但没有效果,可能需要监控tableview的contentOffset来整体上移tableview

手势冲突问题

加上过滤代码 还是需要点击屏幕一下 scrollView 的滑动手势 跟headerView上控件的点击事件才能响应

是否够能支持ScrollView呢?

要显示的内容部分是否应该加上能支持ScrollView界面呢? 因为有一些界面只能用scrollView来做,如果能兼容就更好了

iOS10 crash

反复滑来滑去有纪律触发crash,问题出在UIView+STFrame的

  • (void)setSt_bottom:(CGFloat)b {
    self.frame = CGRectMake(self.st_left, b - self.st_height, self.st_width, self.st_height);
    }
    warning: could not load any Objective-C class information. This will significantly reduce the quality of type information available.
    貌似是无限递归

添加上拉加载

一共4个item,要给第三个和第四个添加上拉加载,要怎么添加啊

Swift3.0开发问题

当我实现swipeTableView(_ swipeView: SwipeTableView!, viewForItemAt index: Int, reusing view: UIScrollView!) -> UIScrollView!{}这个协议方法的时候,我创建了一个自己的tableView,然后用将他赋值给重用的view,报错:Cannot assign to value: 'view' is a 'let' constant!谢谢回答!!!

添加MJRefreshFooter

加了两个Tab 每个tab是1个tableviewcontroller 第一次滑到第2个 tableviewcontroller的时候,第2个会自动走 self.mj_footer = [GJRefreshFooterWithTabBar footerWithRefreshingBlock:^{
[selfWeak loadMoreBrandData];
}];
上拉加载更多。什么原因?

如果设置垂直方向bounce。

看了源码,貌似可以在STHeader的
- (void)handlePanGesture:(UIPanGestureRecognizer *)panGestureRecognizer方法里设置,但是不知道UIGestureRecognizerStateChanged中各种Y值是干什么的,能否给个解决方案呢?

关于在iOS8 和iOS7上运行的问题

在iOS8 和iOS7 上,如果我定义的swipeHeaderView 是xib 并且xib中使用了atuoLayout 添加了约束,那么swipeHeaderBar就无法悬浮,并且会一闪一闪的。。作者,这个问题帮忙解决下吧,我的项目中用到了你的SwipeTableView,最近马上要上线了,测试iOS7 和8 的时候发现了这个问题==。。。

下拉刷新带来的问题

下拉刷新等待时滑动了item,headerView的frame发生变化,我把demo发给你,有空帮我看下,谢谢啦!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.