UITableViewHeaderFooterView header 视图在 reloadData 上向后呈现一半的时间
UITableViewHeaderFooterView header views presented backwards half of the times on reloadData
我已经对 UITableView 进行了分组。
这是我的 header views 方法 - 注意我这样做是为了它只加载一次和下一次而不是重新加载元素它只是 returns headerView 如果它有子视图它已经存在(我需要它,因为我在 headerView 中有元素改变状态,我不希望它们重新加载):
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
static NSString *headerReuseIdentifier = @"myHeader";
UITableViewHeaderFooterView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:headerReuseIdentifier];
if (headerView == nil) {
headerView = [[UITableViewHeaderFooterView alloc] initWithReuseIdentifier:headerReuseIdentifier];//[tableView dequeueReusableHeaderFooterViewWithIdentifier:headerReuseIdentifier];
CGRect frame=CGRectMake(0, 0, tableView.bounds.size.width, 44);
headerView.frame=frame;
}
if (headerView.contentView.subviews.count!=0){
return headerView;//do not reload
}
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(5, 0, tableView.frame.size.width-85, 44)];
[label setFont:[UIFont boldSystemFontOfSize:16]];
NSString *name=@"not found";
[label setText:name];
headerView.backgroundColor=[UIColor clearColor];
[headerView.contentView addSubview:label];
//... etc elements
return headerView;
}
我也为单元格实现了类似的逻辑,即如果子视图已经添加到单元格,它们不会重新加载元素,只是为了看看它在那里是如何工作的:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if (cell.contentView.subviews.count!=0) {
return cell;//do not reload
}
//adding elements here...
return cell;
}
============================================= ========================
这是第一次的样子,请注意 Step:1 和 Step:2 以正确的升序排列
现在,当调用 [myTableView reloadData] 时 table 的问题如下所示:
注意:headerView 视图是倒退的!但是,单元格总是以正确的顺序显示。
如果再次调用 [myTableView reloadData] - headerView 将以正确的顺序显示。
我可以通过避免调用 reloadData 来阻止这种情况发生,但我需要在单元格中而不是在 header 视图中重新加载数据。
有没有人经历过这种header浏览量翻转 - 你是如何解决的?
Xcode 8.3.2,运行 iPad iOS9.0 模拟器
可让您测试此错误头部视图重新排序的完整代码:
.h 文件
@interface TableTestViewController : UIViewController
@property(strong,nonatomic)IBOutlet UITableView *myTable;
@property(strong,nonatomic)NSMutableArray *steps;
@end
.m 文件
#import "TableTestViewController.h"
@interface TableTestViewController ()
@end
@implementation TableTestViewController
@synthesize steps,myTable;
- (void)viewDidLoad {
[super viewDidLoad];
steps=[[NSMutableArray alloc]initWithObjects:@"Step 1",@"Step 2", nil];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return steps.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
return 44.0;
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
static NSString *headerReuseIdentifier = @"myHeader";
UITableViewHeaderFooterView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:headerReuseIdentifier];
if (headerView == nil) {
headerView = [[UITableViewHeaderFooterView alloc] initWithReuseIdentifier:headerReuseIdentifier];//[tableView dequeueReusableHeaderFooterViewWithIdentifier:headerReuseIdentifier];
CGRect frame=CGRectMake(0, 0, tableView.bounds.size.width, 44);
headerView.frame=frame;
}
if (headerView.contentView.subviews.count!=0) {
return headerView;//do not reload when running
}
while (headerView.contentView.subviews.count) {
id subview=[headerView.contentView.subviews objectAtIndex:0];
[subview removeFromSuperview];
}
headerView.backgroundColor=[UIColor clearColor];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(5, 0, tableView.frame.size.width-85, 44)];
[label setFont:[UIFont boldSystemFontOfSize:16]];
[label setText:[steps objectAtIndex:section]];
[headerView.contentView addSubview:label];
int btnSz=tableView.bounds.size.width/6;
UIButton *startStepBtn = [[UIButton alloc]initWithFrame:CGRectMake(btnSz*5+5 ,5, 30, 30)];
[startStepBtn setTitle:@"" forState:UIControlStateNormal];
startStepBtn.tag=section+1000;
startStepBtn.enabled=NO;
[startStepBtn addTarget:self action:@selector(startStep:) forControlEvents:(UIControlEvents)UIControlEventTouchUpInside];
[headerView.contentView addSubview:startStepBtn];
return headerView;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
int i=1;
return 60*ceil((double)i/5)+10;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if ([cell.contentView.subviews count]!=0) {
return cell;//do not reload when running
}
//clear 1st
while( [cell.contentView.subviews count] ){
id subview = [cell.contentView.subviews objectAtIndex:0];
[subview removeFromSuperview];
}
int btnSz=tableView.bounds.size.width/6;
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(5, 0, tableView.frame.size.width-85, 44)];
[label setFont:[UIFont boldSystemFontOfSize:16]];
[label setText:[steps objectAtIndex:indexPath.section]];
[cell.contentView addSubview:label];
CGRect frame;
frame = CGRectMake(btnSz*5+5 ,5, 30, 30);
UIButton *delStepBtn = [[UIButton alloc]initWithFrame:frame];
[delStepBtn setTitle:@"❌" forState:UIControlStateNormal];
[delStepBtn addTarget:self action:@selector(deleteStep:) forControlEvents:(UIControlEvents)UIControlEventTouchUpInside];
[cell.contentView addSubview:delStepBtn];
frame = CGRectMake(btnSz*5+5 ,5+30, 30, 30);
UIButton *editStepBtn = [[UIButton alloc]initWithFrame:frame];
[editStepBtn setTitle:@"" forState:UIControlStateNormal];
[editStepBtn addTarget:self action:@selector(editStep:) forControlEvents:(UIControlEvents)UIControlEventTouchUpInside];
[cell.contentView addSubview:editStepBtn];
return cell;
}
-(void)editStep:(id)sender{
[myTable reloadData];
}
@end
执行,你会看到 headers 以升序排列,触摸锤子按钮会使 table 重新加载并且 headers 现在将以降序排列,再次触摸它们回到升序。
但是单元格内容始终保持原样,因为它应该按升序排列。
您正在进行的检查是没有必要的:
if (headerView.contentView.subviews.count!=0){
return headerView;//do not reload
}
但是,每次都实例化一个 UILabel
并不是一个好习惯。
每个实例化都应在 if (headerView == nil)
if-case 内完成,并且 UILabel
应该是此 header 或单元格的 属性。
顺便说一句,你可以在 header/cell.
出队时更改 text
属性
UITableViewHeaderFooterView
是可重复使用的视图,因此您无法确定 UITableView
每次都会 return 完全相同的视图。
我建议创建 UITableViewHeaderFooterView
的子类并在某些数据源外部保存状态。
如果您不想这样做,作为解决方法,您可以为每个部分使用唯一的重用标识符。
在这种情况下,您应该替换
static NSString *headerReuseIdentifier = @"myHeader";
与
NSString *headerReuseIdentifier = [NSString stringWithFormat: @"myHeader%ld", (long)section];
我已经对 UITableView 进行了分组。
这是我的 header views 方法 - 注意我这样做是为了它只加载一次和下一次而不是重新加载元素它只是 returns headerView 如果它有子视图它已经存在(我需要它,因为我在 headerView 中有元素改变状态,我不希望它们重新加载):
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
static NSString *headerReuseIdentifier = @"myHeader";
UITableViewHeaderFooterView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:headerReuseIdentifier];
if (headerView == nil) {
headerView = [[UITableViewHeaderFooterView alloc] initWithReuseIdentifier:headerReuseIdentifier];//[tableView dequeueReusableHeaderFooterViewWithIdentifier:headerReuseIdentifier];
CGRect frame=CGRectMake(0, 0, tableView.bounds.size.width, 44);
headerView.frame=frame;
}
if (headerView.contentView.subviews.count!=0){
return headerView;//do not reload
}
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(5, 0, tableView.frame.size.width-85, 44)];
[label setFont:[UIFont boldSystemFontOfSize:16]];
NSString *name=@"not found";
[label setText:name];
headerView.backgroundColor=[UIColor clearColor];
[headerView.contentView addSubview:label];
//... etc elements
return headerView;
}
我也为单元格实现了类似的逻辑,即如果子视图已经添加到单元格,它们不会重新加载元素,只是为了看看它在那里是如何工作的:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if (cell.contentView.subviews.count!=0) {
return cell;//do not reload
}
//adding elements here...
return cell;
}
============================================= ========================
这是第一次的样子,请注意 Step:1 和 Step:2 以正确的升序排列
现在,当调用 [myTableView reloadData] 时 table 的问题如下所示:
注意:headerView 视图是倒退的!但是,单元格总是以正确的顺序显示。
如果再次调用 [myTableView reloadData] - headerView 将以正确的顺序显示。
我可以通过避免调用 reloadData 来阻止这种情况发生,但我需要在单元格中而不是在 header 视图中重新加载数据。
有没有人经历过这种header浏览量翻转 - 你是如何解决的?
Xcode 8.3.2,运行 iPad iOS9.0 模拟器
可让您测试此错误头部视图重新排序的完整代码:
.h 文件
@interface TableTestViewController : UIViewController
@property(strong,nonatomic)IBOutlet UITableView *myTable;
@property(strong,nonatomic)NSMutableArray *steps;
@end
.m 文件
#import "TableTestViewController.h"
@interface TableTestViewController ()
@end
@implementation TableTestViewController
@synthesize steps,myTable;
- (void)viewDidLoad {
[super viewDidLoad];
steps=[[NSMutableArray alloc]initWithObjects:@"Step 1",@"Step 2", nil];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return steps.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
return 44.0;
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
static NSString *headerReuseIdentifier = @"myHeader";
UITableViewHeaderFooterView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:headerReuseIdentifier];
if (headerView == nil) {
headerView = [[UITableViewHeaderFooterView alloc] initWithReuseIdentifier:headerReuseIdentifier];//[tableView dequeueReusableHeaderFooterViewWithIdentifier:headerReuseIdentifier];
CGRect frame=CGRectMake(0, 0, tableView.bounds.size.width, 44);
headerView.frame=frame;
}
if (headerView.contentView.subviews.count!=0) {
return headerView;//do not reload when running
}
while (headerView.contentView.subviews.count) {
id subview=[headerView.contentView.subviews objectAtIndex:0];
[subview removeFromSuperview];
}
headerView.backgroundColor=[UIColor clearColor];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(5, 0, tableView.frame.size.width-85, 44)];
[label setFont:[UIFont boldSystemFontOfSize:16]];
[label setText:[steps objectAtIndex:section]];
[headerView.contentView addSubview:label];
int btnSz=tableView.bounds.size.width/6;
UIButton *startStepBtn = [[UIButton alloc]initWithFrame:CGRectMake(btnSz*5+5 ,5, 30, 30)];
[startStepBtn setTitle:@"" forState:UIControlStateNormal];
startStepBtn.tag=section+1000;
startStepBtn.enabled=NO;
[startStepBtn addTarget:self action:@selector(startStep:) forControlEvents:(UIControlEvents)UIControlEventTouchUpInside];
[headerView.contentView addSubview:startStepBtn];
return headerView;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
int i=1;
return 60*ceil((double)i/5)+10;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if ([cell.contentView.subviews count]!=0) {
return cell;//do not reload when running
}
//clear 1st
while( [cell.contentView.subviews count] ){
id subview = [cell.contentView.subviews objectAtIndex:0];
[subview removeFromSuperview];
}
int btnSz=tableView.bounds.size.width/6;
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(5, 0, tableView.frame.size.width-85, 44)];
[label setFont:[UIFont boldSystemFontOfSize:16]];
[label setText:[steps objectAtIndex:indexPath.section]];
[cell.contentView addSubview:label];
CGRect frame;
frame = CGRectMake(btnSz*5+5 ,5, 30, 30);
UIButton *delStepBtn = [[UIButton alloc]initWithFrame:frame];
[delStepBtn setTitle:@"❌" forState:UIControlStateNormal];
[delStepBtn addTarget:self action:@selector(deleteStep:) forControlEvents:(UIControlEvents)UIControlEventTouchUpInside];
[cell.contentView addSubview:delStepBtn];
frame = CGRectMake(btnSz*5+5 ,5+30, 30, 30);
UIButton *editStepBtn = [[UIButton alloc]initWithFrame:frame];
[editStepBtn setTitle:@"" forState:UIControlStateNormal];
[editStepBtn addTarget:self action:@selector(editStep:) forControlEvents:(UIControlEvents)UIControlEventTouchUpInside];
[cell.contentView addSubview:editStepBtn];
return cell;
}
-(void)editStep:(id)sender{
[myTable reloadData];
}
@end
执行,你会看到 headers 以升序排列,触摸锤子按钮会使 table 重新加载并且 headers 现在将以降序排列,再次触摸它们回到升序。
但是单元格内容始终保持原样,因为它应该按升序排列。
您正在进行的检查是没有必要的:
if (headerView.contentView.subviews.count!=0){
return headerView;//do not reload
}
但是,每次都实例化一个 UILabel
并不是一个好习惯。
每个实例化都应在 if (headerView == nil)
if-case 内完成,并且 UILabel
应该是此 header 或单元格的 属性。
顺便说一句,你可以在 header/cell.
出队时更改text
属性
UITableViewHeaderFooterView
是可重复使用的视图,因此您无法确定 UITableView
每次都会 return 完全相同的视图。
我建议创建 UITableViewHeaderFooterView
的子类并在某些数据源外部保存状态。
如果您不想这样做,作为解决方法,您可以为每个部分使用唯一的重用标识符。 在这种情况下,您应该替换
static NSString *headerReuseIdentifier = @"myHeader";
与
NSString *headerReuseIdentifier = [NSString stringWithFormat: @"myHeader%ld", (long)section];