In der vorherigen Lektion haben Sie eine einfache Synchronisation hinzugefügt, die auf dem Haupt-Thread ausgeführt wurde. Das Blockieren des Haupt-Threads auf diese Weise wird nicht empfohlen. In dieser Lektion verschieben Sie die Synchronisation in einen Hintergrund-Thread und fügen eine Beobachtungsmethode für die Synchronisation hinzu, um eine Fortschrittsanzeige zu aktualisieren.
Um den Verarbeitungsfortschritt der Synchronisation anzuzeigen, verwenden Sie eine beschriftete Fortschrittsleiste, die in die untere Navigationsleiste gesetzt wird, ähnlich der Mail-Anwendung, wenn eine neue Nachricht heruntergeladen wird. Um dieses Display nachzuahmen, müssen Sie eine benutzerdefinierte Ansicht erstellen:
Ctrl-klicken Sie auf den Ordner Classes im Abschnitt Groups & Files. Klicken Sie im Menü Add auf New File.
Klicken Sie unter Cocoa Touch Class auf UIViewController subclass.
Deaktivieren Sie UITableViewController subclass.
Klicken Sie auf With XIB for user interface.
Klicken Sie auf Weiter.
Benennen Sie die Datei ProgressToolbarViewController.m und speichern Sie sie im Classes-Unterverzeichnis. Klicken Sie auf das Feld, damit eine Header-Datei erstellt wird.
Klicken Sie auf Fertig stellen.
Verschieben Sie die ProgressToolbarViewController.xib-Datei in das Resources-Verzeichnis.
Bevor Sie das Layout im Interface Builder erstellen, ersetzen Sie die Schnittstellendefinition durch Folgendes in ProgressToolbarViewController:
@interface ProgressToolbarViewController : UIViewController { IBOutlet UILabel *label; IBOutlet UIProgressView *progressBar; } @end |
Fügen Sie die Eigenschaften in der Schnittstelle hinzu:
@property (readonly) IBOutlet UILabel *label; @property (readonly) IBOutlet UIProgressView *progressBar; |
Synthetisieren Sie die Eigenschaften in der Implementierungsdatei:
@synthesize label; @synthesize progressBar; |
Fügen Sie einen Freigabeaufruf in der dealloc-Methode hinzu:
- (void)dealloc { [super dealloc]; [label release]; [progressBar release]; } |
Öffnen Sie ProgressToolbarViewController durch Doppelklicken auf ProgressToolbarViewController.xib im Xcode Resources-Ordner. Da diese Ansicht in einer Symbolleiste angezeigt wird, müssen Sie sie entsprechend dimensionieren und ihre Hintergrundeigenschaften festlegen:
Klicken Sie im Fenster Document (Befehl-0) auf View.
Setzen Sie im Attributes Inspector (Befehl-1) die simulierte Statusleiste auf Unspecified.
Klicken Sie auf Background und setzen Sie die Option Background opacity auf 0 %.
Deaktivieren Sie die Opaque-Einstellung.
Setzen Sie im Size Inspector (Befehl-3) die Breite auf 232 und die Höhe auf 44.
Klicken und ziehen Sie UIProgressView aus der Library in die View.
Setzen Sie im Size Inspector (Befehl-3) die Position der Fortschrittsanzeige auf 26, 29.
Legen Sie die Breite der Fortschrittsanzeige mit 186 fest.
Setzen Sie im Attributes Inspector (Befehl-1) den Stil auf Bar und den Fortschritt auf 0.
Klicken und ziehen Sie UILabel aus der Library in die View.
Setzen Sie im Size Inspector (Befehl-3) die Position der Beschriftung auf 14, 5.
Legen Sie die Größe der Beschriftung mit 210, 16 fest.
Legen Sie im Attribute Inspector (Befehl-1) den Text mit Sync Progress fest.
Legen Sie die Layoutausrichtung als zentriert fest.
Legen Sie die Schriftart mit Helvetica fett, Größe 12 fest.
Legen Sie die Textfarbe mit weiß fest.
Legen Sie die Schattenfarbe mit RGB: (103, 114, 130) und 100 % Opazität fest.
Klicken Sie im Fenster Document auf File's Owner.
Verknüpfen Sie im Connectors Inspector (Befehl-2) den Beschriftungs-Outlet mit dem in den letzten Schritten erstellen UILabel-Objekt.
Verknüpfen Sie den Fortschrittsbalken-Outlet mit dem in den letzten Schritten erstellten UIProgressView-Objekt.
Speichern Sie die XIB-Datei und schließen Sie den Interface Builder.
Die ProgressBar-Ansicht wird der Symbolleiste von RootViewController hinzugefügt. Das DataAccess-Objekt benutzt jedoch auch einen Verweis darauf, um den Fortschritt der Synchronisation anzuzeigen. Fügen Sie die folgende Instanzvariable der Schnittstelle hinzu:
ProgressToolbarViewController * progressToolbar; |
Fügen Sie die Eigenschaft der DataAccess-Klasse hinzu:
@property (retain, readwrite) IBOutlet ProgressToolbarViewController * progressToolbar; |
Importieren Sie den ProgressToolbarViewController-Header in den DataAccess-Header:
#import "ProgressToolbarViewController.h" |
Synthetisieren Sie die Eigenschaft in der Implementierung.
@synthesize progressToolbar |
Um die Fortschrittsanzeige zur Symbolleiste hinzuzufügen, fügen Sie folgenden Code in RootViewController der viewDidLoad-Methode hinzu:
// 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 verfügt nun über eine Fortschrittsanzeige in seiner Symbolleiste, auch wenn die Symbolleiste verborgen ist. Im nächsten Abschnitt werden Sie die Synchronisation in einen Hintergrund-Thread verschieben und den Fortschrittsbalken während der Synchronisation anzeigen.
Bisher wird von der Anwendung alles auf dem Haupt-Thread der Anwendung ausgeführt. Da dies auch der Thread ist, der für das Zeichnen der Schnittstelle und die Verarbeitung von Benutzerereignissen wie Tippen mit dem Finger verwendet wird, ist die Blockierung nicht zu empfehlen.
Der Verarbeitungsfluss der Anwendung während der Synchronisation sieht wie folgt aus:
Der Benutzer klickt auf die Synchronisationsschaltfläche in RootViewController. Die Fortschrittsanzeige erscheint in der Symbolleiste.
RootViewController initiiert die Synchronisation auf einem getrennten Thread und übergibt sich als Argument an die Synchronisationsfunktion. Der Haupt-Thread fährt fort, während die Synchronisation in einem anderen Thread läuft.
Die Synchronisationsfunktion aktualisiert die Benutzeroberfläche mit performSelectorOnMainThread, um die Fortschrittsanzeige während der Synchronisation zu aktualisieren.
Wenn die Synchronisation abgeschlossen ist, zeigt der Hintergrund-Thread ein Warnfeld mit dem Ergebnis der Synchronisation, wobei RootViewController als der Delegat des Alarms eingestellt wird, damit er weiß, wann die Meldung quittiert wird.
Sobald RootViewController erfährt, dass die Warnmeldung quittiert wurde, wird die Fortschrittsanzeige verborgen.
Da RootViewController das UIAlertViewDelegate-Protokoll implementieren muss, um das Quittieren der Warnmeldung zu verarbeiten, ändern Sie die Header-Datei wie folgt:
@interface RootViewController : UITableViewController <UIAlertViewDelegate> { } // Displays the screen to add a name. - (void)showAddNameScreen; @end |
Die einzige Methode aus dem UIAlertViewDelegate-Protokoll, die RootViewController implementieren muss, ist die alertView:clickedButtonAtIndex:-Methode. Diese Methode wird aufgerufen, wenn der Benutzer auf eine Schaltfläche in UIAlertView tippt. Da die Ansicht nur eine einzige Schaltfläche hat, brauchen Sie nicht zu definieren, welche Schaltfläche angetippt wurde. Setzen Sie den folgenden Code in die RootViewController.m-Datei ein.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { NSLog(@"User dismissed sync alert, refreshing table."); [self.tableView reloadData]; [self.navigationController setToolbarHidden:YES animated: YES]; } |
Ändern Sie die Synchronisationsmethode auch in RootViewController.m, sodass der Synchronisationsaufruf auf einem eigenen Thread erfolgt, mit detachNewThreadSelector:
- (void)sync { [self.navigationController setToolbarHidden:NO animated: YES]; [NSThread detachNewThreadSelector:@selector(synchronize:) toTarget:[DataAccess sharedInstance] withObject:self]; } |
Da die Synchronisation jetzt auf einem getrennten Thread abläuft, müssen Sie einige Einstellungen ändern. Wie Sie in der Synchronisationsmethode von RootViewController vielleicht festgestellt haben, wird der Synchronisationsmethode von DataAccess ein Parameter übergeben. Ändern Sie seine Signatur wie folgt:
- (void)synchronize:(id<UIAlertViewDelegate>)sender; |
Aktualisieren Sie die DataAccess.mm-Implementierungsdatei mit Folgendem:
// 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]; } |
In der Synchronisationsmethode müssen Sie auch das übergebene RootViewController als Alarmdelegaten festlegen. Dazu aktualisieren Sie das vorher erstellte UIAlertView-Objekt und ändern den Delegat von nil auf 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."); } |
Sie müssen jetzt den Synchronisations-Callback erstellen, der den Fortschrittsbalken aktualisiert, damit der jeweilige Status und die richtige Meldung angezeigt wird.
Da der Synchronisations-Callback aber auf einem Nicht-Haupt-Thread (dem Synchronisations-Thread, der von RootViewController erstellt wurde) abläuft, müssen Sie performSelectorOnMainThread verwenden, um die Benutzeroberfläche zu aktualisieren. Diese Methode erlaubt nur die Übergabe eines einzelnen Parameters. Um daher sowohl den Fortschritt als auch die Meldung zu übergeben, erstellen Sie eine Objective-C-Klasse mit dem Namen UpdateInfo mit zwei Eigenschaften, einem float-Datentyp und NSString in der Header-Datei:
@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 |
Fügen Sie den folgenden Inhalt in die Implementierungsdatei ein:
// 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 |
Da sie jetzt alle Informationen bündeln können, die von der Aktualisierungsmethode für die Benutzeroberfläche benötigt wird, können Sie sie in der DataAccess-Klasse definieren:
- (void)updateSyncProgress:(UpdateInfo *)info { progressToolbar.label.text = info.message; progressToolbar.progressBar.progress = info.progress; } |
Importieren Sie jetzt UpdateInfo.h in DataAccess.h.
#import "UpdateInfo.h" |
Sie können auch die Callback-Method in der DataAccess.mm-Implementierungsdatei zusammen mit den anderen statischen Methoden definieren:
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]; } |
Wenn alles eingerichtet ist, können Sie jetzt die Synchronisationsbeobachtungsfunktion mit der Callback-Methode in der Synchronisationsmethode in der DataAccess.mm-Datei festlegen:
// 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 |
![]() |
Kommentieren Sie diese Seite in DocCommentXchange.
|
Copyright © 2012, iAnywhere Solutions, Inc. - SQL Anywhere 12.0.1 |