滚动 uitableview 时 UIProgressView 被隐藏

Posted

技术标签:

【中文标题】滚动 uitableview 时 UIProgressView 被隐藏【英文标题】:UIProgressView is getting hidden when scrolling the uitableview 【发布时间】:2012-08-10 12:40:18 【问题描述】:

我正在尝试使用 asinetworkqueue 下载 url 列表。它下载很好。但是当我滚动 uitableview 时,我的 uiprogressview 被隐藏了。

我的意思是如果进度显示在第 2 行,如果我滚动它,那么进度视图就会隐藏。然后在完成第 2 行的下载后,它会在第 3 行显示进度视图。

简而言之,当滚动 uitableview 时,活动的 uiprogressview 会被隐藏。

更新

这里有更多的说明。当一行(例如第 1 行)的下载未完成时,现在如果我滚动 tableview 并查看第 1 行,即使尚未完成下载,progressview 也会被隐藏。一旦第 1 行的下载完成,它就会开始对第 2 行进行下载,现在显示进度视图。

 - (void)viewDidLoad
    
        [super viewDidLoad];

        self.view.backgroundColor = [UIColor clearColor];

        self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"bg.jpg"]];

        CGRect tblViewFrame = CGRectMake(7 , 0 , self.view.bounds.size.width - 14, self.view.bounds.size.height - 90);
        self.tblViewDownload = [[[UITableView alloc]initWithFrame:tblViewFrame style:UITableViewStyleGrouped]autorelease];
        self.tblViewDownload.backgroundColor = [UIColor clearColor];
        self.tblViewDownload.showsVerticalScrollIndicator = FALSE;
        self.tblViewDownload.scrollEnabled = YES;
        self.tblViewDownload.delegate = self;
        self.tblViewDownload.dataSource = self;
        [self.view addSubview:self.tblViewDownload];

        index = 0;

        dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^
            [self loadArray];
        ); // Do any additional setup after loading the view.
    



    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

    
        // Return the number of sections.
        return 1;
    


    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

    
        // Return the number of rows in the section.
        return [self.myUrlArray count];
    


    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:
    (NSIndexPath *)indexPath

    
        CGFloat rowHeight = 75.0;

        return rowHeight;  
    

    // Customize the appearance of table view cells.
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
    (NSIndexPath *)indexPath

    
        NSString *CellIdentifier = [NSString stringWithFormat:@"Cell%d", indexPath.row];

        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

        UILabel *lblTitle, *lblAuthor;


        if (cell == nil)
        
            cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero]autorelease];

            cell.selectionStyle = UITableViewCellSelectionStyleNone;

            lblTitle = [[[UILabel alloc]initWithFrame:CGRectMake(65, 10, 220, 25)]autorelease];
            lblTitle.backgroundColor = [UIColor clearColor];
            //lblTitle.font = [UIFont boldSystemFontOfSize:15.0];
            lblTitle.font = [UIFont fontWithName:@"Palatino-Bold" size:15.0];
            lblTitle.text = @"Sample title";
            [cell.contentView addSubview:lblTitle];

            lblAuthor = [[[UILabel alloc]initWithFrame:CGRectMake(65, 30, 220, 25)]autorelease];
            lblAuthor.backgroundColor = [UIColor clearColor];
            lblAuthor.font = [UIFont italicSystemFontOfSize:13.0];
            lblAuthor.textColor = [UIColor grayColor];
            lblAuthor.text = @"sample authior";
            [cell.contentView addSubview:lblAuthor];



        

        return cell;
    


    -(void)loadArray

      NSString *urk = @"http://sample/iphone/video/video_2.mov";
      self.myUrlArray = [[NSMutableArray alloc]init];

      for( int i = 0;i<10;i++)
        [self.myUrlArray addObject:urk];
      


      dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^
        [self downLoadPoems];
      );

    

    -(void)downLoadPoems

        if( index < [myUrlArray count] )
        
            NSLog(@"this is the index %d",index);

        NSIndexPath *myIP = [NSIndexPath indexPathForRow:index inSection:0];

        UITableViewCell *Cell = [self.tblViewDownload cellForRowAtIndexPath:myIP];

        progress = [[UIProgressView alloc]initWithProgressViewStyle:UIProgressViewStyleBar];
        progress.frame = CGRectMake(65, 55, 210, 25);
        progress.progress = 0.0;
        progress.backgroundColor = [UIColor redColor];
        [Cell.contentView addSubview:progress];
        


        if(!self.networkQueue)
            self.networkQueue = [[[ASINetworkQueue alloc] init] autorelease];

        [self.networkQueue cancelAllOperations];
        [self.networkQueue setQueueDidFinishSelector:@selector(queueCompleted:)];
        [self.networkQueue setShowAccurateProgress:YES];
        [self.networkQueue setDownloadProgressDelegate:progress];

        [self.networkQueue setDelegate:self];

        if( index < [myUrlArray count] )
                
            NSString *url = [myUrlArray objectAtIndex:index];

            NSArray *aPoemArrayUrls = [NSArray arrayWithObjects:url,nil];

            for(NSString* urlString in aPoemArrayUrls)
            
                NSURL *url = [NSURL URLWithString:urlString];
                ASIHTTPRequest *downloadAFileRequest = [[ASIHTTPRequest requestWithURL:url]retain];
                NSString *Filename = [urlString lastPathComponent];
                NSLog(@"%@ filename",Filename);
                [downloadAFileRequest setUserInfo:[NSDictionary dictionaryWithObject:@"request1" forKey:@"name"]];
                [downloadAFileRequest setDownloadDestinationPath:[[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:Filename]];
                [downloadAFileRequest setShouldContinueWhenAppEntersBackground:YES];
                [downloadAFileRequest setDelegate:self];
                [downloadAFileRequest setDidFinishSelector:@selector(requestDone:)];
                [downloadAFileRequest setDidFailSelector:@selector(requestWentWrong:)];
                [downloadAFileRequest setShowAccurateProgress:YES];

                [self.networkQueue addOperation:downloadAFileRequest];
                //----
            

            [self.networkQueue go];


        





    

    -(void)queueCompleted:(ASINetworkQueue*)queue

        NSLog(@"queueCompleted");

       self.tblViewDownload.userInteractionEnabled = YES;

        UIProgressView *progressv = (UIProgressView*)queue.downloadProgressDelegate;

        if (progressv)
            [progressv removeFromSuperview];


        if ( index < [myUrlArray count] )

            index++;

            [self downLoadPoems];
        

    


    - (void)requestDone:(ASIHTTPRequest *)request
    
        NSLog(@"request done");
        NSString *response = [request responseString];
    

    - (void)requestWentWrong:(ASIHTTPRequest *)request
    
        NSLog(@"request went wrong");

        NSError *error = [request error];
    

    - (void)viewDidUnload
    
        [super viewDidUnload];
        // Release any retained subviews of the main view.
    

    - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
    
        return (interfaceOrientation == UIInterfaceOrientationPortrait);
    

【问题讨论】:

您需要澄清这个问题 - 我(很可能还有其他人)不理解这个问题。你有一个表格,第 0 行有一些东西。第 1 行有一个进度条,并显示“下载 xxx 的进度”,直到完成,然后它切换到显示下一个项目的进度。您的表中有其他项目,但它们与前两项无关。当用户滚动表格以查看其他项目时,进度条被隐藏。您希望进度条神奇地保持可见,即使它是表格中的一个单元格,并且您让用户滚动表格。你能看出我的困惑吗? @DavidH:首先感谢您花时间回复。我的问题是当开始下载第 1 行时,显示进度视图。这很好。问题是什么时候我在第 1 行的下载尚未完成时滚动 uitableview,现在当我再次看到第 1 行时,即使它没有完成下载,它也不可见。我在第 1 行中看不到任何进度视图。完成第 1 行的下载后没有 1,它将显示第 2 行的进度视图。这是问题,请帮助 【参考方案1】:

每次 tableView 要求您提供一个单元格时,您都在上面创建一个新单元格 - 您应该回收单元格是不正确的(Apple 文档中的此处和其他地方有大量示例说明如何执行此操作。)

现在,更新单元格中的进度条有一些您无法处理的复杂性。首先,progressBar 的对象会随着表格的滚动而改变——它会从你的下方改变。假设您有一个部分可以简化步骤(但这些部分也适用于多个部分):

所以你应该只通过执行以下操作来更新它:

您的下载想要更新进度。它必须确定具有该进度条的表格行(如果您将进度条添加到单元格,则在该视图上设置标签以便以后更容易找到它)

然后上面的代码向 UITableView 询问可见单元格列表,并确定它需要更新的单元格是否可见 - 如果不可见,则不执行任何操作。

假设单元格是可见的,它会询问单元格的进度条(即 [cell.contentView viewWithTag:...]),然后更新当前值(此进度条之前可能已显示其他文件由于回收利用)。

在您的 cellForRowAtIndexPath: 中,您尝试回收一个单元格,如果失败,您将创建一个新单元格。在该代码之后,您不能对任何视图做出任何假设 - 您需要更新 progressBar 最大值,将当前值设置为下载的当前值,更改任何标签文本等。

【讨论】:

【参考方案2】:

几个观察:

    您在ASINetworkQueue 中设置setDownloadProgressDelegate,但如果您想跟踪各个下载的进度,您可能希望在ASIHTTPRequest 中进行设置。这样,您理论上可以相应地更新多个下载。

    我这么说的原因之一是您当前正在创建 networkQueue 添加一个 ASIHTTPRequest 并开始它。理论上,你可以将一大堆ASIHTTPRequest 排成队列,然后将它们踢掉,每个都更新自己的UIProgressView

    最大的问题是你的cellForRowAtIndexPath

    首先,您不应该真正为每个单元格使用唯一的单元格标识符。你失去了所有的内存优化;

    1234563然后创建表格视图和您需要的任何控件;

    不过,无论dequeueReusableCellWithIdentifier 是否返回nil,您都应该设置标签并更新进度条;和

    就个人而言,使用进度条,如果进度条已经存在,我发现它更容易删除,然后再次添加,无论dequeueReusableCellWithIdentifier 是否成功。

    李>

    您可能想要跟踪一组“行”对象,例如,这些对象具有正在下载的内容的 URL,但更重要的是,保留了一个 UIProgressView 对象(因为downloadProgressDelegateassign 属性,不会为您保留。因此,您需要跟踪当前正在进行的所有请求的进度条(无论单元格是否可见、已出列或不),不只是一个单一的 UIProgressView。

    顺便说一句,您似乎正在将内容分派到后台队列。你真的不需要这样做,因为ASINetworkQueue 为你做了所有这些事情。您可能想要这样做可能有技术原因,但我认为没有必要,因为我认为这些东西已经异步工作。如果您真的想在另一个队列中启动它,那很好,但请记住将您的 UI 更新分派回主队列,因为您永远不应该从后台进行 UI 更新。

仅供参考,在下面的代码示例中,我使用了我经常用于表格视图的代码模式,特别是我的表格视图有一个模型,该模型由 Row 对象的 NSArray 组成,它跟踪我需要做什么表格视图的每个单元格。

@interface Row : NSObject

@property (nonatomic, strong) NSString *author;
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) UIProgressView *progress;
@property (nonatomic, strong) NSString *urlString;

@end

我会像下面这样使用它:

@interface SampleViewController ()

@property (nonatomic, strong) NSArray *rows;
@property (nonatomic, strong) ASINetworkQueue *networkQueue;

@end

@implementation SampleViewController

- (void)viewDidLoad

    [super viewDidLoad];

    self.rows = [self loadImageUrls];

    [self downloadStuff];


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

    // Return the number of sections.
    return 1;


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

    // Return the number of rows in the section.
    return [self.rows count];


- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

    return 75.0;


#define kTitleTag 1
#define kAuthorTag 2
#define kProgressViewTag 3

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

    NSString *CellIdentifier = @"asi";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    UILabel *lblTitle, *lblAuthor;

    // if you could use storyboards, a lot of this silliness goes away

    if (cell == nil)
    
        // but in the world of nibs, you have to create the custom controls when you create the cell

        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];

        cell.selectionStyle = UITableViewCellSelectionStyleNone;

        lblTitle = [[UILabel alloc]initWithFrame:CGRectMake(65, 10, 220, 25)];
        lblTitle.backgroundColor = [UIColor clearColor];
        lblTitle.font = [UIFont boldSystemFontOfSize:15.0];
        //lblTitle.font = [UIFont fontWithName:@"Palatino-Bold" size:15.0];
        lblTitle.tag  = kTitleTag;
        [cell.contentView addSubview:lblTitle];

        lblAuthor = [[UILabel alloc]initWithFrame:CGRectMake(65, 30, 220, 25)];
        lblAuthor.backgroundColor = [UIColor clearColor];
        lblAuthor.font = [UIFont italicSystemFontOfSize:13.0];
        lblAuthor.textColor = [UIColor grayColor];
        lblAuthor.tag = kAuthorTag;
        [cell.contentView addSubview:lblAuthor];
    
    else
    
        // if you're recycling a cell, then link up with it's controls

        cell.textLabel.backgroundColor = [UIColor clearColor];
        cell.detailTextLabel.backgroundColor = [UIColor clearColor];

        lblTitle = (UILabel *)[cell.contentView viewWithTag:kTitleTag];
        lblAuthor = (UILabel *)[cell.contentView viewWithTag:kAuthorTag];

        UIProgressView *oldProgressView = (UIProgressView *)[cell.contentView viewWithTag:kProgressViewTag];
        if (oldProgressView && [oldProgressView isKindOfClass:[UIProgressView class]])
        
            [oldProgressView removeFromSuperview];
        
    

    Row *row = [self.rows objectAtIndex:indexPath.row];

    lblTitle.text = row.title;
    lblAuthor.text = row.author;

    if (row.progress)
    
        row.progress.frame = CGRectMake(0, 55, 200, 25);
        [cell.contentView addSubview:row.progress];
    

    return cell;


-(void)downloadStuff
    
    ASINetworkQueue *networkQueue = [[ASINetworkQueue alloc] init];

    [networkQueue setQueueDidFinishSelector:@selector(queueCompleted:)];
    [networkQueue setDelegate:self];

    NSFileManager *fileManager = [NSFileManager defaultManager];
    [fileManager createDirectoryAtPath:[[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:@"ASIHTTP"]
           withIntermediateDirectories:NO
                            attributes:nil
                                 error:nil];

    for (NSUInteger i = 0; i < [self.rows count]; i++)
    
        Row *row = [self.rows objectAtIndex:i];

        row.progress = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleBar];
        row.progress.tag = kProgressViewTag;

        NSIndexPath *myIP = [NSIndexPath indexPathForRow:i inSection:0];
        UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:myIP];
        if (cell)
        
            // if the cell is visible, add the progress view to it

            row.progress.frame = CGRectMake(0, 55, 200, 25);
            [cell.contentView addSubview:row.progress];
        

        NSString *urlString = row.urlString;

        ASIHTTPRequest *request;
        request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:urlString]];
        NSString *filename = [urlString lastPathComponent];
        [request setUserInfo:[NSDictionary dictionaryWithObject:@"request1" forKey:@"name"]];
        [request setDownloadDestinationPath:[[[[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:@"ASIHTTP"] stringByAppendingPathComponent:filename] stringByAppendingFormat:@"%1d", i]];
        [request setShouldContinueWhenAppEntersBackground:YES];
        [request setDelegate:self];
        [request setDidFinishSelector:@selector(requestDone:)];
        [request setDidFailSelector:@selector(requestWentWrong:)];
        [request setDownloadProgressDelegate:row.progress];
        [request setShowAccurateProgress:YES];

        [networkQueue addOperation:request];
    

    [networkQueue go];


-(void)queueCompleted:(ASINetworkQueue*)queue
    
    NSLog(@"%s queueCompleted", __FUNCTION__);


- (void)requestDone:(ASIHTTPRequest *)request

    NSLog(@"%s request done: %@", __FUNCTION__, [request responseString]);

    [request.downloadProgressDelegate removeFromSuperview];

    // find which row it's on and remove it from that one

    for (Row *row in self.rows)
    
        if (row.progress == request.downloadProgressDelegate)
            row.progress = nil;
    

    // and let it go (obviously, non-ARC, release)

    request.downloadProgressDelegate = nil;


- (void)requestWentWrong:(ASIHTTPRequest *)request

    NSLog(@"%s request went wrong err = %@", __FUNCTION__, [request error]);


- (NSArray *)loadImageUrls

    NSMutableArray *results = [[NSMutableArray alloc] init];

    for (NSInteger i = 0; i < 100; i++)
    
        Row *row = [[Row alloc] init];
        row.urlString = @"http://host/test/bigfile";
        row.title = [NSString stringWithFormat:@"Title %1d", i];
        row.author = [NSString stringWithFormat:@"Author %1d", i];

        [results addObject:row];
    

    return results;

显然,您必须根据自己的目的对其进行自定义(例如,我为自己的目的进行了调整;我的生命太短了,无法浪费时间编写非 ARC 代码;等等),但它向您展示了我个人的方式解决了我认为您提供的代码示例的主要缺陷。我认为这需要相当大的扩展(如果我们离开,请优雅地取消ASINetworkQueue,也许只加载可见单元格的ASIHTTPRequests,等等),但我会把它留给你。

【讨论】:

【参考方案3】:

使用这个方法

  - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
    (NSIndexPath *)indexPath


    NSString *CellIdentifier = [NSString stringWithFormat:@"Cell%d", indexPath.row];

    UITableViewCell *cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero]autorelease];

    UILabel *lblTitle, *lblAuthor;





        cell.selectionStyle = UITableViewCellSelectionStyleNone;

        lblTitle = [[[UILabel alloc]initWithFrame:CGRectMake(65, 10, 220, 25)]autorelease];
        lblTitle.backgroundColor = [UIColor clearColor];
        //lblTitle.font = [UIFont boldSystemFontOfSize:15.0];
        lblTitle.font = [UIFont fontWithName:@"Palatino-Bold" size:15.0];
        lblTitle.text = @"Sample title";
        [cell.contentView addSubview:lblTitle];

        lblAuthor = [[[UILabel alloc]initWithFrame:CGRectMake(65, 30, 220, 25)]autorelease];
        lblAuthor.backgroundColor = [UIColor clearColor];
        lblAuthor.font = [UIFont italicSystemFontOfSize:13.0];
        lblAuthor.textColor = [UIColor grayColor];
        lblAuthor.text = @"sample authior";
        [cell.contentView addSubview:lblAuthor];





    return cell;

【讨论】:

你使用了 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];当您向下滚动时,它在 tableview 中插入了一个旧行。此方法将在每个索引处创建新行

以上是关于滚动 uitableview 时 UIProgressView 被隐藏的主要内容,如果未能解决你的问题,请参考以下文章

当 scrollViewDidEndDragging 时 UITableView 滚动到特定点

UITableView 滚动 UINavigationBar

滚动 UITableView 时如何创建 NSLayoutConstraint

滚动时 UITableView 中的数据重复

UIScrollView 中的 UIView 中的 UITableView。滚动表格视图时也不要滚动滚动视图

当文本字段开始编辑时停止 UITableView 滚动