Crea un'app Meteo con previsione - Integrazione API

Nel primo articolo di questa serie, abbiamo posto le basi del progetto impostando il progetto e creando la struttura dell'applicazione. In questo articolo, utilizziamo la libreria AFNetworking per interagire con l'API Forecast.


introduzione

Nella prima parte di questa serie, abbiamo posto le basi della nostra applicazione meteorologica. Gli utenti possono aggiungere la loro posizione corrente e passare da una posizione all'altra. In questo tutorial, utilizzeremo la libreria di AFNetworking per chiedere all'API di previsione i dati meteo della posizione attualmente selezionata.

Se vuoi seguire, avrai bisogno di una chiave API Forecast. È possibile ottenere una chiave API registrandosi come sviluppatore a Previsione. La registrazione è gratuita, quindi ti incoraggio a provare il servizio meteorologico di previsione. Puoi trovare la tua chiave API nella parte inferiore della tua dashboard (figura 1).


Figura 1: recupero della chiave API

1. Sottoclassi AFHTTPClient

Come ho scritto in precedenza in questo articolo, useremo il AFNetworking libreria per comunicare con l'API Forecast. Ci sono diverse opzioni quando si lavora con AFNetworking, ma per rendere la nostra applicazione a prova di futuro, opteremo per AFHTTPClient classe. Questa classe è progettata per l'utilizzo di servizi Web, come l'API Forecast. Anche se accediamo a un solo endpoint API, è comunque utile fare uso di AFHTTPClient come imparerai tra qualche istante.

Si consiglia di creare un AFHTTPClient sottoclasse per ogni servizio web. Poiché abbiamo già aggiunto AFNetworking al nostro progetto nel tutorial precedente, possiamo iniziare immediatamente la sottoclasse AFHTTPClient.

Passaggio 1: creare la classe

Crea una nuova classe Objective-C, chiamala MTForecastClient, e renderlo una sottoclasse di AFHTTPClient (figura 2).

Figura 2: sottoclasse di AFHTTPClient

Passaggio 2: creazione di un oggetto Singleton

Adotteremo il modello singleton per rendere più facile l'uso di MTForecastClient classe nel nostro progetto. Ciò significa che solo una istanza della classe è attiva in qualsiasi momento per tutta la durata dell'applicazione. È probabile che tu abbia già familiarità con il modello singleton poiché è un modello comune in molti linguaggi di programmazione orientati agli oggetti. A prima vista, il modello singleton sembra molto conveniente, ma ci sono un certo numero di avvertimenti a cui prestare attenzione. Puoi saperne di più sui singleton leggendo questo eccellente articolo di Matt Gallagher.

La creazione di un oggetto singleton è piuttosto semplice in Objective-C. Inizia dichiarando un metodo di classe in MTForecastClient.h per fornire l'accesso pubblico all'oggetto singleton (vedi sotto).

 #import "AFHTTPClient.h" @interface MTForecastClient: AFHTTPClient #pragma mark - #pragma mark Shared Client + (MTForecastClient *) sharedClient; @fine

L'implementazione di sharedClient all'inizio può sembrare scoraggiante, ma non è così difficile una volta capito cosa sta succedendo. Prima dichiariamo due variabili statiche, (1) predicato di tipo dispatch_once_t e (2) _sharedClient di tipo MTForecastClient. Come suggerisce il nome, predicato è un predicato che usiamo in combinazione con il dispatch_once funzione. Quando si lavora con una variabile di tipo dispatch_once_t, è importante che sia dichiarato staticamente. La seconda variabile, _sharedClient, memorizzerà un riferimento all'oggetto singleton.

Il dispatch_once la funzione accetta un puntatore a a dispatch_once_t struttura, il predicato e un blocco. La bellezza di dispatch_once è che eseguirà il blocco una sola volta per tutta la durata dell'applicazione, che è esattamente ciò che vogliamo. Il dispatch_once la funzione non ha molti usi, ma questo è sicuramente uno di questi. Nel blocco al quale passiamo dispatch_once, creiamo l'oggetto singleton e memorizziamo un riferimento in _sharedClient. È più sicuro da invocare alloc e dentro separatamente per evitare una condizione di competizione che potrebbe portare a un deadlock. Eh ... cosa? Puoi leggere di più sui dettagli nitty su Stack Overflow.

 + (MTForecastClient *) sharedClient static dispatch_once_t predicate; static MTForecastClient * _sharedClient = nil; dispatch_once (& predicate, ^ _sharedClient = [self alloc], _sharedClient = [_sharedClient initWithBaseURL: [self baseURL]];); return _sharedClient; 

La cosa importante da capire sull'implementazione del sharedClient il metodo di classe è quello dell'inizializzatore, initWithBaseURL:, viene invocato una sola volta. L'oggetto Singleton è memorizzato nel _sharedClient variabile statica, che viene restituita dal sharedClient metodo di classe.

Passaggio 3: configurazione del client

Nel sharedClient, noi invochiamo initWithBaseURL:, che a sua volta invoca baseURL, un altro metodo di classe. Nel initWithBaseURL:, impostiamo un'intestazione predefinita, il che significa che il client aggiunge questa intestazione ad ogni richiesta che invia. Questo è uno dei vantaggi del lavorare con AFHTTPClient classe. Nel initWithBaseURL:, registriamo anche una classe operativa HTTP invocando registerHTTPOperationClass:. La libreria AFNetworking fornisce una serie di classi operative specializzate. Una di queste classi è la AFJSONRequestOperation classe, che rende molto semplice l'interazione con un'API JSON. Poiché l'API Forecast restituisce una risposta JSON, la AFJSONRequestOperation la classe è una buona scelta. Il registerHTTPOperationClass: il metodo funziona in modo simile a come registerClass: forCellReuseIdentifier: del UITableView lavori di classe. Dicendo al cliente che classe di operazioni vogliamo utilizzare per interagire con il servizio web, istanziamo istanze di quella classe per noi sotto il cofano. Perché questo è utile diventerà chiaro in pochi istanti.

 - (id) initWithBaseURL: (NSURL *) url self = [super initWithBaseURL: url]; if (self) // Accept HTTP Header [self setDefaultHeader: @ "Accept" valore: @ "application / json"]; // Register HTTP Operation Class [self registerHTTPOperationClass: [AFJSONRequestOperation class]];  return self; 

L'implementazione di baseURL non è altro che un metodo di convenienza per costruire l'URL di base del cliente. L'URL di base è l'URL che il client utilizza per raggiungere il servizio web. È l'URL senza nomi di metodi o parametri. L'URL di base per l'API di previsione è https://api.forecast.io/forecast//. La chiave API è parte dell'URL, come puoi vedere. Questo può sembrare insicuro e in realtà lo è. Non è difficile per qualcuno afferrare la chiave API, quindi è consigliabile lavorare con un proxy per mascherare la chiave API. Poiché questo approccio è un po 'più impegnativo, non tratterò questo aspetto in questa serie.

 + (NSURL *) baseURL return [NSURL URLWithString: [NSString stringWithFormat: @ "https://api.forecast.io/forecast/%@/", MTForecastAPIKey]]; 

Potresti aver notato nell'implementazione di baseURL che ho usato un'altra costante di stringa per l'archiviazione della chiave API. Questo potrebbe sembrare non necessario poiché utilizziamo solo la chiave API in un'unica posizione. Tuttavia, è buona pratica archiviare i dati dell'applicazione in una posizione o in un elenco di proprietà.

 #pragma mark - #pragma mark Forecast API extern NSString * const MTForecastAPIKey;
 #pragma mark - #pragma mark Forecast API NSString * const MTForecastAPIKey = @ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

Passaggio 4: aggiunta di un metodo di supporto

Prima di andare avanti, vorrei estendere il MTForecastClient classe aggiungendo un helper o un metodo di convenienza che renderà più semplice la query dell'API di previsione. Questo metodo di convenienza accetta una posizione e un blocco di completamento. Il blocco di completamento viene eseguito al termine della richiesta. Per semplificare il lavoro con i blocchi, si consiglia di dichiarare un tipo di blocco personalizzato come mostrato di seguito. Se ti senti ancora a disagio con i blocchi, ti consiglio di leggere questo fantastico articolo di Akiel Khan.

Il blocco accetta due argomenti, (1) un valore booleano che indica se la query ha avuto esito positivo e (2) un dizionario con la risposta dalla query. Il metodo di convenienza, requestWeatherForCoordinate: esecuzione:, prende le coordinate di una posizione (CLLocationCoordinate2D) e un blocco di completamento. Utilizzando un blocco di completamento, possiamo evitare di creare un protocollo delegato personalizzato o ricorrere all'utilizzo delle notifiche. I blocchi si adattano perfettamente a questo tipo di scenario.

 #import "AFHTTPClient.h" typedef void (^ MTForecastClientCompletionBlock) (successo BOOL, risposta NSDictionary *); @interface MTForecastClient: AFHTTPClient #pragma mark - #pragma mark Shared Client + (MTForecastClient *) sharedClient; #pragma mark - #pragma mark Instance Methods - (void) requestWeatherForCoordinate: completamento della coordinata (CLLocationCoordinate2D): (completamento MTForecastClientCompletionBlock); @fine

Nel requestWeatherForCoordinate: esecuzione:, noi invochiamo getPath: il successo: il fallimento:, un metodo dichiarato in AFHTTPClient. Il primo argomento è il percorso che viene aggiunto all'URL di base che abbiamo creato in precedenza. Il secondo e il terzo argomento sono blocchi che vengono eseguiti quando la richiesta ha esito positivo e non riesce, rispettivamente. I blocchi di successo e fallimento sono piuttosto semplici. Se è stato passato un blocco di completamento a requestWeatherForCoordinate: esecuzione:, eseguiamo il blocco e passiamo un valore booleano e il dizionario di risposta (o zero nel blocco di errore). Nel blocco di errore, l'errore viene registrato dal blocco di errore nella console per facilitare il debug.

 - (void) requestWeatherForCoordinate: completamento della coordinata (CLLocationCoordinate2D): (MTForecastClientCompletionBlock) completion NSString * path = [NSString stringWithFormat: @ "% f,% f", coordinate.latitudine, coordinate.lunghezza]; [auto getPath: parametri del percorso: nil successo: ^ (operazione AFHTTPRequestOperation *, risposta id) if (completamento) completamento (YES, risposta);  errore: ^ (operazione AFHTTPRequestOperation *, errore NSError *) if (completamento) completamento (NO, nil); NSLog (@ "Impossibile recuperare i dati meteo a causa dell'errore% @ con informazioni utente% @.", Errore, error.userInfo); ]; 

Forse ti starai chiedendo cosa risposta oggetto nei blocchi di successo è o riferimenti. Anche se l'API Forecast restituisce una risposta JSON, il risposta l'oggetto nel blocco di successo è un NSDictionary esempio. Il vantaggio di lavorare con AFJSONHTTPRequestOperation classe, in cui ci siamo registrati initWithBaseURL:, è che accetta la risposta JSON e crea automaticamente un oggetto dai dati di risposta, un dizionario in questo esempio.


2. Interrogazione dell'API previsione

Passaggio 1: modificare setLocation:

Armato di MTForecastClient classe, è il momento di interrogare l'API Forecast e recuperare i dati meteo per la posizione attualmente selezionata. Il posto più adatto per farlo è nel setLocation: metodo del MTWeatherViewController classe. Modificare il setLocation: metodo come mostrato di seguito. Come puoi vedere, tutto ciò che facciamo è invocare fetchWeatherData, un altro metodo di supporto.

 - (void) setLocation: (NSDictionary *) location if (_location! = location) _location = location; // Update User Defaults NSUserDefaults * ud = [NSUserDefaults standardUserDefaults]; [ud setObject: location forKey: MTRainUserDefaultsLocation]; [ud synchronize]; // Notifica post NSNotification * notification1 = [NSNotification notificationWithName: MTRainLocationDidChangeNotification object: self userInfo: location]; [[NSNotificationCenter defaultCenter] postNotification: notification1]; // Aggiorna Visualizza [auto updateView]; // Request Location [self fetchWeatherData]; 

Ti sei mai chiesto perché uso così tanti metodi di supporto nel mio codice? La ragione è semplice. Con il wrapping delle funzionalità nei metodi di supporto, è molto facile riutilizzare il codice in varie parti di un progetto. Il vantaggio principale, tuttavia, è che aiuta a combattere la duplicazione del codice. La duplicazione del codice è qualcosa che dovresti sempre cercare di evitare il più possibile. Un altro vantaggio dell'utilizzo dei metodi di supporto è che rende il codice molto più leggibile. Creando metodi che fanno una cosa e fornendo un nome di metodo ben scelto, è più facile leggere ed elaborare rapidamente il codice.

Passaggio 2: invio della richiesta

È tempo di mettere il SVProgressHUD libreria da usare. Mi piace molto questa libreria perché è così semplice da usare senza ingombrare il codice base del progetto. Dai un'occhiata all'implementazione di fetchWeatherData sotto. Iniziamo mostrando l'avanzamento HUD e poi passiamo una struttura (CLLocationCoordinate2D) al metodo di convenienza che abbiamo creato in precedenza, requestWeatherForCoordinate: esecuzione:. Nel blocco di completamento, nascondiamo l'HUD di avanzamento e registriamo la risposta alla console.

 - (void) fetchWeatherData // Show Progress HUD [SVProgressHUD showWithMaskType: SVProgressHUDMaskTypeGradient]; // Query Forecast API double lat = [[_location objectForKey: MTLocationKeyLatitude] doubleValue]; double lng = [[_locazione oggettoForKey: MTLocationKeyLongitude] doubleValue]; [[MTForecastClient sharedClient] requestWeatherForCoordinate: CLLocationCoordinate2DMake (lat, lng) completamento: ^ (successo BOOL, risposta NSDictionary *) // Ignora avanzamento HUD [SVProgressHUD dismiss]; NSLog (@ "Risposta>% @", risposta); ]; 

Prima di creare ed eseguire l'applicazione, importa il file di intestazione del file MTForecastClient classe in MTWeatherViewController.m.

 #import "MTWeatherViewController.h" #import "MTForecastClient.h" @interface MTWeatherViewController ()  BOOL _locationFound;  @property (strong, nonatomic) NSDictionary * posizione; @property (strong, nonatomic) CLLocationManager * locationManager; @fine

Cosa succede quando il dispositivo non è connesso al web? Hai pensato a quello scenario? In termini di esperienza utente, è buona norma informare l'utente quando l'applicazione non è in grado di richiedere dati dall'API di previsione. Lascia che ti mostri come farlo con la libreria di AFNetworking.


3. Raggiungibilità

Ci sono un certo numero di librerie che forniscono questa funzionalità, ma ci atterremo con AFNetworking. Apple fornisce anche codice di esempio, ma è un po 'obsoleto e non supporta ARC.

AFNetworking ha davvero abbracciato i blocchi, che è sicuramente uno dei motivi per cui questa libreria è diventata così popolare. Monitorare le modifiche di raggiungibilità è semplice come passare un blocco a setReachabilityStatusChangeBlock:, un altro metodo di AFHTTPClient classe. Il blocco viene eseguito ogni volta che lo stato di raggiungibilità cambia e accetta un singolo argomento di tipo AFNetworkReachabilityStatus. Dai un'occhiata al aggiornato initWithBaseURL: metodo del MTForecastClient classe.

 - (id) initWithBaseURL: (NSURL *) url self = [super initWithBaseURL: url]; if (self) // Accept HTTP Header [self setDefaultHeader: @ "Accept" valore: @ "application / json"]; // Register HTTP Operation Class [self registerHTTPOperationClass: [AFJSONRequestOperation class]]; // Raggiungibilità __weak typeof (self) weakSelf = self; [self setReachabilityStatusChangeBlock: ^ (stato AFNetworkReachabilityStatus) [[NSNotificationCenter defaultCenter] postNotificationName: MTRainReachabilityStatusDidChangeNotification object: weakSelf]; ];  return self; 

Per evitare un ciclo di conservazione, passiamo un riferimento debole all'oggetto singleton nel blocco a cui passiamo setReachabilityStatusChangeBlock:. Anche se utilizzi ARC nei tuoi progetti, devi comunque essere a conoscenza di problemi di memoria sottili come questo. Il nome della notifica che pubblichiamo è un'altra costante di stringa dichiarata in MTConstants.h / .m.

 extern NSString * const MTRainReachabilityStatusDidChangeNotification;
 NSString * const MTRainReachabilityStatusDidChangeNotification = @ "com.mobileTuts.MTRainReachabilityStatusDidChangeNotification";

Il motivo per la pubblicazione di una notifica nel blocco di modifica dello stato di raggiungibilità è quello di facilitare l'aggiornamento di altre parti dell'applicazione quando la raggiungibilità del dispositivo cambia. Per assicurarsi che il MTWeatherViewController la classe viene avvisata delle variazioni di raggiungibilità, le istanze della classe vengono aggiunte come osservatore per le notifiche inviate dal client Forecast come mostrato di seguito.

 - (id) initWithNibName: (NSString *) nibNameOrNil bundle: (NSBundle *) nibBundleOrNil self = [super initWithNibName: nibNameOrNil bundle: nibBundleOrNil]; if (self) // Initialize Location Manager self.locationManager = [[CLLocationManager alloc] init]; // Configura Location Manager [self.locationManager setDelegate: self]; [self.locationManager setDesiredAccuracy: kCLLocationAccuracyKilometer]; // Aggiungi Observer NSNotificationCenter * nc = [NSNotificationCenter defaultCenter]; [nc addObserver: self-selector: @selector (reachabilityStatusDidChange :) nome: MTRainReachabilityStatusDidChangeNotification object: nil];  return self; 

Ciò significa anche che dobbiamo rimuovere l'istanza come osservatore in dealloc metodo. Questo è un dettaglio che viene spesso trascurato.

 - (void) dealloc // Remove Observer [[NSNotificationCenter defaultCenter] removeObserver: self]; 

L'implementazione di reachabilityStatusDidChange: è piuttosto semplice al momento. Aggiorneremo la sua implementazione una volta creata l'interfaccia utente dell'applicazione.

 - (void) reachabilityStatusDidChange: notifica (NSNotification *) MTForecastClient * forecastClient = [oggetto notifica]; NSLog (@ "Reachability Status>% i", forecastClient.networkReachabilityStatus); 

4. Dati di aggiornamento

Prima di concludere questo post, desidero aggiungere due funzionalità aggiuntive, (1) il recupero dei dati meteo ogni volta che l'applicazione diventa attiva e (2) l'aggiunta della possibilità di aggiornare manualmente i dati meteorologici. Potremmo implementare un timer che recuperi dati freschi ogni ora o così, ma a mio parere questo non è necessario per un'applicazione meteorologica. La maggior parte degli utenti avvierà l'applicazione, guarderà il tempo e metterà l'applicazione in secondo piano. È quindi necessario recuperare dati freschi solo quando l'utente avvia l'applicazione. Ciò significa che dobbiamo ascoltare UIApplicationDidBecomeActiveNotification notifiche nel MTWeatherViewController classe. Come abbiamo fatto per monitorare i cambiamenti di raggiungibilità, aggiungiamo istanze della classe come osservatori di notifiche di tipo UIApplicationDidBecomeActiveNotification.

 - (id) initWithNibName: (NSString *) nibNameOrNil bundle: (NSBundle *) nibBundleOrNil self = [super initWithNibName: nibNameOrNil bundle: nibBundleOrNil]; if (self) // Initialize Location Manager self.locationManager = [[CLLocationManager alloc] init]; // Configura Location Manager [self.locationManager setDelegate: self]; [self.locationManager setDesiredAccuracy: kCLLocationAccuracyKilometer]; // Aggiungi Observer NSNotificationCenter * nc = [NSNotificationCenter defaultCenter]; [nc addObserver: self-selector: @selector (applicationDidBecomeActive :) name: UIApplicationDidBecomeActiveNotification object: nil]; [nc addObserver: self-selector: @selector (reachabilityStatusDidChange :) nome: MTRainReachabilityStatusDidChangeNotification object: nil];  return self; 

Nel applicationDidBecomeActive:, lo verifichiamo Posizione è impostato (not zero) perché questo non sarà sempre vero. Se la posizione è valida, recuperiamo i dati meteorologici.

 - (void) applicationDidBecomeActive: (NSNotification *) notification if (self.location) [self fetchWeatherData]; 

Ho anche modificato fetchWeatherData interrogare solo l'API Forecast se il dispositivo è connesso al web.

 - (void) fetchWeatherData if ([[MTForecastClient sharedClient] networkReachabilityStatus] == AFNetworkReachabilityStatusNotReachable) return; // Show Progress HUD [SVProgressHUD showWithMaskType: SVProgressHUDMaskTypeGradient]; // Query Forecast API double lat = [[_location objectForKey: MTLocationKeyLatitude] doubleValue]; double lng = [[_locazione oggettoForKey: MTLocationKeyLongitude] doubleValue]; [[MTForecastClient sharedClient] requestWeatherForCoordinate: CLLocationCoordinate2DMake (lat, lng) completamento: ^ (successo BOOL, risposta NSDictionary *) // Ignora avanzamento HUD [SVProgressHUD dismiss]; // NSLog (@ "Risposta>% @", risposta); ]; 

Aggiungiamo un pulsante al controller della vista meteo che l'utente può toccare per aggiornare manualmente i dati meteorologici. Crea un outlet in MTWeatherViewController.h e creare a ricaricare: azione in MTWeatherViewController.m.

 #importare  #import "MTLocationsViewController.h" @interface MTWeatherViewController: UIViewController  @property (weak, nonatomic) IBOutlet UILabel * labelLocation; @property (weak, nonatomic) IBOutlet UIButton * buttonRefresh; @fine
 - (IBAction) refresh: (id) sender if (self.location) [self fetchWeatherData]; 

Aperto MTWeatherViewController.xib, aggiungi un pulsante alla vista del controller della vista con un titolo di ricaricare, e collegare la presa e l'azione con il pulsante (figura 3). Il motivo per cui si crea uno sbocco per il pulsante è di poterlo disabilitare quando non è disponibile alcuna connessione di rete. Perché funzioni, dobbiamo aggiornare il reachabilityStatusDidChange: metodo come mostrato di seguito.

Figura 3: aggiunta di un pulsante di aggiornamento
 - (void) reachabilityStatusDidChange: notifica (NSNotification *) MTForecastClient * forecastClient = [oggetto notifica]; NSLog (@ "Reachability Status>% i", forecastClient.networkReachabilityStatus); // Aggiorna pulsante Aggiorna self.buttonRefresh.enabled = (forecastClient.networkReachabilityStatus! = AFNetworkReachabilityStatusNotReachable); 

Non è necessario disabilitare temporaneamente il pulsante di aggiornamento quando viene elaborata una richiesta fetchWeatherData perché l'HUD di avanzamento aggiunge un livello in cima alla vista del controller della vista che impedisce all'utente di toccare il pulsante più di una volta. Costruisci ed esegui l'applicazione per testare tutto.


Bonus: rimozione delle posizioni

Un lettore mi ha chiesto come eliminare le posizioni dall'elenco, quindi lo includo qui per motivi di completezza. La prima cosa che dobbiamo fare è dire alla tabella quali righe sono modificabili implementando tableView: canEditRowAtIndexPath: del UITableViewDataSource protocollo. Questo metodo restituisce se la riga a indexPath è modificabile e NO se non è. L'implementazione è semplice come puoi vedere qui sotto. Ogni riga è modificabile tranne la prima riga e la posizione attualmente selezionata.

 - (BOOL) tableView: (UITableView *) tableView canEditRowAtIndexPath: (NSIndexPath *) indexPath if (indexPath.row == 0) return NO;  // Fetch Location NSDictionary * location = [self.locations objectAtIndex: (indexPath.row - 1)]; return! [self isCurrentLocation: location]; 

Per verificare se Posizione è la posizione corrente, usiamo un altro metodo di supporto, isCurrentLocation:, in cui recuperiamo la posizione corrente e confrontiamo le coordinate delle posizioni. Sarebbe stato meglio (e più semplice) se avessimo assegnato un identificatore univoco a ciascuna posizione memorizzata nel database dei valori predefiniti dell'utente. Non solo renderebbe più facile il confronto delle posizioni, ma ci consentirebbe anche di memorizzare l'identificativo univoco della posizione corrente nel database dei valori predefiniti dell'utente e di cercarlo nella serie di posizioni. Il problema con l'implementazione corrente è che le posizioni con le stesse identiche coordinate non possono essere distinte l'una dall'altra.

 - (BOOL) isCurrentLocation: (NSDictionary *) posizione // Recupera posizione corrente NSDictionary * currentLocation = [[NSUserDefaults standardUserDefaults] objectForKey: MTRainUserDefaultsLocation]; if ([location [MTLocationKeyLatitude] doubleValue] == [currentLocation [MTLocationKeyLatitude] doubleValue] && [posizione [MTLocationKeyLongitude] doubleValue] == [currentLocation [MTLocationKeyLongitude] doubleValue]) return YES;  return NO; 

Quando l'utente tocca il pulsante Elimina di una riga di visualizzazione tabella, l'origine dati della vista tabella viene inviata a tableView: commitEditingStyle: forRowAtIndexPath: Messaggio. In questo metodo, è necessario (1) aggiornare l'origine dati, (2) salvare le modifiche al database dei valori predefiniti dell'utente e (3) aggiornare la vista tabella. Se editingStyle è uguale a UITableViewCellEditingStyleDelete, rimuoviamo la posizione dal posizioni array e memorizzare l'array aggiornato nel database dei valori predefiniti dell'utente. Cancelliamo anche la riga dalla vista tabella per riflettere la modifica dell'origine dati.

 - (void) tableView: (UITableView *) tableView commitEditingStyle: (UITableViewCellEditingStyle) editStyle forRowAtIndexPath: (NSIndexPath *) indexPath if (editingStyle == UITableViewCellEditingStyleDelete) // Percorsi di aggiornamento [self.locations removeObjectAtIndex: (indexPath.row - 1)] ; // Aggiorna impostazioni predefinite utente [[NSUserDefaults standardUserDefaults] setObject: self.locations forKey: MTRainUserDefaultsLocations]; // Aggiorna tabella Visualizza [tableView deleteRowsAtIndexPaths: @ [indexPath] withRowAnimation: UITableViewRowAnimationTop]; 

Per attivare lo stile di modifica della vista tabella, è necessario aggiungere un pulsante di modifica all'interfaccia utente. Crea una presa per il pulsante in MTLocationsViewController.h e un'azione chiamata editLocations: nel MTLocationsViewController.m. Nel editLocations:, alteriamo lo stile di modifica della vista tabella.

 #importare  @protocol MTLocationsViewControllerDelegate; @interface MTLocationsViewController: UIViewController  @property (weak, nonatomic) id delegare; @property (weak, nonatomic) IBOutlet UITableView * tableView; @property (weak, nonatomic) IBOutlet UIBarButtonItem * editButton; @end @protocol MTLocationsViewControllerDelegate  - (void) controllerShouldAddCurrentLocation: (MTLocationsViewController *) controller; - (void) controller: (MTLocationsViewController *) controller didSelectLocation: (NSDictionary *) posizione; @fine
 - (IBAction) editLocations: (id) sender [self.tableView setEditing:! [Self.tableView isEditing] animato: YES]; 

Aperto MTLocationsViewController.xib, aggiungi una barra di navigazione alla vista del controller della vista e aggiungi un pulsante di modifica alla barra di navigazione. Collega il pulsante di modifica con la presa e l'azione che abbiamo creato un momento fa.


Figura 4: aggiunta di un pulsante Modifica

Ci si potrebbe chiedere perché abbiamo creato uno sbocco per il pulsante di modifica. Il motivo è che dobbiamo essere in grado di cambiare il titolo del pulsante di modifica da modificare a Fatto, e viceversa, ogni volta che cambia lo stile di modifica della vista tabella. Inoltre, quando l'utente cancella l'ultima posizione (tranne la posizione corrente) nella vista tabella, sarebbe bello attivare automaticamente lo stile di modifica della vista tabella. Queste caratteristiche non sono difficili da implementare, motivo per cui le lascio a te come esercizio. In caso di problemi o domande, non esitare a lasciare un commento nei commenti sotto questo articolo.

Conclusione

Abbiamo integrato con successo l'API Forecast nella nostra applicazione meteo. Nel prossimo tutorial, implementeremo l'attenzione sull'interfaccia utente e sul design dell'applicazione.