一、我们这个利用可重用cell展示汽车品牌案例,就说完了,然后我们再给大家说一个什么东西,就是这个刷新数据这个东西, 1.刷新数据这个东西,我们用的就是代理,再把代理相当于是再给大家复习一下,再把代理复习一下,顺便再说一下怎么刷新数据, 2.接下来,要实现一个什么功能呢,是这么一个功能,来,打开我们这个代码, 我先运行起来这个程序,注意看, 这个是展示一个单组数据吧,展示一个单组数据, 然后把这个高度都给它改的一样了,我把高度都改的一样了, 在我们那个viewDidLoad里面,就是要删掉了, 在我们那个代理方法里面,heightForRowAtIndexPath,这些是不是要删掉吧,要删掉,不要这个东西, 删掉这个heightForRowAtIndexPath这个代理方法里面的东西, 这样每一行的高度就一样高了, 然后在我们的viewDidLoad里面,是不是通过这个把它的高度数统一设置为60, - (void)viewDidLoad{ [super viewDidLoad]; self.tableView.rowHeight = 60; } 然后,运行,高度都一样了吧,我希望实现这么一个效果,高度都一样了, 我希望实现这么一个效果: 当我选中某一行的时候,就是一选中某一行的时候,立刻弹出一个对话框,这个对话框当中显示这个英雄的名称, 然后呢,我可以修改这个英雄的名称,然后再点确定,然后这个对话框它就关了,然后呢,关了以后,这儿这个英雄的名称,就改了, 没有什么plist文件,就在内存, 我们刚才做的是不是把一个plist文件加载到内存,加载到内存的一个集合里面, 我们这里修改,就是把那个集合里面的数据改了,明白,把集合里面的数据改了, 现在知道我的需求了吗, 我的需求就想实现一个编辑的一个效果,编辑这个英雄的名称,一点它弹出一个窗口,可以显示英雄的名称,我可以改这个英雄的名称, 改完以后,一点确定,这个窗口关闭,同时这儿这个英雄的名称就变了,明白,就想实现这个效果, 要想实现这个功能, 1)第一步,我们要做的,就是怎么来监听选中某一行这个事件啊, 我们要在选中某行的这个事件里面,去弹出一个对话框,这是第一步,监听, 监听这个选中某一行的事件,监听用户选中某一行的事件, 就像我们UIScrollView里面的滚动事件一样, 也是不能够直接通过addTarget方式来监听, 需要通过什么来监听,需要通过代理来监听,需要通过代理来监听, 接下来,就做第一个功能,为UITableView监听,监听它选中某一行的这个事件,怎么来监听,通过代理来监听, 既然要通过代理,所以说,第一步怎么办: 1)第一步,设置UITableView的代理对象,设置UITableView的代理对象, 怎么设置代理,对,拖线就OK了, 直接选中我们这个UITableView,然后右键,选择delegate右边的加号,把它拖到View Controller上面, 这样就设置好代理了, 2)代理拖好以后,接下来,控制器要作为它的代理,所以就找到控制器, 控制器既然要作为UITableView的代理,所以说,控制器得遵守哪个协议, 是不是UITableViewDelegate,这个协议啊, UIScrollView那个代理协议叫什么,UIScrollViewDelegate吧,我们这个叫UITableViewDelegate, 遵守这个协议, 既然遵守这个协议了,接下来,就要怎么办, 3)实现代理方法, 代理方法,刚才这个方法,是不是设置行高的代理方法, 我们接下来,要实现的是, //监听行被选中的代理方法, //这个行被选中的代理方法是哪个呢,打开我们这个UITableViewDelegate,大家来猜一下,你觉得是哪个,哪个像, 你就看这个名称就可以了, 下面这两个,像吗, 像选中某一行的事件吗,像吧, select,是不是选中的意思,did,表示已经选中了吧,did,过去时,表示已经选中了吧, didSelect,表示已经选中这一行了吧, 那我再问一下,哪个方法像:当你选中第一行,这时候,你再选中第二行的时候,第一行要怎么样,要取消选择吧,然后是这个第二行被选中,哪个方法像取消选中这一行,取消选择这个方法,哪个像, didSelectedRowAtIndexPath,是选中这一行, didDeselectedRowAtIndexPath,是取消选中这一行吧, 选中第一行,当你选第二行的时候,第二行被选中,第一行就自动取消选中了吧,就这两个吧,didSelect和didDeselect, 所以说,我们这里要实现的是一个tableView didSelect这个方法, - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath; 这个方法什么时候被调用呢,Called after the user changes the selection, 当用户改变了选项的时候,这个方法会被调用, 就是这个,didSelectRowAtIndexPath,当用户选择完某行时候,会调用这个方法, 返回值类型是什么,void, - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ } 是这个方法吧,这个就是当你选中某行以后,会调用的方法, 我们看一下是不是这个方法,打印一句话试试, - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ NSLog(@“%ld”,(long)indexPath.row); } 现在,当我选中第一行的时候,索引是0吧,这里打印出来0, 我再选中其他的行,是不是选中哪行,哪行的索引被打出来吧, 也就是说,这个方法,是当你选中某一行时候,要执行的方法啊, 好,那我们在这个方法中,不需要打印一个当前是第几行吧,而需要在这里面显示一个对话框吧, 2.显示对话框, 对话框,还记得我们说过一个对话框的类型吗, 是不是UIAlertView啊, UIAlertView *alertView = [[UIAlertView alloc] initWithTitle initWithTitle,是不是这个方法, 这就是创建一个对话框吧, 这就是创建一个对话框对象,五个参数, 1)第一个,initWithTitle:(NSString *),标题,是什么,就是编辑吧,比如说,可以叫编辑吧,@“编辑英雄”, 2)第二个,message:(NSString *),消息,message,来个什么消息啊,比如说,我可以来个,@“哈哈”, 3)第三个,delegate:(id),代理,先给它来个nil, 4)第四个,cancelButtonTitle:(NSString *),这个cancelButton标题,给它来个,@“取消”, 5)第五个,otherButtonTitles:(NSString *),这个otherButton,给它来个,@“确定”, 6)最后,给它来个nil, UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@“编辑英雄” message:@“哈哈” delegate:nil cancelButtonTitle:@“取消” otherButtonTitles:@“确定”,nil]; 这只是创建好这个对象了,要给它显示出来,怎么办, [alertView show]; 调用它的show方法,是不是就显示出来了, 创建一个对话框对象,显示对话框, 选中一个英雄,是不是弹出来了, 然后点“确定”,或者点“取消”,是不是就关了, 但是我们要的是这里需要显示“哈哈”吗,不是吧,应该是需要显示的是我们的英雄的名称吧,不是说显示“哈哈”吧, 既然要显示英雄的名称,是不是要在这里获取当前这个被选中的那行的英雄吧, 3.要获取当前被选中这行的那个英雄的名称,怎么获取当前被选中这行的英雄的名称呢, 1)那么,首先,你在这个方法中是不是可以获取当前被选中的是哪一行吧, //获取当前被选中的这行的英雄的名称,indexPath.row,是不是当前被选中的行, indexPath.row, //然后呢,数据在哪里,self.heros,里面是不是包含当前所有的英雄的数据, self.heros[indexPath.row]; //那么这样的话,就拿到了这个当前被选中这行的英雄的模型了吧,这样就拿到英雄的模型了, CZHero *hero = self.heros[indexPath.row]; //然后,你这个地方要的是英雄的名称,英雄的名称怎么办, hero.name; //这是不是英雄的名称,是不是,我把它放到这个message:@“哈哈”这个地方,替换@“哈哈”, UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@“编辑英雄” message:hero.name delegate:nil cancelButtonTitle:@“取消” otherButtonTitles:@“确定” , nil]; //这样的话,我们看一下,能不能把英雄的名称显示出来, //英雄的名称是不是有了, //但是我们现在能编辑这个名称吗,不能,因为这个地方没有什么,没有文本框,这是个Label,不是文本框, //当这个名称显示到文本框里面的时候才能编辑啊,也就是说,我们现在要求弹出了这个对话框,这里需要有一个文本框, //现在这里有文本框吗,没有,要想让它显示文本框,得修改一下,不需要我们手动去创建文本框, //只要修改一下这个AlertView的样式,它就会自动有一个文本框了, //修改UIAlertView的样式,显示出一个文本框来, //怎么修改UIAlertView的样式呢,注意看, 想修改UIAlertView的样式,直接调它的alertViewStyle, alertView.alertViewStyle = UIAlertViewStylePlainTextInput; //想修改UIAlertView的样式,直接调它的alertViewStyle,用哪个样式呢,用这个UIAlertViewStylePlainTextInput,这个样式, //其实这个你不需要记,AlertView的样式,看看就知道了, alertView.alertViewStyle //你一看它,就知道它是个啥,是个枚举吧,所以你直接写就可以了, alertView.alertViewStyle = UIAlertViewStylePlainTextInput; //哪个Style比较像,输入文本框就是啊,TextInput, //下面这个是密码框吧,SecureTextInput, //第二个是登录和密码框吧,LoginAndPasswordInput, //我们要的只是一个普通文本框,TextInput, alertView.alertViewStyle = UIAlertViewStylePlainTextInput; //运行一下,看看,是不是就有了, 但是,这个时候,是不是要让这个文字,显示到这个里面,也就是说,我们设置这个message的时候,它只能只会显示在这个Label里面,是不会显示到这个地方的, 第一,我们这儿这个文字还要吗,不要了,所以说呢,message这里,大胆的变成nil, 变成nil以后,我们要干什么,接下去是要, //获取那个文本框,并且设置文本框中的什么,文字为hero.name, //是不是要做这个操作,那么我的问题是,怎么获取这个文本框呢, //你这样做,是显示这个文本框了, alertView.alertViewStyle = UIAlertViewStylePlainTextInput; //要获取这个文本框,得这样做, //alertView,有一个方法叫做,textFieldAtIndex, //也就是说,将来这个alertView中可能会弹出很多的文本框,你要获取哪个,文本框,根据文本框的索引,就可以了, //现在,我们只有一个文本框,是不是就是索引为零的这个文本框啊, //就是根据它的这个方法,textFieldAtIndex,第几个文本框呢,索引为零的这个文本框,拿到以后,直接设置它的文字,为hero.name,就搞定了, //根据索引获取指定的某个文本框, [alertView textFieldAtIndex:0]; //设置这个文本框的文字为,hero.name, [alertView textFieldAtIndex:0].text = hero.name; //来,回到咱们的代码里面, [alertView 调它的方法,textFieldAtIndex:第几个,第零个,0,] [alertView textFieldAtIndex:0]; //这样是不是就拿到它的文本框了, //“点”text,等于什么,hero.name,吧, [alertView textFiledIndex:0].text = hero.name; //这样就可以了吧,运行一下看看, 4.接下来,我修改文本框里面的文字,一点确定,我希望把文字更新到数据里面,点“取消”的时候,不更新数据, 所以,接下来,因为这个点“确定”,和点“取消”,这两个内容是不一样的,所以,接下来,我们得监听这两个按钮的点击事件, 所以,接下来,得判断,如果点击“确定”,怎么怎么着,如果点击“取消”,需要管吗,不需要管吧, 所以,接下来,要做的就是监听这个AlertView的按钮的点击事件, 注意,这个AlertView中,具体是哪个按钮的点击事件,也不能通过addTarget方式来监听, 也得通过代理来监听, 这个按钮的点击事件, 这个时候,我们想监听谁的事件,AlertView的这个按钮的单击事件吧, 既然是AlertView有这个事件,所以说,要用代理来实现,要给谁设置这个代理对象,AlertView,要给AlertView设置这个代理对象, 所以说,接下来,我们在创建这个AlertView的时候, 我们在创建这个AlertView的时候,这里是不是有个delegate,是又个delegate,这个delegate就是,你在创建的时候,就可以指定一下,谁作为这个AlertView的代理对象, 对,self,让控制器作为这个AlertView的代理对象,大家学会举一反三了,学会抢答了,让self,控制器,作为这个AlertView的代理对象, 这个就是设置控制器作为AlertView的代理对象, 既然控制器要作为AlertView的代理对象,你说,控制器能随便做吗,得怎么样,得遵守AlertView的代理协议吧,遵守UIAlertViewDelegate, 控制器得遵守UIAlertViewDelegate, 我们控制器现在要作为AlertView的代理对象,所以,现在要遵守的协议,就是AlertView的代理协议, 现在,控制器已经可以作为AlertView的代理对象了吧,但是这个AlertView,还没有实现代理方法吧, 这个代理协议里面到底有多少个代理方法,哪些是我们要的,我们得看一看吧,注意,AlertView中的代理方法,非常非常的少,就这么几个, 就这么几个,我们现在要监听点击了哪个按钮,需要实现哪个代理方法,是不是就是第一个啊,clickedButtonAtIndex, - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex; 就是这个代理方法啊, 这个方法的意思就是, //当点击了AlertView上的按钮的时候,会执行这个方法, - (void)alertView:(UIAlertView *) alertView clickedButtonAtIndex:(NSInteger)buttonIndex{ //code; } 那么,这个方法中有一个参数,叫做buttonIndex,这个指的就是点击的是第几个按钮,是那个按钮的索引, 比如说,这里有两个按钮,点击第一个按钮,就是0,点击第二个按钮,就是1, 点第一个按钮,“取消”,buttonIndex就是0,点第二个按钮,“确定”,buttonIndex就是1,但是,我们要验证一下, 我在这个代理方法中,打印一句话, NSLog(@“%ld”,(long)buttonIndex); 验证一下,buttonIndex, 当我点完左边的按钮,“取消”的时候,返回的是几,0, 当我点完右边的按钮,“确定”的时候,返回的是几,1, 也就是说,只要返回的是1,是不是就是点的“确定”,只要返回的是0,点的就是“取消”, 所以说,我们这里是不是可以做一个判断, 如果,这个buttonIndex等于等于1,表示点击的是什么, 表示点击的是“确定”吧, 如果是0,我们还需要做任何事情吗,0就不需要做了, - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{ //NSLog(@“%ld”,(long)buttonIndex); if(buttonIndex = = 1) { //表示点击的是“确定” } } 0就不需要做了,如果是1的话,这个时候是要更新数据吧,更新数据, 更新数据的意思是什么意思,注意,更新数据的意思是, 1)第一步,获取用户在文本框中修改以后的这个值, 2)第二步,找到那个数据集合里面,把英雄集合里面, 找到对应的那个英雄,然后把那个英雄模型里面的name改成用户输入的这个name,就OK了,明白, 这样的话,就把数据模型改了, 但是改数据模型以后,咱们等会儿看看它能不能显示出来, 咱们先把这个数据模型改了,可以吗, 咱们看看改了数据模型以后,能不能显示出来, //更新数据 //1.获取用户文本框中输入的内容 //2.找到对应的英雄模型 //3.修改英雄模型的name //4.刷新tableView 因为数据改了以后,你不刷新表格,能变吗, 当你重新刷新一下,才能把数据重新给你加载一下吧, 1)第一步,怎么获取文本框中用户输入的name, NSString * 首先拿到alertView的文本框,把它的text拿出来,就可以了, alertView 有一个方法,叫做textFieldAtIndex, 这里只有一个文本框,所以它的序号是0, NSString *name = [alertView textFieldAtIndex:0].text; 这是拿到用户修改的这个名称了吧, 2)第二步,找到当前选中的这行,对应的英雄的模型, CZHero *hero = self.heros self.heros,是不是所有的英雄模型啊,你只要再给我传一个索引, self.heros[?],你只要再给我传一个索引,当前选中这行的索引,是不是就能拿到当前选中这行的英雄模型, 但是你告诉我,这儿能拿到这个索引吗,这是啥,这是alertView的代理方法吧,在里面怎么能,可能拿到当前选中这行的索引呢, 在这个alertView的代理方法里面,怎么可能拿到当前选中的tableView的这行的索引呢,绝对拿不到的,对吧,绝对拿不到, 有人说,拿不到的时候,我们在什么地方,大家告诉我,在什么地方才能拿到当前选中的这一行的索引呢, 在这个didSelect方法中,才可以吧, 在这个方法中才可以拿到当前选中行的索引吧, - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath; 在这个方法中,我们通过这个indexPath.row,是不是可以拿到当前选中行的索引, 但是,你要在那个方法中,拿到indexPath.row,能拿到吗, 拿不到, 有人说,可以在我们这个控制器里面加一个属性,是不是可以引用啊, 或者说,我不加属性,也能引用,看我怎么做, 还记得任何一个UI控件中,都有这个tag吗, alertView.tag = indexPath.row; 在didSelect方法中,创建alertView的时候,就把当前行的索引,赋值给alertView的tag, //把当前行的索引,保存到alertView的tag中, alertView.tag = indexPath.row; //然后,你在alertView的代理协议中,代理方法中,能不能拿到alertView, //可以, //在alertView的代理方法中,可以拿到alertView吧, //既然能拿到alertView,alertView.tag,这是不是那个行索引, //这样是不是拿到当前选中的那行的模型了吧, CZHero *hero = self.heros[alertView.tag]; //然后咋办, hero.name = name; //这是不是把这个英雄模型的name修改了吧, //修改了一个,大家说,现在是不是就完了, //来,咱们试试看, //我把英雄的名字改成“暴走”,点确定,它变了吗,没有, //但是我会变魔术,看一看,我把它往上拖,出了屏幕之后,再拉回来,变了吗,变了, //变了没,变了吧, //谁能告诉我为什么变了, //肯定不是因为我的魔术对吧,好,为什么刚改了以后它不变,但是我刷一下,就变了, //我往下滚,它会变吗,不会,必须得出了屏幕,再回来,它才会变吧, //还记得刚才我们说,每一次滚动,当一个单元格出现的时候,当一个单元格显示到屏幕里面的时候,会调用哪个方法, //cellForRowAtIndexPath,吧, //是不是会调用我们那个数据源方法吧, //当一个单元格即将显示到屏幕的时候,就会调我们这个数据源方法, //在这个数据源方法中,我们说,会重新获取模型, //然后,获取单元格,重新设置一下这个数据, //所以说,这就显示了,为什么滚一下就有了呢,因为当你把它滚出去,再滚进来的时候,即将滚进来的时候,它会重新调用我们这个数据源方法,而在这个数据源方法中,你重新获取了集合中的数据,重新把集合中的数据设置给单元格,所以说,单元格是不是就显示这个数据了, //那么,也就是说,只要它重新调用一下数据源方法,是不是就可以刷新数据了, //那么,也就是说,注意听,也就是说,我们接下来这个刷新tableView,什么叫做刷新tableView, //刷新tableView的意思是什么意思,重新加载数据对吧,那么其实换句话说,刷新tableView的意思,是重新加载数据,也是,什么叫做重新加载数据,重新加载数据的意思就是,重新调用UITableView的数据源对象的那些数据源方法, //刷新数据的意思,就是重新调用数据源方法,就是刷新数据, //所以说,我们这里就是要刷新数据, //4.刷新tableView(重新刷新数据的意思就是重新调用UITableView的数据源对象中的那些数据源方法) //这儿指的就是,重新刷新数据的意思, //那么,对于我们来说,一句话就搞定,怎么改, [self.tableView reloadData]; //重新刷新tableView //[self.tableView reloadData],这句话什么意思,重新刷新数据,重新刷新数据什么意思,调用那个数据源方法吧, //这就是最后我们编辑的这个效果, 四、在这个编辑的效果里面,我们学了几个知识点, 1.如何监听选中某一行事件,怎么监听, 1)首先,设置UITableView的代理, 2)代理里面,有一个didSelect方法,这就是当前选中某一行这个事件, 3)然后呢,当点击的时候,弹出一个对话框,alertView, 4)监听alertView的事件,就可以,给它执行我们这个编辑操作了, 5)但是,最后记得,你要改过数据模型以后,得刷新一下数据, 2.可以单个刷新,接下来,我们就给大家说这个单个刷新, 1)[self.tableView reloadData]; 这个表示的是刷新整个tableView, 2)你就改了一个人的名字,刷新整个tableView,你觉得这样效率好吗, 显然不好吧, 接下来,我希望,改了哪行,刷新哪行, 3.接下来,做一个局部刷新, 局部刷新什么意思啊,就是你修改了哪行,我刷新哪行,没有必要刷新整个tableView, 那么,怎么实现局部刷新呢, 1)首先,你要刷新谁, UITableView吧,所以说,是不是得调UITableView的方法, [self.tableView reload…… 不是第一个,reloadData,这是刷新整个tableView, 也不是第二个,reloadInputViews, 是第三个,reloadRowsAtIndexPaths:(NSArray *) withRowAnimation:(UITableViewAnimation) 不是第五个,reloadSections,这个是刷新整个的组, 我们这个第三个,是刷新指定的行, 我们这里是要改模一行,所以说,是不是要刷新指定的行,就OK了, 局部刷新,刷新指定的行, 2)刷新指定的行,第一个,它这儿是个什么参数, 数组, 第二个参数,不管它是什么参数,一看它没有星号,很有可能是枚举吧, 一看Animation,肯定是刷新的内容肯定是动画了, 刷新时候,你希望它用动画吗, 什么YES,这是个枚举,选一个就OK了吧, 选一种动画,UITableViewRowAnimation, 这么多种动画,选一个就OK了吧, 哪个动画,Automatic,用这个自动的动画就OK了吧, 3)然后,我们这个地方,接下来,这个第一个参数,数组是什么意思, 注意,第一个参数,你凡是要刷新的行,把那个行放进来, 你要刷新一行,这个数组里面就有一个元素, 你要刷新十行,这个数组里面是不是应该有十行这个行对象啊, 所以说,这个地方应该保存一个行对象,写一个@[ ], [self.tableView reloadRowsAtIndexPaths:@[ ] withRowAnimation:UITableViewRowAnimationAutomatic]; 4)现在,我们怎么创建一个行对象呢, 一、假如说,我们如何来定位UITableView中的某一行呢, UITableView中的这行,根据它是在第几组中的第几行,是不是就可以来定位啊, 它必须是第几组的第几行, 所以说,我们首先要创建一个行对象, 来,看一下,还记得我们在数据源方法, didSelectRowAtIndexPath:(NSIndexPath *)indexPath; 中的这个indexPath吗, 用这个indexPath,是不是就可以确定某一组的某一行吧, 所以说,这个行对象,我们就可以用这个NSIndexPath, 这个对象,这个类, 在alertView的代理方法clickedButtonAtIndex:(NSInteger)buttonIndex中, //4.刷新tableView中的数据, //局部刷新,刷新指定的行, //创建一个行对象, NSIndexPath *idxPath = [NSIndexPath indexPathFor…… 第二个, (NSIndexPath * )indexPathForRow:(NSInteger)inSection : (NSInteger), 先是行序号,再是组序号吧, 第几行呢,alertView.tag,这是第几行吧, 第几个组呢,我们一共是单组数据,还是多组数据, 单组数据吧, 所以说,这儿写啥,0,吧,就是第0组的第几行吧, NSIndexPath *idxPath = [NSIndexPath indexPathForRow:alertView.tag inSection:0]; 这就是那个行对象,把它直接写进来,虽然只有一个,也得放到数组里面, [self.tableView reloadRowsAtIndexPaths:@[idxPath] withRowAnimation:UITableViewRowAnimationAutomatic]; 这样的话,当你调这个方法的时候,就只会刷新这一行,其他行都不刷新, 运行一下看看,是不是很难看出动画啊,慢慢隐藏掉了, 给你换一个比较容易识别的动画, UITableViewRowAnimationLeft, 从左边显示出来, 这就是我们如何去编辑一行,就是这么来编辑, 这是我们这么一个效果,编辑的效果,就是这些