如何在UIAlertView中顯示進度條

今天這個問題是,在一個iPhone程序中,我要在後臺做大量的數據處理,希望在界面上顯示一個進度條(Progress Bar)使得用戶瞭解處理進度。這個進度條應該是在一個模態的窗口中,使界

今天這個問題是,在一個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


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];
}
 


 
原文:http://www.gocalf.com/blog/iphone-dev-progressview-in-alertview.html

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章