This component support the UITableview scroll up and down, and both support switch between two list horizontally.At the same time it set a header and a segment bar at the top of list view, the usage is similar to the native UITableview tableHeaderView way.
SwipeTableView is available on CocoaPods. Add the following to your Podfile:
pod 'SwipeTableView'In order to be compatible with the pull to refresh, adopted two kinds of ways, but the basic structure is the same.
-
Use
UICollectionViewas a contentview for items,to enable scroll horizontally. -
After supporting the horizontal scrolling, the most pivotal issue is the alignment of neighboring items after scroll horizontally.
For the alignment of two itemViews after scroll, should compared to the contentOffsets of the two itemViews, and then set the contentOffset of the nest itemView same as the previous one. By this way, the offset of the itemView is aligned after scroll horizontally.
- Since the itemViews share a header and bar, so, the header and bar must be the subview of the root view, that is, the same as the CollectionView which is subview of
SwipeTableView, and above the CollectionView.
For support sticky of headr & bar, KVO observe the contentOffset of current itemView. And then change the Y frame of header and bar when the contentOffset of current itemView changed.
-
Because he top header & bar are at the top, so the head of each itemView need make a blank space for header & bar display. In
Model 1, the way is modify the top contentInsets ofUIScrollViewto set top blank space. -
Due to the header is at the top of the layer, so if the current itemView should scroll follow with header when pan scroll the header, need to reset the contentOffset of current ItemView when header's frame changed. And the header must have a elasticity effect same as UIScrollview.
Here, use the
UIKit Dynamicphysical animation engine to customize theSTHeaderView, achieve the customUIScrollVieweffect to solve the problems aboveReference.
- In
Mode 2, the basic structure sames asMode 1, the only difference is that the top balnk of each itemView.
By setting
tableHeaderViewofUITabelViewto provide top space blank, CollectionView item should use customcollectionHeaderViewofSTCollectionViewfor set the blank. (Current mode does not supportUIScrollView)
- How to distinguish
Mode 1fromMode 2?
Under normal conditions, it is
Mode 1; ForMode 2, set the macro#define ST_PULLTOREFRESH_HEADER_HEIGHT xxin theSwipeTableView.hor the PCH file.
Conform protocol SwipeTableViewDataSource and implement the two methods below:
- (NSInteger)numberOfItemsInSwipeTableView:(SwipeTableView *)swipeView Return a count of the itemViews.
- (UIScrollView *)swipeTableView:(SwipeTableView *)swipeView viewForItemAtIndex:(NSInteger)index reusingView:(UIScrollView *)viewReturn a itemView at the index,the itemView must be kind of
UIScrollView、UITableVieworUICollectionView. It completed by reuse mechanism, so it is based the reusingView when create a itemView.
The swipeHeaderView must be STHeaderView or subclass of STHeaderView
There is two ways to support pull to refresh, one is custom pull to refresh by yourself(just custom part), another is set a macro simply and crudely
1. Support pull to refersh by one line code, juset set the macro below in SwipeTableView.h or the PCH file:
#define ST_PULLTOREFRESH_HEADER_HEIGHT xx The
xxof macro above should be same as the height of your third pull to refresh component:
MJRefreshisMJRefreshHeaderHeight,SVPullToRefreshisSVPullToRefreshViewHeight(Note: current mode isMode 2)
Add a protocol of refresh, now you can set the top height of each itemView when began refresh, and set should supports pull to refresh for each item freely.
- (BOOL)swipeTableView:(SwipeTableView *)swipeTableView shouldPullToRefreshAtIndex:(NSInteger)indexSet the item should supports pull to refresh at the index. Default is Yes When set macro
#define ST_PULLTOREFRESH_HEADER_HEIGHT xx, otherwise is NO.
- (CGFloat)swipeTableView:(SwipeTableView *)swipeTableView heightForRefreshHeaderAtIndex:(NSInteger)indexReturn a height of the itemView when began refresh, if not implement this method, the default height is
ST_PULLTOREFRESH_HEADER_HEIGHTwhen you set the macro#define ST_PULLTOREFRESH_HEADER_HEIGHT xx.If you didn't set the refresh macro, and want to support pull to refresh by custom the refresh, you must implement this method, and provide a height of the RefreshHeader(A height when the RefreshHeader show wholly), to notifySwipeTableViewcall the pull to refresh
2. If you want a better extension, as well as students who likes research, you can try to modify or custom a refresh control to solve the problem of pull to refresh, while here provide some ideas:
If the frame of refresh control is fixed (such as frame of refresh header), you can reset the frame of refresh header when init it, or reset the frame in the datasource of SwipeTableViewDataSource.
Get the refresh header, and change Y of the frame, subtract both heights of
swipeHeaderViewandswipeHeaderBar(or override methodsetFrame:of RefreshHeader). So this way can solve the effect of top contentInsets of the itemVewi(othewise, the frefresh header will below theswipeHeaderView).
- (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;
...
}For some refreh control component, the frame of RefreshHeader will be set in mehtod layoutSubviews, so we should change the frame of RefreshHeader after execute layouSubviews of RefreshHeader, such as:
- (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;
}
}How to judge the frame of the RefreshHeader of refresh control is constant?
One way is check out the source code of the refresh control; The other way is simple, just log the frame of RefreshHeader when scroll the itemView(most RefreshHeader height of third refresh control is constant).
- In basic mode
Model 1, has the best extensibility, it supportsUITableView、UICollectionView、UIScrollView.If you set the propertyshouldAdjustContentSizeYES to adjust the contentSize of itemView, you shuld only useSTCollectionViewits subcalss when your itemView isUICollectionViewand its contentinfo is less
UICollectionViewcan not set contentSize by property contentSize.
- In
Model 2, collectionView you used must be kind ofSwipeTableView, now, not supportUIScrollView.
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;
}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;
}
If STCollectionViewFlowLayout can not satisfy the layout of UICollectionView, you can custom a flowlayout subcalss of STCollectionViewFlowLayout, And need to call call the parent class method and follow some rules when override methods, such as:
- (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;
}
-
SingleOneKindView
The itemView is just one kind, it isCustomTableView(subclass ofUITableView) in the demo -
HybridItemViews
The itemViews which dataSource provided are hybird, they areCustomTableViewCustomCollectionView(subclass ofUICollectionView) in the demo -
In release 0.2.3, delete this module in the demo, in module `SingleOneKindView` default is adjsut contentSize.`AdjustContentSize` Adjust the cotentOffszie to fit screen when the data is less than screen. -
DisabledBarScroll
Disabel the segment scrolling when scroll the itemView, it is available when swipeHeaderView is nill. -
HiddenNavigationBarHidden the nabigationbar. Have a back button, and support slide back. -
In the Demo, you can add or delete the header or bar.
-
Tap the header can view the image in screen.
-
Custom pull to refresh component
STRefreshHeader, for reference only
SwipeTableView is available under the MIT license. See the LICENSE file for more info.





