前のレッスンでは、メインスレッドで実行される基本的な同期を追加しました。メインスレッドをこのようにブロックすることはおすすめしません。このレッスンでは、同期をバックグラウンドスレッドに移動し、同期監視メソッドを追加して、進行状況の表示を更新します。
同期の進行状況を表示するには、下部のナビゲーションツールバーに配置されるラベル付きの進行状況バーを使用します。このバーは、新しいメッセージをダウンロードするときの [メール] アプリケーションの表示に似ています。このバーの外観を再作成するには、カスタムビューを作成してください。
[グループとファイル] ウィンドウ枠の [クラス] フォルダーを [control] キーを押したままクリックします。[追加] » [新規ファイル] をクリックします。
[Cocoa タッチクラス] で [UIViewController subclass] をクリックします。
[UITableViewController subclass] をオフにします。
[With XIB for user interface] をクリックします。
[次へ] をクリックします。
ファイルに ProgressToolbarViewController.m という名前を付け、Classes サブフォルダーに配置します。チェックボックスをクリックして、ヘッダーファイルが作成されるようにします。
[完了] をクリックします。
ProgressToolbarViewController.xib ファイルを Resources フォルダーに移動します。
[Interface Builder] でレイアウトを作成する前に、ProgressToolbarViewController のインターフェイス定義を次のコードで置き換えます。
@interface ProgressToolbarViewController : UIViewController { IBOutlet UILabel *label; IBOutlet UIProgressView *progressBar; } @end |
インターフェイスにプロパティを追加します。
@property (readonly) IBOutlet UILabel *label; @property (readonly) IBOutlet UIProgressView *progressBar; |
実装ファイルのプロパティを同期します。
@synthesize label; @synthesize progressBar; |
dealloc メソッドに解放呼び出しを追加します。
- (void)dealloc { [super dealloc]; [label release]; [progressBar release]; } |
Xcode の Resources フォルダーで ProgressToolbarViewController.xib をダブルクリックして、ProgressToolbarViewController を開きます。このビューはツールバーに表示されるため、サイズを適切に調整し、背景プロパティを設定します。
[Document] ウィンドウ (Command-0) で [View] をクリックします。
[Attribute Inspector] (Command-1) で、シミュレートされたステータスバーを [Unspecified] に設定します。
[Background] をクリックし、[Background opacity] を 0 % に設定します。
[Opaque] 設定をオフにします。
[Size Inspector] (Command-3) で、幅を 232 に高さを 44 に設定します。
[Library] で UIProgressView をクリックして、[View] にドラッグします。
[Size Inspector] (Command-3) で、進行状況ビューの位置を 26、29 に設定します。
進行状況ビューの幅を 186 に設定します。
[Attributes Inspector] (Command-1) で、スタイルを [Bar] に、進行状況を 0 に設定します。
[Library] で UILabel をクリックして、[View] にドラッグします。
[Size Inspector] (Command-3) で、ラベルの位置を 14、5 に設定します。
ラベルのサイズを 210、16 に設定します。
[Attribute Inspector] (Command-1) で、テキストを [同期進行状況] に設定します。
レイアウトアラインメントを中央に設定します。
フォントをヘルベチカボールド、サイズ 12 に設定します。
テキストの色を白に設定します。
影の色を RGB (103、114、130) に、不透明度 100 % に設定します。
[Document] ウィンドウで [File's Owner] をクリックします。
[Connectors Inspector] (Command-2) で、ラベルアウトレットを前の手順で作成した UILabel にリンクします。
進行状況バーアウトレットを前の手順で作成した UIProgressView にリンクします。
XIB ファイルを保存し、[Interface Builder] を閉じます。
この ProgressBar ビューは、RootViewController のツールバーに追加されます。ただし、DataAccess オブジェクトも、同期の進行状況を表示するためにこのビューへの参照を使用します。次のインスタンス変数をインターフェイスに追加します。
ProgressToolbarViewController * progressToolbar; |
また、プロパティを DataAccess クラスに追加します。
@property (retain, readwrite) IBOutlet ProgressToolbarViewController * progressToolbar; |
DataAccess ヘッダーに ProgressToolbarViewController ヘッダーをインポートします。
#import "ProgressToolbarViewController.h" |
実装ファイルのプロパティを同期します。
@synthesize progressToolbar |
進行状況ビューをツールバーに追加するには、次のコードを RootViewController の viewDidLoad メソッドに追加します。
// Create progress display ProgressToolbarViewController * progress = [[ProgressToolbarViewController alloc] initWithNibName:@"ProgressToolbarViewController" bundle:nil]; // Register the toolbar with the DataAccess [[DataAccess sharedInstance] setProgressToolbar:progress]; // Setup UIBarButtonItems UIBarButtonItem * space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; UIBarButtonItem * progressButtonItem = [[UIBarButtonItem alloc] initWithCustomView:progress.view]; // Put them in the toolbar self.toolbarItems = [NSArray arrayWithObjects:space, progressButtonItem, space, nil]; [space release]; [progressButtonItem release]; |
RootViewController のツールバーに進行状況ビューが追加されましたが、ツールバーが非表示になっています。次の項では、同期をバックグラウンドスレッドに移動し、同期中に進行状況ツールバーを表示します。
現在のところ、アプリケーションのすべての処理は、アプリケーションのメインスレッドで実行されます。メインスレッドは、インターフェイスを描画し、タッチなどのユーザーイベントを処理するスレッドでもあるため、ブロックすることはおすすめしません。
同期中のアプリケーションのフローは次のとおりです。
ユーザーが RootViewController で同期ボタンをクリックします。ツールバーの進行状況ビューが表示されます。
RootViewController は別のスレッドで同期を開始し、それ自体を引数として同期機能に渡します。メインスレッドは引き続き処理を行い、同期は別のスレッドで実行されます。
同期機能は、performSelectorOnMainThread を使用してユーザーインターフェイスを更新し、同期中に進行状況の表示を更新します。
同期が完了すると、バックグラウンドスレッドによって同期の結果を示す警告ボックスが表示されます。このとき、警告が破棄された場合に認識できるように、RootViewController が警告に対するデリゲートとして設定されています。
警告ボックスが破棄されたことが RootViewController に通知されると、ツールバーの進行状況ビューは非表示になります。
RootViewController では、警告ビューの破棄を処理するために UIAlertViewDelegate プロトコルを実装する必要があるため、ヘッダーファイルを次のように変更します。
@interface RootViewController : UITableViewController <UIAlertViewDelegate> { } // Displays the screen to add a name. - (void)showAddNameScreen; @end |
RootViewController が実装する必要がある UIAlertViewDelegate プロトコルのメソッドは、 alertView:clickedButtonAtIndex: メソッドのみです。このメソッドは、ユーザーが UIAlertView のボタンを押すと呼び出されます。このビューのボタンはボタンが 1 つのみであるため、どのボタンが押されたかを検査する必要はありません。RootViewController.m ファイルに次のコードを記述します。
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { NSLog(@"User dismissed sync alert, refreshing table."); [self.tableView reloadData]; [self.navigationController setToolbarHidden:YES animated: YES]; } |
また、RootViewController.m で sync メソッドを変更し、detachNewThreadSelector を使用することによって、同期呼び出しを別のスレッドで実行するようにします。
- (void)sync { [self.navigationController setToolbarHidden:NO animated: YES]; [NSThread detachNewThreadSelector:@selector(synchronize:) toTarget:[DataAccess sharedInstance] withObject:self]; } |
同期が別のスレッドで実行されるようになったため、いくつかの変更を加える必要があります。RootViewController の sync メソッドで示されているとおり、DataAccess の synchronize メソッドにはパラメーターが渡されます。シグネチャーを次のように変更します。
- (void)synchronize:(id<UIAlertViewDelegate>)sender; |
DataAccess.mm 実装ファイルを次のように更新します。
// Since the synchronization method uses auto-released object instances, you also need // to create an NSAutoreleasePool and release it at the end of the synchronization: - (void)synchronize:(id<UIAlertViewDelegate>)sender { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; // Rest of synchronize method... [pool release]; } |
同じく synchronize メソッドで、渡される RootViewController を警告のデリゲートとして設定する必要があります。このことを行うために、前に作成した UIAlertView を更新し、デリゲートを nil から sender に変更します。
// Set RootViewController as the alert delegate if (sender != nil) { NSLog(@"Showing Alert with sync result."); [[[[UIAlertView alloc] initWithTitle:@"Synchronization" message:result delegate:sender // changed from nil to sender cancelButtonTitle:nil otherButtonTitles:@"OK", nil] autorelease] show]; } else { NSLog(@"Not showing alert since sender was nil."); } |
次に、進捗度に対応して進行状況バーを更新し、適切なメッセージを表示する同期コールバックを作成する必要があります。
ただし、同期コールバックはメインではないスレッド (RootViewController によって作成された同期スレッド) で実行されるため、performSelectorOnMainThread を使用して、ユーザーインターフェイスを更新する必要があります。このメソッドに渡すことができるパラメーターは 1 つのみであるため、進行状況とメッセージを渡すには、float と NSString の 2 つのプロパティを持つ UpdateInfo という名前の Objective-C クラスをヘッダーファイルに作成します。
@interface UpdateInfo : NSObject { NSString * message; float progress; } // Message to display in the progress view @property (readonly) NSString * message; // Progress of the sync [0.0-1.0] @property (readonly) float progress; // Preferred initializer - (id)initWithMessage:(NSString*) message andProgress:(float)progress; @end |
次のコードを実装ファイルに追加します。
// The implementation is a single constructor that takes both parameters: @implementation UpdateInfo @synthesize message; @synthesize progress; - (id)initWithMessage:(NSString*) msg andProgress:(float) syncProgress { if (self = [super init]) { message = msg; progress = syncProgress; } return self; } @end |
これで、ユーザーインターフェイスの更新メソッドに必要なすべての情報をバンドルできるようになりました。次に、DataAccess クラスにそれを定義します。
- (void)updateSyncProgress:(UpdateInfo *)info { progressToolbar.label.text = info.message; progressToolbar.progressBar.progress = info.progress; } |
UpdateInfo.h を DataAccess.h にインポートします。
#import "UpdateInfo.h" |
また、実装ファイル DataAccess.mm には、その他の静的メソッドとともに、コールバックメソッドも定義できます。
static void UL_CALLBACK_FN progressCallback(ul_synch_status * status) { // Sync information for the GUI float percentDone = 0.0; NSString * message; // Note: percentDone is approximate. switch (status->state) { case UL_SYNCH_STATE_STARTING: percentDone = 0; message = @"Starting Sync"; break; case UL_SYNCH_STATE_CONNECTING: percentDone = 5; message = @"Connecting to Server"; break; case UL_SYNCH_STATE_SENDING_HEADER: percentDone = 10; message = @"Sending Sync Header"; break; case UL_SYNCH_STATE_SENDING_TABLE: percentDone = 10 + 25 * (status->sync_table_index / status->sync_table_count); message = [NSString stringWithFormat:@"Sending Table %s: %d of %d", status->table_name, status->sync_table_index, status->sync_table_count]; break; case UL_SYNCH_STATE_SENDING_DATA: percentDone = 10 + 25 * (status->sync_table_index / status->sync_table_count); message = @"Sending Name Changes"; break; case UL_SYNCH_STATE_FINISHING_UPLOAD: case UL_SYNCH_STATE_RECEIVING_UPLOAD_ACK: percentDone = 50; message = @"Finishing Upload"; break; case UL_SYNCH_STATE_RECEIVING_TABLE: case UL_SYNCH_STATE_RECEIVING_DATA: percentDone = 50 + 25 * (status->sync_table_index / status->sync_table_count); message = [NSString stringWithFormat:@"Receiving Table %s: %d of %d", status->table_name, status->sync_table_index, status->sync_table_count]; break; case UL_SYNCH_STATE_COMMITTING_DOWNLOAD: case UL_SYNCH_STATE_SENDING_DOWNLOAD_ACK: percentDone = 80; message = @"Committing Downloaded Updates"; break; case UL_SYNCH_STATE_DISCONNECTING: percentDone = 90; message = @"Disconnecting from Server"; break; case UL_SYNCH_STATE_DONE: percentDone = 100; message = @"Finished Sync"; break; case UL_SYNCH_STATE_ERROR: percentDone = 95; message = @"Error During Sync"; break; case UL_SYNCH_STATE_ROLLING_BACK_DOWNLOAD: percentDone = 100; message = @"Rolling Back due to Error"; break; default: percentDone = 100; NSLog(@"Unknown sync state: '%d'", status->state); break; } // Wrap the GUI info in an object and have the main thread update UpdateInfo * info = [[[UpdateInfo alloc] initWithMessage:message andProgress:percentDone / 100] autorelease]; [[DataAccess sharedInstance] performSelectorOnMainThread:@selector(updateSyncProgress:) withObject:info waitUntilDone:YES]; } |
すべての準備が完了したため、DataAccess.mm の synchronize メソッドのコールバックメソッドに、同期 observer を設定できます。
// Set the sync parameters info.user_name = (char*)"user"; // Set to your username info.password = (char*)"password"; // Set to your password info.version = (char*)"NamesModel"; info.stream = "tcpip"; info.stream_parms = (char*)"host=localhost"; info.observer = progressCallback; // Add this line |
![]() |
DocCommentXchange で意見交換できます
|
Copyright © 2012, iAnywhere Solutions, Inc. - SQL Anywhere 12.0.1 |