凡是进行iOS开发的,基本上都会遇到要展示列表,或者即使不是标准列表,但由于数量不固定,也需要如同列表一样从上往下显示、加载的情况。这些,都绕不过对UITableView的使用。
这篇文章就是讲述我对UITableView的认识,帮助和我一样的新人将其更快的理解。
我推荐的创建方式
无论是代码还是Storyboard,你都会发现有两种方式创建与tableview有关的控件。一种是直接创建一个UITableViewController,还有便是在别的ViewController里面创建一个UITableView。
刚开始接触Xcode的时候,我是用Storyboard里面的UITableViewController,觉得上手很快。但是后来发现不太灵活,因为很难在这个里面追加其他的元素。
所以,我推荐大家使用的是:先创建UIViewController,然后在这里面增加一个UITableView,然后再增加其他的控件,可以很从容的布局。
我自己常用的是:UITViewController里面:自定义导航栏+UITableView+自定义工具栏
@interface WebPage()<UITableViewDelegate,UITableViewDataSource>{
UITableView *tableview;//这个是将要用来创建的列表
NSMutableArray *webpages;//这个数组是列表内容,通常会是可变的,便于增删改
}
@end
@implementation WebPage
-(void)viewDidLoad{
[super viewDidLoad];
webpages = [[NSMutableArray alloc]init];//可变数组要初始化一下,不然不能用,会报错,后面会讲到。
[self initalView];
}
//初始化界面
-(void)initalView{
CGRect rx = [ UIScreen mainScreen ].bounds;//获取屏幕尺寸
self.view.backgroundColor = [UIColor whiteColor];
UIView *Nav = [[UIView alloc]initWithFrame:CGRectMake(0, 0, rx.size.width, 50)];//这个是自定义导航栏,可略过
Nav.backgroundColor = bluewebpage;
[self.view addSubview:Nav];
UIButton *back = [[UIButton alloc]initWithFrame:CGRectMake(10, 10, 30, 30)];
back.tintColor = [UIColor whiteColor];
[back setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
back.titleLabel.font = [UIFont systemFontOfSize: 14.0];
[back setBackgroundImage:[UIImage imageNamed:@"back.png"] forState:UIControlStateNormal];
[back addTarget:self action:@selector(goback) forControlEvents:UIControlEventTouchUpInside];
[Nav addSubview:back];
UIImageView *wishimage =[[UIImageView alloc]initWithFrame:CGRectMake(rx.size.width/2-40-30+15, 10,30, 30)];
[wishimage setImage:[UIImage imageNamed:@"boximage.png"]];
[Nav addSubview:wishimage];
UILabel *title = [[UILabel alloc]initWithFrame:CGRectMake(rx.size.width/2-40+15, 10, 80, 30)];
title.text = @"网页";
title.textAlignment =NSTextAlignmentCenter;
title.textColor = [UIColor whiteColor];
title.font = [UIFont systemFontOfSize:18];
[Nav addSubview:title];
UIButton *refresh = [[UIButton alloc]initWithFrame:CGRectMake(rx.size.width-60, 10, 50, 30)];
refresh.tintColor = [UIColor whiteColor];
[refresh setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
refresh.titleLabel.font = [UIFont systemFontOfSize: 18.0];
[refresh setTitle:@"设置" forState:UIControlStateNormal];
[refresh addTarget:self action:@selector(setmore) forControlEvents:UIControlEventTouchUpInside];
[Nav addSubview:refresh];
//这个是初始化UITableView
tableview =[[UITableView alloc]initWithFrame:CGRectMake(0, 50, rx.size.width, rx.size.height-100) style:UITableViewStylePlain];
tableview.dataSource = self;
tableview.delegate =self;
[self.view addSubview:tableview];
//这个是自定义工具栏,可略过
UIView *toolBarView = [[UIView alloc]initWithFrame:CGRectMake(0, rx.size.height-50, rx.size.width, 50)];
toolBarView.backgroundColor = [UIColor bluecolor];
[self.view addSubview:toolBarView];
UIButton *addwebpage = [[UIButton alloc]initWithFrame:CGRectMake(10, 10, 30, 30)];
addwebpage.tintColor = [UIColor whiteColor];
[addwebpage setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
addwebpage.titleLabel.font = [UIFont systemFontOfSize: 14.0];
[addwebpage setTitle:@"添加" forState:UIControlStateNormal];
[addwebpage addTarget:self action:@selector(newpage) forControlEvents:UIControlEventTouchUpInside];
[toolBarView addSubview:addwebpage];
UIButton *sharebtn = [[UIButton alloc]initWithFrame:CGRectMake(rx.size.width/2-15, 10, 30, 30)];
sharebtn.tintColor = [UIColor whiteColor];
[sharebtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
sharebtn.titleLabel.font = [UIFont systemFontOfSize: 14.0];
[sharebtn setTitle:@"分享" forState:UIControlStateNormal];
[sharebtn addTarget:self action:@selector(share) forControlEvents:UIControlEventTouchUpInside];
[toolBarView addSubview:sharebtn];
UIButton *previewpage = [[UIButton alloc]initWithFrame:CGRectMake(rx.size.width-50, 10, 30, 30)];
previewpage.tintColor = [UIColor whiteColor];
[previewpage setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
previewpage.titleLabel.font = [UIFont systemFontOfSize: 14.0];
[previewpage setTitle:@"预览" forState:UIControlStateNormal];
[previewpage addTarget:self action:@selector(preview) forControlEvents:UIControlEventTouchUpInside];
[toolBarView addSubview:previewpage];
}
关于创建流程的简要说明
1.首先,肯定是要创建内容数组和tableview了。
2.然后要对tableview有个基本设置,包括下面写到的:
1)delegate和datasource:不设置会显示不出tableview,但也不会崩溃
2)整体布局:不设置或设置的不对,会崩溃
3)cell的内容和高度:不设置或设置的不对,会崩溃
其中,整体布局与cell的内容和高度是有相关性的,而且往往都会与内容数组有直接关系,如果对不上,就可能崩溃。
如果内容数组的内容是异步加载,或动态更新的话,那么还有两步:
3.更新数组,无论是从本地数据中还是网络中
4.[tableview reloadData];//这个tableview是你自己创建的那个。
关于delegate和datasource
在上文中可以看到:
tableview.dataSource = self;
tableview.delegate =self;
这个是非常重要的,这个决定了tableview是否能够被使用,以及能否把数据加载到tableview里面去。
没太多可说的,加上这两行代码就好了。
说明:直接创建UITableViewController,就不用写这两行代码了。如果是在storyboard里面创建的,可以救灾storyboard里面关联。
整体布局
这个不写的话,会直接崩溃
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 2;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == 0) {
return 1;
}else{
return webpages.count;
}
}
关于cell内容及高度的
TableView里面的每一行格子被称为cell,这个不难理解。必须设置内容(可以为空)和高度,否则崩溃。
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger section = [indexPath section];
if (section==0) {
static NSString *CellIdentifier = @"WebCoverCell";
WebCoverCell *cell= [tableview dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[WebCoverCell alloc] initWithReuseIdentifier:CellIdentifier];
}
//这里写内容
return cell;
}
else{
WebPageCell *cell= [tableview dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[WebPageCell alloc] initWithReuseIdentifier:CellIdentifier];
}
//这里写内容,但是最好是调用你在cell布局里面写好的控件;也能添加新的控件,但最好不要这样做,因为这样的话,在列表上下滚动的时候,这个cell里面就会不断创建你添加的这个新控件。
如:cell.webpage_label.text=@"haha";cell.webpage_image.image =...;
cell.background_image=...;
return cell;
}
}
关于cell自己的布局,用默认的基本不够用,所以一般是会自定义的
//
// UITableViewCell+WebPageCell.h
// wenyi
//
// Created by li xinglin on 16/3/5.
// Copyright © 2016年 nashihuakai team. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface WebPageCell:UITableViewCell
@property (nonatomic,retain) UILabel *webpage_label;
@property (retain, nonatomic) UIImageView *webpage_image;
@property (retain, nonatomic) UIImageView *background_image;
-(id)initWithReuseIdentifier:(NSString*)reuseIdentifier;
@end
//
// UITableViewCell+WebPageCell.m
// wenyi
//
// Created by li xinglin on 16/3/5.
// Copyright © 2016年 nashihuakai team. All rights reserved.
//
#import "UITableViewCell+WebPageCell.h"
@implementation WebPageCell
-(id)initWithReuseIdentifier:(NSString*)reuseIdentifier{
self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
if (self) {
[self initLayuot];
}
return self;
}
//初始化控件
-(void)initLayuot{
CGRect rx = [ UIScreen mainScreen ].bounds;//获取屏幕尺寸
int cellheight = 0;
self.background_image = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, rx.size.width, 60)];
[self addSubview:self.background_image];
self.webpage_image = [[UIImageView alloc] initWithFrame:CGRectMake(10, 5, 70, 70)];
self.webpage_image.contentMode = UIViewContentModeScaleAspectFill;//图片不会变形!!!!!!
self.webpage_image.clipsToBounds = YES;//保证多余的部分不会显示!!!!!
self.webpage_image.backgroundColor = [UIColor groupTableViewBackgroundColor];
[self addSubview:self.webpage_image];
self.webpage_label = [[UILabel alloc] initWithFrame:CGRectMake(10, 5, rx.size.width-20, 70)];
//self.webpage_label.font = [UIFont fontWithName:fontstyle_yinxiang size:16];
//self.webpage_label.textAlignment = NSTextAlignmentLeft;
//self.webpage_label.lineBreakMode = NSLineBreakByCharWrapping;
self.webpage_label.numberOfLines = 3; // 最关键的一句
[self addSubview:self.webpage_label];
cellheight +=80;
//设置当前cell高度
CGRect frame = [self frame];
frame.size.height = cellheight;
self.frame = frame;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
// [super setSelected:selected animated:animated]; 这样就没有点击时的效果了
}
-(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
}
@end
设置cell的高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
// NSUInteger section = [indexPath section];
if (indexPath.section==0) {
return SCREENWIDTH*5/8+40+40;
}
else{
return 80;
/*如果高度不固定的话
WebPageCell *cell =(WebPageCell*)[self tableView:tableview cellForRowAtIndexPath:indexPath];
return cell.frame.size.height;
*/
}
}
关于更多布局的
//直接写header的
//-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
//{
//
// NSString *groupname = [self.headers objectAtIndex:section];
// return groupname;
//}
//section头部间距
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 10;//section头部高度
}
//section头部视图
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *view=[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 10)];
view.backgroundColor = [UIColor clearColor];
return view;
}
//section底部间距
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
return 1;
}
//section底部视图
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
UIView *view=[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 10)];
view.backgroundColor = [UIColor clearColor];
return view;
}
关于TableView的操作的
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger section = [indexPath section];
if (section==0) {
//个别情况下,需要调用一下主线程,我也没搞懂
dispatch_async(dispatch_get_main_queue(), ^{
SetWeb *setwebcontrollor =[[SetWeb alloc]init];
setwebcontrollor.isEdit =YES;
setwebcontrollor.web = self.web;
[self presentViewController:setwebcontrollor animated:YES completion:NULL];
});
}
else{
NSDictionary *webpage = [self.webpages objectAtIndex:indexPath.row];
NSString *content = [webpage objectForKey:@"web_page_content"];
NSString *videourl = [webpage objectForKey:@"web_page_videourl"];
dispatch_async(dispatch_get_main_queue(), ^{
WebPageWord *webpagesetcontrollor =[[WebPageWord alloc]init];
webpagesetcontrollor.isEdit =YES;
webpagesetcontrollor.web = self.web;
webpagesetcontrollor.webpage = [self.webpages objectAtIndex:indexPath.row];
[self presentViewController:webpagesetcontrollor animated:YES completion:NULL];
});
}
}
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath{
// NSDictionary *webpage = [self.webpages objectAtIndex:indexPath.row];
// int wishid = [[webpage objectForKey:@"wishid"] intValue];
//int status = [[webpage objectForKey:@"status"] intValue];
//设置删除按钮
UITableViewRowAction *deleteRowAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive title:@"删除" handler:^(UITableViewRowAction *action,NSIndexPath *indexPath) {
UIAlertController * alertController = [UIAlertController alertControllerWithTitle: nil
message: @"确定要删除吗?"
preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
}]];
[alertController addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
[self deleteWeb:indexPath];
/*
[self.webpages removeObjectAtIndex:indexpath.row];
[tableview deleteRowsAtIndexPaths:@[indexpath] withRowAnimation:UITableViewRowAnimationAutomatic];
*/
}]];
[self presentViewController:alertController animated:YES completion:nil];
//在数据库中删除
}];
//设置收藏按钮
UITableViewRowAction *collectRowAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:@"下移" handler:^(UITableViewRowAction *action,NSIndexPath *indexPath) {
if (indexPath.row == self.webpages.count-1) {
//
}else{
[self moveWeb:indexPath direction:0];
}
/*
[self.webpages exchangeObjectAtIndex:(int)indexpath.row withObjectAtIndex:movetorow];
NSIndexPath *newindexpath = [NSIndexPath indexPathForRow:movetorow inSection:indexpath.section];
[tableview moveRowAtIndexPath:indexpath toIndexPath:newindexpath];
*/
}];
collectRowAction.backgroundColor = [UIColor orangeColor];
//设置置顶按钮
UITableViewRowAction *topRowAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:@"上移"handler:^(UITableViewRowAction *action,NSIndexPath *indexPath) {
if (indexPath.row == 0) {
//
}else{
[self moveWeb:indexPath direction:1];
}
/*
[self.webpages exchangeObjectAtIndex:(int)indexpath.row withObjectAtIndex:movetorow];
NSIndexPath *newindexpath = [NSIndexPath indexPathForRow:movetorow inSection:indexpath.section];
[tableview moveRowAtIndexPath:indexpath toIndexPath:newindexpath];
*/
}];
//collectRowAction.backgroundEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
topRowAction.backgroundColor = greenstory;
//设置插入按钮
UITableViewRowAction *insertRowAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:@"插入"handler:^(UITableViewRowAction *action,NSIndexPath *indexPath) {
insertnumber = (int)indexPath.row;
[self popView];
}];
//collectRowAction.backgroundEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
insertRowAction.backgroundColor = bluewebpage;
if (indexPath.section == 1) {
if (indexPath.row ==0) {
if (self.webpages.count==1) {
return @[deleteRowAction,insertRowAction];
}else{
return @[deleteRowAction,collectRowAction,insertRowAction];
}
}else if(indexPath.row ==self.webpages.count-1){
return @[deleteRowAction,topRowAction,insertRowAction];
}else{
return @[deleteRowAction,collectRowAction,topRowAction,insertRowAction];
}
}else{
return nil;
}
}
容易出错的情况
1.没有绑定delegate和datasource:现象是tableview为空或没反应;
2.收到错误参数:考虑内容数组没有初始化;数组与布局不匹配;
3.操作时发生崩溃
首先需要注意的是,tableview中的增加、删减操作,往往要与内容数组同步进行。否则当二者不统一的时候,容易发生崩溃,尤其是列表在上下滑动的时候。
我发生的一个例子:
在使用下拉刷新的时候,我们往往要先设置原来的内容数组为空,然后把新获取到的数组添加进去,再reload这个tableview。看起来很简单,但我去弄错过很多次。
事实上,严格的顺序是这样:1)先获取,无论是从本地还是网络,2)获取成功后,清空原来的数组,添加新数组;3)[tableview reloadData];
我有时忘记了,会在获取之前就清空数组,结果在下拉列表回弹的时候,由于要加载数组,致使程序崩溃。