注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

云水禅心

淡若秋菊何妨瘦, 清到梅花不畏寒.

 
 
 

日志

 
 

iPhone: UIAlterView 显示进度条及UI刷新问题  

2012-06-09 21:28:24|  分类: iphone |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
http://www.gocalf.com/blog/iphone-dev-progressview-in-alertview.html

今天这个问题是,在一个iPhone程序中,我要在后台做大量的数据处理,希望在界面上显示一个进度条(Progress Bar)使得用户了解处理进度。这个进度条应该是在一个模态的窗口中,使界面上其他控件无法被操作。怎么用最简单的方法来实现这个功能?UIAlertView是一个现成的模态窗口,如果能把进度条嵌入到它里面就好了。

以下内容适用于iOS 2.0+。

我们知道,如果要显示一个alert窗口(比如用来显示错误或警告信息、询问用户是否确认某操作等等),只要简单地创建一个UIAlertView对象,再调用其show方法即可。示意代码如下:

1
2
3
4
5
6
7
UIAlertView* alertView = [[[UIAlertView alloc] initWithTitle:@"Title"
                                                     message:@"Message"
                                                    delegate:nil
                                           cancelButtonTitle:@"OK"
                                           otherButtonTitles:nil]
                          autorelease];
[alertView show];

如果要添加一个进度条,只要先创建并设置好一个UIProgressView的实例,再利用addSubbiew方法添加到alertView中即可。

在实际应用中,我可能需要在类中保存进度条的对象实例,以便更新其状态,因此先在自己的ViewController类中添加成员变量:

1
2
3
4
5
6
7
8
9
//  MySampleViewController.h
#import <UIKit/UIKit.h>

@interface MySampleViewController : UIViewController {
@private
    UIProgressView* progressView_;
}

@end

接下来写一个叫做showProgressAlert的方法来创建并显示带有进度条的alert窗口,其中高亮的部分就是把进度条添加到alertView中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void)showProgressAlert:(NSString*)title withMessage:(NSString*)message {
    UIAlertView* alertView = [[[UIAlertView alloc] initWithTitle:title
                                                         message:message
                                                        delegate:nil
                                               cancelButtonTitle:nil
                                               otherButtonTitles:nil]
                              autorelease];

    progressView_ = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleBar];
    progressView_.frame = CGRectMake(308022530);
    [alertView addSubview:progressView_];

    [alertView show];
}

为了让数据处理的子进程能够方便地修改进度条的值,再添加一个简单的方法:

1
2
3
- (void)updateProgress:(NSNumber*)progress {
    progressView_.progress = [progress floatValue];
}

另外,数据处理完毕后,我们还需要让进度条以及alertView消失,由于之前并没有保存alertView的实例,可以通过进度条的superview访问之:

1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)dismissProgressAlert {
    if (progressView_ == nil) {
        return;
    }

    if ([progressView_.superview isKindOfClass:[UIAlertView class]]) {
        UIAlertView* alertView = (UIAlertView*)progressView_.superview;
        [alertView dismissWithClickedButtonIndex:0 animated:NO];
    }

    [progressView_ release];
    progressView_ = nil;
}

假设处理数据的方法叫processData,当然它会在一个单独的线程中运行,下面的片段示意了如何更新进度条状态,以及最后如何让它消失。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void)processData:(int)total {
    for (int i = 0; i < total; ++i) {
        // Update UI to show progess.
        float progress = (float)/ total;
        NSNumber* progressNumber = [NSNumber numberWithFloat:progress];
        [self performSelectorOnMainThread:@selector(updateProgress:)
                               withObject:progressNumber
                            waitUntilDone:NO];

        // Process.
        // do it.
    }

    // Finished.
    [self performSelectorOnMainThread:@selector(dismissProgressAlert)
                           withObject:nil
                        waitUntilDone:YES];
    // Other finalizations.
}

在实际使用中,带进度条的alert view大概长得是这样的:

progress_alert

带进度条的alert窗口


另:

processData需要在另外一个线程中。我目前用到了两种执行的方式:

一种是在单独的线程中处理数据,并通过performSelectorOnMainThread方法通知主线程刷新UI。比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
dispatch_queue_t mediaInputQueue = dispatch_queue_create("mediaInputQueue"NULL);

[instanceOfAVAssetWriterInput requestMediaDataWhenReadyOnQueue:mediaInputQueue usingBlock:^(void) {
  // This block will be called asynchronously by Grand Central Dispatch periodically.
  while ([instanceOfAVAssetWriterInput.readyForMoreMediaData) {
    // processing ...
    // .......
    // Update UI to show progress.
    NSNumber* progressNumber = [NSNumber numberWithFloat:progress];
    [self performSelectorOnMainThread:@selector(updateProgress:)
                           withObject:progressNumber
                        waitUntilDone:NO];
  }  // Ends of while
}  // Ends of ^block

dispatch_release(mediaInputQueue);

另一个方式也差不多,是直接调用一个异步处理方法(比如AVAssetExportSession类的-(void)exportAsynchronouslyWithCompletionHandler:(void (^)(void))handler)。但由于这种异步处理方法是不受用户代码干扰的,所以再开一个NSTimer,当timer触发的时候更新UI:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (void)mothod1 {
  NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:kTimerInterval
                                                    target:self
                                                  selector:@selector(timerFired:)
                                                  userInfo:nil
                                                   repeats:YES];

  [instanceOfAVAssetExportSession_ exportAsynchronouslyWithCompletionHandler:^(void) {
    [self performSelectorOnMainThread:@selector(onSaveFinished)
                           withObject:nil
                        waitUntilDone:YES];
  }];
}

- (void)timerFired:(NSTimer*)theTimer {
  [self updateProgress:[NSNumber numberWithFloat:instanceOfAVAssetExportSession_.progress]];
}





进度条刷新不了的情况

额....我采用了一种方法..不过不知道为什么不行,如果有时间的话,能不能帮我看看...
代码如下
//在 按钮事件里开启 AlertView
 
 -(IBAction) click :(id)sender
{
[self showProgressAlert:@"title" withMessage:@"message"];

 
//在按钮事件 里面触发 processData 方法

 -(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex==1) {// 这个按钮 是提示框的确定按钮
[self processData:1000];
}
}
但是 .. 进度条不动.....


可能的原因

这可能是因为你没有在单独的线程中处理数据。在主线程中,如果你的函数仍在运行,UI基本上不会被更新的。

你可以用NSThread来创建新的线程,并在新的线程中处理数据,比如这样(注意这里的processData跟我上面提供的有一些小的差异,一方面把它的参数改成NSObject的子类,另一方面在其内部添加了autorelease pool):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
- (void)progressButtonClicked:(id)sender {
    [self showProgressAlert:@"Working" withMessage:@"Processing data"];
    [NSThread detachNewThreadSelector:@selector(processData:)
                             toTarget:self
                           withObject:[NSNumber numberWithInt:100]];
}


- (void)processData:(NSNumber*)total {
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    
    int totalData = [total intValue];
    for (int i = 0; i < totalData; ++i) {
        // Update UI to show progess.
        float progress = (float)/ totalData;
        NSNumber* progressNumber = [NSNumber numberWithFloat:progress];
        [self performSelectorOnMainThread:@selector(updateProgress:)
                               withObject:progressNumber
                            waitUntilDone:NO];
        
        // Process.
        [NSThread sleepForTimeInterval:0.1];
    }
    
    // Finished.
    [self performSelectorOnMainThread:@selector(dismissProgressAlert)
                           withObject:nil
                        waitUntilDone:YES];
    // Other finalizations.
    
    [pool release];
    [NSThread exit];
}

  评论这张
 
阅读(1377)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018