tableView上拉刷新,下拉加载
1:UITableView说明:
1)UItableView继承自UIScrollView,有Plain和Grouped两种风格
2) UItableView有两个协议(delegate)方法:dataSource与delegate
dataSource是UITableViewDataSource类型,主要为UITableView提供显示用的数据(UITableViewCell),指定UITableViewCell,并根据用户的操作进行相应的数据更新操作,如果数据没有更具操作进行正确的更新,可能会导致显示异常
delegate是UITableViewDelegate类型,主要提供一些可选的方法,用来控制tableView的选择、指定section的头和尾的显示,协助完成cell的删除和排序等功能。
UITableView声明了一个NSIndexPath的类别,主要用来标识当前cell的在tableView中的位置,该类别有section和row两个属性,前者标识当前cell处于第几个section中,后者代表在该section中的第几行。
UITableView只能有一列数据(cell),且只支持纵向滑动,当创建好的tablView第一次显示的时候,我们需要调用其reloadData方法,强制刷新一次,从而使tableView的数据更新到最新状态。
3)如何提高tableView的性能
a、重用cell
申请内存是需要时间的,在一段时间内频繁的申请内存将会造成很大的开销,而且在tebleView中cell大部分情况下布局都是一样的,这个时候我们可以通过回收重用机制来提高性能。
b、避免content的重新布局
尽量避免在重用cell时候,对cell的重新布局,一般情况在在创建cell的时候就将cell布局好。
c、使用不透明的subView
在定制cell的时候,将要添加的subView设置成不透明的会大大减少多个view层叠加时渲染所需要的时间。
d、如果方便,直接重载subView的drawRect方法
如果定制cell的过程中需要多个小的元素的话,最好直接对要显示的多个项目进行绘制,而不是采用添加多个subView。
e、tableView的delegate的方法除非必要,否则我们尽量不实现
tableView的delegate中的很多函数提供了对cell属性的进一步控制,比如每个cell的高度,cell是否可以编辑,支持的edit风格等,如非必要最好不要实现这些方法因为快速的调用这些方法也会影响性能。
2:tableView上拉刷新,下拉加载
1)功能说明:下拉时实现查找更新的内容,上拉时进行图片或数据的加载功能。
Header:有向上的箭头,文字,进度条等元素,根据下拉的距离来改变其状态,从而现实不同的样式
Content:这是内容区域,当我们下拉时,将内容视图区域向下滑动,使得Header显示,上拉加载时,使得Footer显示
Footer:有显示向下的箭头,自动加载更多的进度条等
2)框架需求:
下载EGORefreshTableHeaderView,EGORefreshTableFooterView开源框架。
3)
A:将EGORefreshTableHeaderView.h文件、 EGORefreshTableHeaderView.m文件、
EGORefreshTableFooterView.h文件
EGORefreshTableFooterView.m文件
EGOViewCommon.h文件添加到你的工程目录下
B:Resource目录,将TMQuiltView文件添加到工程目录下
a:瀑布流视图控件"quilt":瀑布流(quilt)-以不同的纵横比在多个列中显示图片和媒体
b:TMQuiltView的实现:
TMQuiltView的原型由UITableView而来,它主要实现两个简单的工程目标:为开发者提供简单友好的接口;平滑的滚动性能,即使有成千上万个单元
实现TMQuiltView时考虑的几个细节:
数据源(Datasource)和委托(delegate)-我们使用和UITableView类似的委托和数据源(如cellAtIndexPath)。请注意,quilt视图本身并没有“行”因为不同列中的单元不是水平对齐的。不过我们使用NSIndexPath来保持和NSFetchedResultsController的兼容性。虽然数据源定义了单元格的顺序,但没有指定列(见“列”)。
单元格循环-像UITableView那样,quilt只维护可见的单元格对象。这使得即使有成千上万个单元quilt也只需创建很少的单元格对象,保证快速加载和平滑滚动。
数据源变化-quilt视图需要反映出数据源的任何变化,包括添加、删除、改变顺序。由于经常会有多个变化同时发生,我们采用批量的方式处理它们。quilt也会处理个别的变化,在视图控制器中调用endUpdates方法来提交所做的改变,重新计算一次布局。
列-和UITableView的主要区别之一是使用多个列。quilt通过给每个单元格分配它们最小的高度来保持列的平衡。
屏幕方向变化-quilt支持的列数量和屏幕方向有关。TMQuiltViewController会根据屏幕方向自动重载数据。
定制-大多数定制的地方应该都在单元格上。但是,quilt委托提供了定义外边距、单元格高度和列数目的方法。
C:导入QuartzCore.framework到你的工程
项目目录
3:添加代码
1)在对应的类的头文件中添加以下代码
<span style="font-size:18px;">#import <UIKit/UIKit.h>
#import "EGORefreshTableFooterView.h"
#import "EGORefreshTableHeaderView.h"
#import "TMQuiltView.h"
@interface RootViewController : UIViewController<TMQuiltViewDataSource,TMQuiltViewDelegate,EGORefreshTableDelegate>
{
EGORefreshTableFooterView *_refreshTableFooterView ;
EGORefreshTableHeaderView *_refreshTableHeaderView ;
BOOL _reloading;
}
@property(nonatomic ,retain)NSMutableArray *images ;
@end
</span>
2)往viewDidLoad函数里添加如下代码
<span style="font-size:18px;">- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
tmQuiltView = [[TMQuiltView alloc] initWithFrame:self.view.bounds];
tmQuiltView.delegate = self ;
tmQuiltView.dataSource = self ;
[self.view addSubview:tmQuiltView];
//刷新tmQuiltView
[tmQuiltView reloadData];
//创建刷新视图
[self creatHeaderView];
[self performSelector:@selector(testFinishedLoadData) withObject:nil afterDelay:0.0f];
}</span>
3)添加事件函数事件
<span style="font-size:18px;">//创建刷新视图
- (void)creatHeaderView
{
//移除刷新视图
if (_refreshTableHeaderView && [_refreshTableHeaderView superview]) {
[_refreshTableHeaderView removeFromSuperview];
}
//初始化下拉刷新控件
_refreshTableHeaderView = [[EGORefreshTableHeaderView alloc] initWithFrame:CGRectMake(0.0f, 0.0f- self.view.bounds.size.height, self.view.bounds.size.width, self.view.bounds.size.height)] ;
_refreshTableHeaderView.delegate = self ;
//将下拉刷新控件作为子控件添加到tmQuiltView中
[tmQuiltView addSubview:_refreshTableHeaderView] ;
[_refreshTableHeaderView refreshLastUpdatedDate] ;
}
- (void)testFinishedLoadData
{
[self finishReloadingData] ;
[self setFootView] ;
}
//结束读取数据,即完成加载时调用
- (void)finishReloadingData
{
//结束读取数据
_reloading = NO;
//刷新视图
if (_refreshTableHeaderView) {
[_refreshTableHeaderView egoRefreshScrollViewDataSourceDidFinishedLoading:tmQuiltView];
}
//加载视图
if (_refreshTableFooterView) {
[_refreshTableFooterView egoRefreshScrollViewDataSourceDidFinishedLoading:tmQuiltView];
[self setFootView] ;
}
}
//创建加载视图
- (void)setFootView
{
CGFloat height = MAX(tmQuiltView.contentSize.height, tmQuiltView.frame.size.height);
if (_refreshTableFooterView && [_refreshTableFooterView superview]) {
//重置位置
_refreshTableFooterView.frame = CGRectMake(0.0f, height, tmQuiltView.frame.size.width, self.view.frame.size.height);
}else{
//创建_refreshTableFooterView
_refreshTableFooterView = [[EGORefreshTableFooterView alloc] initWithFrame:CGRectMake(0.0f, height, tmQuiltView.frame.size.width, self.view.frame.size.height)];
_refreshTableFooterView.delegate = self ;
[tmQuiltView addSubview:_refreshTableFooterView] ;
}
if (_refreshTableFooterView) {
[_refreshTableFooterView refreshLastUpdatedDate];
}
}
-(void)removeFooterView
{
if (_refreshTableFooterView && [_refreshTableFooterView superview])
{
[_refreshTableFooterView removeFromSuperview];
}
_refreshTableFooterView = nil;
}
//刷新delegate,开始重新加载时调用
-(void)beginToReloadData:(EGORefreshPos)aRefreshPos{
_reloading = YES;
if (aRefreshPos == EGORefreshHeader)
{
// 刷新
[self performSelector:@selector(refreshView) withObject:nil afterDelay:2.0];
}else if(aRefreshPos == EGORefreshFooter)
{
// 加载
[self performSelector:@selector(getNextPageView) withObject:nil afterDelay:2.0];
}
}
//刷新完成调用的方法
- (void)refreshView
{
NSLog(@"刷新完成");
[self testFinishedLoadData];
}
//加载调用的方法
- (void)getNextPageView
{
for (int i = 0; i < 9; i ++) {
[_images addObject:[NSString stringWithFormat:@"%d.jpg",i%10+1]];
}
[tmQuiltView reloadData];
[self removeFooterView];
[self testFinishedLoadData];
}</span>
4)实现的协议
<span style="font-size:18px;">#pragma mark UIScrollViewDelegate
//滚动控件的委托方法,scrollView滚动时,就调用该方法。任何offset值改变都调用该方法。即滚动过程中,调用多次
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (_refreshTableHeaderView) {
[_refreshTableHeaderView egoRefreshScrollViewDidScroll:scrollView];
}
if (_refreshTableFooterView) {
[_refreshTableFooterView egoRefreshScrollViewDidScroll:scrollView];
}
}
// 滑动视图,当手指离开屏幕那一霎那,调用该方法。一次有效滑动,只执行一次。
// decelerate,指代,当我们手指离开那一瞬后,视图是否还将继续向前滚动(一段距离),经过测试,decelerate=YES
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (_refreshTableHeaderView) {
[_refreshTableHeaderView egoRefreshScrollViewDidEndDragging:scrollView];
}
if (_refreshTableFooterView) {
[_refreshTableFooterView egoRefreshScrollViewDidEndDragging:scrollView];
}
}
#pragma mask -EGORefreshTableDelegate
//下拉被触发时调用,触发刷新操作,
//实现委托方法 egoRefreshTableHeaderDidTriggerRefresh,在里面开始实现你自己的数据刷新。
- (void)egoRefreshTableDidTriggerRefresh:(EGORefreshPos)aRefreshPos
{
[self beginToReloadData:aRefreshPos];
}
//返回当前是刷新还是无刷新状态
- (BOOL)egoRefreshTableDataSourceIsLoading:(UIView*)view
{
return _reloading ;
}
//返回刷新时间的回调方法
- (NSDate*)egoRefreshTableDataSourceLastUpdated:(UIView*)view
{
return [NSDate date];
}//无论是之后自定义footer还是这个header,判断到底是否触发刷新动作的时候,除了判断拖动到了什么位置之类的,都判断了_reloading值,如果正在_reloading,即使再怎样上拉下拉,都不能刷新,否则势必影响当前正在进行的数据加载或刷新操作
//数据源和代理方法需要的就这么多,有其它需求可以跳转到定义数据源和代理的类看一下。给出的demo中将-(NSArray *)images和- (UIImage *)imageAtIndexPath:(NSIndexPath *)indexPath两个方法也写在了数据源中,注意这两个方法不是数据源的方法,其中TMPhotoQuiltViewCell是我直接从demo中拷贝出来的自定义的单元,你可以根据自己的需求将其改成你想要的外观。代理方法与tableview的代理很是相似,所以也很容易懂。
- (NSMutableArray *)images
{
if (!_images) {
NSMutableArray *imageNames = [NSMutableArray array];
for (int i = 0; i< 9; i++) {
[imageNames addObject:[NSString stringWithFormat:@"%d.jpg",i%10+1]];
}
_images = [imageNames retain];
}
return _images ;
}
- (UIImage *)imageAtIndexPath:(NSIndexPath *)indexPath
{
return [UIImage imageNamed:[self.images objectAtIndex:indexPath.row]];
}
#pragma mark - TMQuiltViewDataSource
- (NSInteger)quiltViewNumberOfCells:(TMQuiltView *)TMQuiltView
{
return [self.images count];
}
- (TMQuiltViewCell *)quiltView:(TMQuiltView *)quiltView cellAtIndexPath:(NSIndexPath*)indexPath
{
TMPhotoQuiltViewCell *cell = (TMPhotoQuiltViewCell *)[quiltView dequeueReusableCellWithReuseIdentifier:@"PhotoCell"];
if (!cell) {
cell = [[[TMPhotoQuiltViewCell alloc] initWithReuseIdentifier:@"PhotoCell"] autorelease];
}
cell.photoView.image = [self imageAtIndexPath:indexPath];
cell.titleLabel.text = [NSString stringWithFormat:@"%d",indexPath.row];
return cell ;
}
#pragma mark - TMQuiltViewDelegate
//列数
- (NSInteger)quiltViewNumberOfColumns:(TMQuiltView *)quiltView
{
if ([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeLeft ||[[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeRight) {//横屏幕
return 3 ;
}else{
return 2 ;
}
}
//行高
- (CGFloat)quiltView:(TMQuiltView *)quiltView heightForCellAtIndexPath:(NSIndexPath *)indexPath
{
return [self imageAtIndexPath:indexPath].size.height/[self quiltViewNumberOfColumns:quiltView];
}
@end</span>
5)TMPhotoQuiltViewCell.m文件里面
- (void)dealloc {
[_photoView release], _photoView = nil;
[_titleLabel release], _titleLabel = nil;
[super dealloc];
}
- (id)initWithReuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithReuseIdentifier:reuseIdentifier];
if (self) {
self.backgroundColor = [UIColor whiteColor];
}
return self;
}
- (UIImageView *)photoView {
if (!_photoView) {
_photoView = [[UIImageView alloc] init];
_photoView.contentMode = UIViewContentModeScaleAspectFill;
_photoView.clipsToBounds = YES;
[self addSubview:_photoView];
}
return _photoView;
}
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] init];
_titleLabel.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
_titleLabel.textColor = [UIColor whiteColor];
_titleLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:_titleLabel];
}
return _titleLabel;
}
- (void)layoutSubviews {
self.photoView.frame = CGRectInset(self.bounds, kTMPhotoQuiltViewMargin, kTMPhotoQuiltViewMargin);
self.titleLabel.frame = CGRectMake(kTMPhotoQuiltViewMargin, self.bounds.size.height - 20 - kTMPhotoQuiltViewMargin,
self.bounds.size.width - 2 * kTMPhotoQuiltViewMargin, 20);
}
4:图片展示