Creați o aplicație meteo cu prognoză - Integrare API

În primul articol din această serie, am pus bazele proiectului prin stabilirea proiectului și crearea structurii aplicației. În acest articol, folosim biblioteca AFNetworking pentru a interacționa cu API-ul Forecast.


Introducere

În prima parte a acestei serii, am pus temelia aplicației noastre meteorologice. Utilizatorii își pot adăuga locația curentă și pot comuta între locații. În acest tutorial, vom folosi biblioteca AFNetworking pentru a solicita API-ul Forecast pentru datele meteo ale locației selectate în prezent.

Dacă doriți să urmați, veți avea nevoie de o cheie API pentru prognoză. Puteți obține o cheie API înregistrând ca dezvoltator la Forecast. Înregistrarea este gratuită, așa că vă încurajez să încercați serviciul meteorologic Prognoza. Puteți găsi cheia dvs. API în partea de jos a tabloului de bord (figura 1).


Figura 1: Obținerea cheii API

1. Subclasarea AFHTTPClient

Așa cum am scris mai devreme în acest articol, vom folosi AFNetworking bibliotecă pentru comunicarea cu API-ul Forecast. Există mai multe opțiuni atunci când lucrați cu AFNetworking, dar pentru a face dovada noastră viitoare dovada, vom opta pentru AFHTTPClient clasă. Această clasă este concepută pentru consumarea serviciilor web, cum ar fi API Forecast. Chiar dacă vom avea acces doar la un punct final API, este încă util să folosim AFHTTPClient după cum veți învăța în câteva momente.

Se recomandă crearea unui AFHTTPClient subclasa pentru fiecare serviciu web. Deoarece am adăugat deja AFNetworking la proiectul nostru din tutorialul anterior, putem începe imediat subclasarea AFHTTPClient.

Pasul 1: Creați Clasa

Creați o nouă clasă Obiectiv-C, denumiți-o MTForecastClient, și să o facă o subclasă de AFHTTPClient (figura 2).

Figura 2: Subclasarea AFHTTPClient

Pasul 2: Crearea unui obiect Singleton

Vom adopta modelul singleton pentru a facilita utilizarea acestuia MTForecastClient clasă în proiectul nostru. Aceasta înseamnă că numai o singură instanță a clasei este în viață la un moment dat pe durata de viață a aplicației. Șansele sunt că deja sunteți familiarizat cu modelul singleton deoarece este un model comun în multe limbi de programare orientate pe obiecte. La prima vedere, modelul singleton pare foarte convenabil, dar există o serie de avertismente pe care să le urmăriți. Puteți afla mai multe despre single-uri prin citirea acestui articol excelent de Matt Gallagher.

Crearea unui obiect singleton este destul de simplă în Obiectiv-C. Începeți prin a declara o metodă de clasă în MTForecastClient.h pentru a oferi acces public la obiectul singleton (vezi mai jos).

 #import "AFHTTPClient.h" @interface MTForecastClient: AFHTTPClient #pragma - marca #pragma Client comun + (MTForecastClient *) sharedClient; @Sfârșit

Implementarea sistemului sharedClient ar putea părea descurajant la început, dar nu este atât de dificil când înțelegeți ce se întâmplă. Mai întâi declarăm două variabile statice, (1) predicat de tip dispatch_once_t și (2) _sharedClient de tip MTForecastClient. După cum sugerează și numele, predicat este un predicat pe care îl folosim în combinație cu dispatch_once funcţie. Când lucrați cu o variabilă de tip dispatch_once_t, este important să fie declarat static. A doua variabilă, _sharedClient, va stoca o referință la obiectul singleton.

dispatch_once funcția are un pointer la a dispatch_once_t structura, predicatul și un bloc. Frumusețea lui dispatch_once este că va executa blocul o singură dată pentru durata de viață a aplicației, ceea ce este exact ceea ce vrem. dispatch_once funcția nu are multe utilizări, dar aceasta este cu siguranță una dintre ele. În blocul în care trecem dispatch_once, vom crea obiectul singleton și vom stoca o referință în _sharedClient. Este mai sigur să invocați aloc și init separat pentru a evita o situație de rasă care ar putea duce la un impas. Eu ... ce? Puteți să citiți mai multe detalii despre detaliile cu privire la supraîncărcarea stivei.

 + (MTForecastClient *) sharedClient static dispatch_once_t predicate; static MTForecastClient * _sharedClient = nul; dispatch_once (& predicate, ^ _sharedClient = [self alloc]; _sharedClient = [_sharedClient initWithBaseURL: [self baseURL]];); returnați _sharedClient; 

Lucru important de înțeles cu privire la punerea în aplicare a sharedClient metoda de clasă este că inițializatorul, initWithBaseURL:, este invocată o singură dată. Obiectul singleton este stocat în _sharedClient variabila statica, care este returnata de catre sharedClient clasă.

Pasul 3: Configurarea clientului

În sharedClient, noi invocăm initWithBaseURL:, care la rândul său invocă baseURL, o altă metodă de clasă. În initWithBaseURL:, am setat un antet implicit, ceea ce înseamnă că clientul adaugă acest antet la fiecare solicitare pe care o trimite. Acesta este unul dintre avantajele de a lucra cu AFHTTPClient clasă. În initWithBaseURL:, se înregistrează, de asemenea, o clasă de operațiuni HTTP invocând registerHTTPOperationClass:. Biblioteca AFNetworking oferă o serie de clase de operații specializate. Una dintre aceste clase este AFJSONRequestOperation clasa, ceea ce face ca interacțiunea cu un API JSON să fie foarte ușoară. Deoarece API-ul de prognoză returnează un răspuns JSON, AFJSONRequestOperation clasa este o alegere bună. registerHTTPOperationClass: metoda funcționează similar cu modul în care RegisterClass: forCellReuseIdentifier: din UITableView clasa de lucrări. Dacă îi spunem clientului ce clasă de operațiuni vrem să utilizăm pentru a interacționa cu serviciul web, acesta va instanțiate instanțele acelei clase pentru noi sub capotă. De ce este util acest lucru va deveni clar în câteva momente.

 - (id) initWithBaseURL: (NSURL *) url auto = [super initWithBaseURL: url]; dacă (auto) // Acceptați antetul HTTP [auto setDefaultHeader: @ "Acceptați" valoarea: @ "application / json"]; // Înregistrați Clasa de operațiuni HTTP [auto-înregistrareHTTPOperationClass: [AFJSONRequestOperation class]];  întoarce-te; 

Implementarea sistemului baseURL nu este altceva decât o metodă convenabilă pentru construirea adresei URL de bază a clientului. URL-ul de bază este adresa URL utilizată de client pentru a ajunge la serviciul web. Este URL-ul fără nume sau parametri de metode. Adresa URL de bază pentru API-ul Forecast este https://api.forecast.io/forecast//. Cheia API face parte din URL, după cum puteți vedea. Acest lucru poate părea nesigur și de fapt este. Nu este dificil pentru cineva să apucă cheia API, așa că este recomandabil să lucrezi cu un proxy pentru a masca cheia API. Deoarece această abordare este puțin mai implicată, nu voi acoperi acest aspect în această serie.

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

Este posibil să fi observat în punerea în aplicare a baseURL că am folosit o altă constantă șir pentru stocarea cheii API. Acest lucru poate părea inutil, deoarece utilizăm cheia API numai într-o singură locație. Cu toate acestea, este bine să stocați datele aplicației într-o singură locație sau într-o listă de proprietăți.

 #pragma - marca #pragma Prognoza API externă NSString * const MTForecastAPIKey;
 #pragma - marca #pragma Prognoza API NSString * const MTForecastAPIKey = @ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

Pasul 4: Adăugarea unei metode de ajutor

Înainte de a merge mai departe, aș dori să prelungesc MTForecastClient clasă prin adăugarea unei metode de ajutor sau de conveniență care va facilita interogarea API-ului Forecast. Această metodă convenabilă va accepta o locație și un bloc de completare. Blocul de completare este executat când cererea se termină. Pentru a facilita lucrul cu blocuri, se recomandă să declarați un tip de bloc personalizat, după cum se arată mai jos. Dacă tot vă simțiți inconfortabil folosind blocuri, vă recomand să citiți acest articol mare de Akiel Khan.

Blocul are două argumente, (1) un boolean care indică dacă interogarea a avut succes și (2) un dicționar cu răspunsul din interogare. Metoda de confort, requestWeatherForCoordinate: finalizare:, ia coordonatele unei locații (CLLocationCoordinate2D) și un bloc de completare. Prin utilizarea unui bloc de completare, putem evita crearea unui protocol personalizat de delegat sau să ne întoarcem la utilizarea notificărilor. Blocurile sunt potrivite perfect pentru acest tip de scenariu.

 #import "AFHTTPClient.h" typedef void (^ MTForecastClientCompletionBlock) (succes BOOL, răspuns NSDictionary *); @interface MTForecastClient: AFHTTPClient #pragma - marca #pragma Client comun + (MTForecastClient *) sharedClient; #pragma marcă - #pragma marca Instance Metode - (void) requestWeatherForCoordinate: (CLLocationCoordinate2D) coordona completarea: (MTForecastClientCompletionBlock) finalizare; @Sfârșit

În requestWeatherForCoordinate: finalizare:, noi invocăm getPath: succes: eșec:, o metodă declarată în AFHTTPClient. Primul argument este calea care este adăugată la adresa de bază pe care am creat-o mai devreme. Al doilea și al treilea argument sunt blocurile care sunt executate atunci când cererea are succes și respectiv eșuează. Blocurile de succes și de eșec sunt destul de simple. Dacă a fost trecut un bloc de completare requestWeatherForCoordinate: finalizare:, execută blocul și transmite o valoare booleană și dicționarul de răspuns (sau zero în blocul de defecțiuni). În blocul de defecțiuni, se înregistrează eroarea de la blocul de defecțiuni la consola pentru a facilita depanarea.

 - (void) requestWeatherForCoordinate: (CLLocationCoordinate2D) coordonarea finalizării: (MTForecastClientCompletionBlock) finalizarea NSString * path = [NSString șirWithFormat: @ "% f,% f", coordinate.latitude, coordinate.longitude]; [self getPath: parametrii căii: succesul nil: ^ (operațiunea AFHTTPRequestOperation *, id răspuns) if (completare) completare (DA, răspuns);  eșec: ^ (operație AFHTTPRequestOperation *, eroare NSError *) if (completare) completare (NO, nil); NSLog (@ "Imposibil de preluat date meteorologice din cauza erorii% @ cu informația utilizatorului% @", eroare, error.userInfo); ]; 

S-ar putea să vă întrebați ce raspuns obiect în blocurile de succes este sau referințe. Chiar dacă API-ul de prognoză returnează un răspuns JSON, raspuns obiect în blocul de succes este un NSDictionary instanță. Beneficiul de a lucra cu AFJSONHTTPRequestOperation clasa la care ne-am înregistrat initWithBaseURL:, este că acceptă răspunsul JSON și creează automat un obiect din datele de răspuns, un dicționar în acest exemplu.


2. Solicitați interfața API pentru prognoză

Pasul 1: Modificarea setLocation:

Înarmat cu MTForecastClient , este timpul să interogați API Forecast și să preluați datele meteo pentru locația selectată în prezent. Cel mai potrivit loc pentru a face acest lucru este în setLocation: metodă a MTWeatherViewController clasă. Modificați setLocation: așa cum se arată mai jos. După cum puteți vedea, tot ce facem este să invocăm fetchWeatherData, altă metodă de ajutor.

 - (void) setLocation: (NSDictionary *) locație if (_location! = locație) _location = locație; // Actualizați setările implicite ale utilizatorilor NSUserDefaults * ud = [NSUserDefaults standardUserDefaults]; [ud setObject: locația pentruKey: MTRainUserDefaultsLocation]; [sincroniza]; // Notificare de notificare NSNotification * notification1 = [NSNotification notificationWithName: Obiect MTRainLocationDidChangeNotification: self userInfo: location]; [[NSNotificationCenter defaultCenter] postNotificare: notificare1]; // Actualizare Vizualizare [self updateView]; // Solicitați locația [self fetchWeatherData]; 

Te-ai întrebat vreodată de ce folosesc atât de multe metode de ajutor în codul meu? Motivul este simplu. Prin împachetarea funcționalității în metodele de ajutor, este foarte ușor să reutilizați codul în diferite locuri ale unui proiect. Principalul beneficiu, totuși, este că ajută la dublarea codului de luptă. Duplicarea de coduri este ceva ce ar trebui să încercați întotdeauna să evitați cât mai mult posibil. Un alt avantaj al utilizării metodelor de ajutor este că face codul mult mai ușor de citit. Prin crearea de metode care fac un singur lucru și oferind un nume bine ales al metodei, este mai ușor să citiți și să procesați rapid codul.

Pasul 2: Trimiterea cererii

Este timpul pentru a pune SVProgressHUD bibliotecă de utilizat. Îmi place foarte mult această bibliotecă deoarece este atât de simplu de utilizat, fără a aglomera baza de cod a proiectului. Aruncați o privire la punerea în aplicare a fetchWeatherData de mai jos. Începem prin a arăta progresul HUD și apoi trece o structură (CLLocationCoordinate2D) la metoda de confort pe care am creat-o mai devreme, requestWeatherForCoordinate: finalizare:. În blocul de completare, ascundem progresul HUD și înregistram răspunsul la consola.

 - (void) fetchWeatherData // Afișați progresul HUD [SVProgressHUD showWithMaskType: SVProgressHUDMaskTypeGradient]; // Query Forecast API dublă lat = [[_locație obiectForKey: MTLocationKeyLatitude] doubleValue]; dublu lng = [[localizare obiectForKey: MTLocationKeyLongitude] doubleValue]; [[MTForecastClient sharedClient] requestWeatherForCoordinate: CLLocationCoordinate2DMake (lat, lng) finalizare: ^ (succes BOOL, NSDictionary * răspuns) // Renunță la progresul HUD [SVProgressHUD respinge]; NSLog (@ "Response>% @", răspuns); ]; 

Înainte de a construi și a rula aplicația, importați fișierul antet al MTForecastClient clasă în MTWeatherViewController.m.

 #import "MTWeatherViewController.h" #import "MTForecastClient.h" @interface MTWeatherViewController ()  BOOL _locationFound;  @property (puternic, nonatomic) NSDictionary * locație; @property (puternic, nonatomic) CLLocationManager * locationManager; @Sfârșit

Ce se întâmplă atunci când dispozitivul nu este conectat la web? Te-ai gândit la scenariul ăsta? În ceea ce privește experiența utilizatorului, este o bună practică să informați utilizatorul atunci când aplicația nu poate solicita date din API-ul Forecast. Permiteți-mi să arăt cum să faceți acest lucru cu biblioteca AFNetworking.


3. Reachabilitatea

Există o serie de biblioteci care oferă această funcție, dar vom rămâne cu AFNetworking. Apple oferă, de asemenea, cod exemplu, dar este puțin depășit și nu suportă ARC.

AFNetworking a îmbrățișat cu adevărat blocuri, ceea ce este cu siguranță unul dintre motivele pentru care această bibliotecă a devenit atât de populară. Monitorizarea pentru modificările de accesibilitate este la fel de simplă ca și trecerea unui bloc setReachabilityStatusChangeBlock:, o altă metodă a AFHTTPClient clasă. Blocul este executat de fiecare dată când se schimbă starea de accesibilitate și acceptă un singur argument de tip AFNetworkReachabilityStatus. Uitați-vă la actualizată initWithBaseURL: metodă a MTForecastClient clasă.

 - (id) initWithBaseURL: (NSURL *) url auto = [super initWithBaseURL: url]; dacă (auto) // Acceptați antetul HTTP [auto setDefaultHeader: @ "Acceptați" valoarea: @ "application / json"]; // Înregistrați Clasa de operațiuni HTTP [auto-înregistrareHTTPOperationClass: [AFJSONRequestOperation class]]; // Reachabilitate __weak tip de (self) weakSelf = auto; [auto setReachabilityStatusChangeBlock: ^ (starea AFNetworkReachabilityStatus) [[NSNotificationCenter defaultCenter] postNotificationName: Obiect MTRainReachabilityStatusDidChangeNotification: slabSelf]; ];  întoarce-te; 

Pentru a evita un ciclu de reținere, trimitem o referință slabă la obiectul singleton din blocul la care trecem setReachabilityStatusChangeBlock:. Chiar dacă folosiți ARC în proiectele dvs., trebuie să fiți conștienți de problemele de memorie subtile precum acest lucru. Numele notificării pe care o trimitem este o altă constantă string declarată în MTConstants.h / .m.

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

Motivul pentru care trimiteți o notificare în blocul de modificare a stării de disponibilitate este să ușurați actualizarea altor părți ale aplicației atunci când se schimbă gradul de accesibilitate al dispozitivului. Pentru a vă asigura că MTWeatherViewController clasa este notificată cu privire la modificările de accesibilitate, instanțele clasei sunt adăugate în calitate de observator pentru notificările trimise de clientul Forecast, după cum se arată mai jos.

 - (id) initWithNibName: (NSString *) pachet nibNameOrNil: (NSBundle *) nibBundleOrNil auto = [super initWithNibName: nibNameOrNil bundle: nibBundleOrNil]; dacă (self) // Initializați Managerul locației self.locationManager = [[CLLocationManager alloc] init]; // Configurează managerul locației [self.locationManager setDelegate: self]; [self.locationManager setDesiredAccuracy: kCLLocationActivitateKilometru]; // Adaugă Observer NSNotificationCenter * nc = [NSNotificationCenter defaultCenter]; [nc addObserver: selector auto: @selector (accesibilStatusDidChange :) nume: obiect MTRainReachabilityStatusDidChangeNotification: nil];  întoarce-te; 

Acest lucru înseamnă, de asemenea, că trebuie să eliminăm instanța ca observator în dealloc metodă. Acesta este un detaliu care este de multe ori trecut cu vederea.

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

Implementarea sistemului reachabilityStatusDidChange: este destul de fundamental în acest moment. Vom actualiza punerea sa în aplicare odată ce vom crea interfața de utilizare a aplicației.

 - (void) reachabilityStatusDidChange: (NSNotification *) notificare MTForecastClient * forecastClient = [obiect notificare]; NSLog (@ "Starea de disponibilitate>% i", forecastClient.networkReachabilityStatus); 

4. Actualizarea datelor

Înainte de a încheia acest post, vreau să adaug două caracteristici suplimentare, (1) să preiau date meteorologice ori de câte ori aplicația devine activă și (2) să adauge capacitatea de a actualiza manual datele meteorologice. Am putea pune în aplicare un cronometru care să aducă date proaspete în fiecare oră sau cam așa, dar acest lucru nu este necesar pentru o aplicație meteo în opinia mea. Majoritatea utilizatorilor vor lansa aplicația, aruncă o privire asupra vremii și vor pune aplicația în fundal. Prin urmare, este necesară numai preluarea datelor noi când utilizatorul lansează aplicația. Asta înseamnă că trebuie să ascultăm UIApplicationDidBecomeActiveNotification notificări în MTWeatherViewController clasă. Așa cum am făcut pentru monitorizarea modificărilor de accesibilitate, adăugăm instanțe ale clasei ca observatori ai notificărilor de tip UIApplicationDidBecomeActiveNotification.

 - (id) initWithNibName: (NSString *) pachet nibNameOrNil: (NSBundle *) nibBundleOrNil auto = [super initWithNibName: nibNameOrNil bundle: nibBundleOrNil]; dacă (self) // Initializați Managerul locației self.locationManager = [[CLLocationManager alloc] init]; // Configurează managerul locației [self.locationManager setDelegate: self]; [self.locationManager setDesiredAccuracy: kCLLocationActivitateKilometru]; // Adaugă Observer NSNotificationCenter * nc = [NSNotificationCenter defaultCenter]; [nc addObserver: selector auto: @selector (applicationDidBecomeActive :) nume: obiect UIApplicationDidBecomeActiveNotification: nil]; [nc addObserver: selector auto: @selector (accesibilStatusDidChange :) nume: obiect MTRainReachabilityStatusDidChangeNotification: nil];  întoarce-te; 

În applicationDidBecomeActive:, verificăm asta Locație este setat (nu zero) pentru că nu va fi întotdeauna adevărat. Dacă locația este validă, vom prelua datele despre vreme.

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

De asemenea, am modificat fetchWeatherData pentru a interoga API-ul Prognoza numai dacă dispozitivul este conectat la web.

 - (void) fetchWeatherData if ([[MTForecastClient sharedClient] rețeaReachabilityStatus] == AFNetworkReachabilityStatusNotReachable) întoarcere; // Afișați progresul HUD [SVProgressHUD showWithMaskType: SVProgressHUDMaskTypeGradient]; // Query Forecast API dublă lat = [[_locație obiectForKey: MTLocationKeyLatitude] doubleValue]; dublu lng = [[localizare obiectForKey: MTLocationKeyLongitude] doubleValue]; [[MTForecastClient sharedClient] requestWeatherForCoordinate: CLLocationCoordinate2DMake (lat, lng) finalizare: ^ (succes BOOL, NSDictionary * răspuns) // Renunță la progresul HUD [SVProgressHUD respinge]; // NSLog (@ "Response>% @", răspuns); ]; 

Să adăugăm un buton controlerului de vizualizare pe care utilizatorul îl poate atinge pentru a actualiza manual datele meteorologice. Creați o priză în MTWeatherViewController.h și de a crea un reîmprospăta: acțiune în MTWeatherViewController.m.

 #import  #import "MTLocationsViewController.h" @interface MTWeatherViewController: UIViewController  @property (slab, nonatomic) IBOutlet UILabel * labelLocation; @property (slab, nonatomic) IBOutlet UIButton * butonRefresh; @Sfârșit
 - (IBAction) refresh: (id) expeditorul if (self.location) [self fetchWeatherData]; 

Deschis MTWeatherViewController.xib, adăugați un buton la vizualizarea controlerului de vizualizare cu un titlu de Reîmprospăta, și conectați priza și acțiunea cu butonul (figura 3). Motivul creării unei prize pentru buton este să îl puteți dezactiva atunci când nu este disponibilă nicio conexiune la rețea. Pentru ca acest lucru să funcționeze, trebuie să actualizăm reachabilityStatusDidChange: așa cum se arată mai jos.

Figura 3: Adăugarea unui buton de actualizare
 - (void) reachabilityStatusDidChange: (NSNotification *) notificare MTForecastClient * forecastClient = [obiect notificare]; NSLog (@ "Starea de disponibilitate>% i", forecastClient.networkReachabilityStatus); // Actualizați butonul de actualizare self.buttonRefresh.enabled = (forecastClient.networkReachabilityStatus! = AFNetworkReachabilityStatusNotReachable); 

Nu este necesar să dezactivați temporar butonul de reîmprospătare când se procesează o cerere fetchWeatherData deoarece progresul HUD adaugă un strat deasupra vederii controlerului de vizualizare care împiedică utilizatorul să atingă butonul de mai multe ori. Construiți și rulați aplicația pentru a testa totul.


Bonus: eliminarea locațiilor

Un cititor ma întrebat cum să șterg locațiile din listă, așa că eu îl includ aici de dragul completării. Primul lucru pe care trebuie să-l facem este să aflăm în tabelul de vizualizare care rânduri sunt editate prin implementare tableView: canEditRowAtIndexPath: din UITableViewDataSource protocol. Această metodă revine DA dacă rândul la indexPath este editabil și NU daca nu este. Implementarea este simplă, după cum puteți vedea mai jos. Fiecare rând este editabil, cu excepția primului rând și a locației selectate în prezent.

 - (BOOL) tableView: (UITableView *) tableView canEditRowAtIndexPath: (NSIndexPath *) indexPath if (indexPath.row == 0) retur NO;  // Preluare locație NSDictionary * location = [auto.locations objectAtIndex: (indexPath.row - 1)]; retur! [auto esteCurrentLocation: locație]; 

Pentru a verifica dacă Locație este locația curentă, folosim o altă metodă de ajutor, isCurrentLocation:, în care preiați locația curentă și comparați coordonatele locațiilor. Ar fi fost mai bine (și mai ușor) dacă am fi atribuit un identificator unic pentru fiecare locație stocată în baza de date implicită a utilizatorului. Nu numai că ar fi mai ușor să comparăm locațiile, dar și ne-ar permite să stocăm identificatorul unic al locației curente în baza de date implicită a utilizatorului și să îl căutăm în matricea de locații. Problema cu implementarea actuală este că locațiile cu aceleași coordonate nu pot fi distinse una de cealaltă.

 - (BOOL) este locația curentă: (NSDictionary *) locația // Returnați locația curentă NSDictionary * currentLocation = [[NSUserDefaults standardUserDefaults] objectForKey: MTRainUserDefaultsLocation]; dacă [[locație [MTLocationKeyLatitude] doubleValue] == [actualLocationKeyLatitude] doubleValue] && [locație [MTLocationKeyLongitude] doubleValue] == [actuală locație [MTLocationKeyLongitude] doubleValue]) return YES;  retur NO; 

Când utilizatorul întrerupe butonul de ștergere al unui rând de vizualizare de tabel, sursa de date din tabel este trimisă a tableView: commitEditingStyle: forRowAtIndexPath: mesaj. În această metodă, trebuie să actualizăm (1) sursa de date, (2) să salvăm schimbările în baza de date implicită a utilizatorilor și (3) să actualizăm vizualizarea tabelului. Dacă editingStyle este egal cu UITableViewCellEditingStyleDelete, eliminăm locația din locații array și să stocheze matricea actualizată în baza de date implicită a utilizatorului. De asemenea, ștergem rândul din vizualizarea tabelului pentru a reflecta modificarea sursei de date.

 - (void) tableView: (UITableView *) tableView commitEditingStyle: (UITableViewCellEditingStyle) editingStyle forRowAtIndexPath: (NSIndexPath *) indexPath if (editingStyle ==UITableViewCellEditingStyleDelete) // Update Locații [self.locations removeObjectAtIndex: (indexPath.row - 1)] ; // Actualizare setări implicite utilizator [[NSUserDefaults standardUserDefaults] setObject: auto.locații pentruKey: MTRainUserDefaultsLocations]; // Actualizați tabela de vizualizare [tableView deleteRowsAtIndexPaths: @ [indexPath] withRowAnimation: UITableViewRowAnimationTop]; 

Pentru a comuta stilul de editare al tabelului, trebuie să adăugăm un buton de editare la interfața cu utilizatorul. Creați o priză pentru butonul din MTLocationsViewController.h și o acțiune numită editLocations: în MTLocationsViewController.m. În editLocations:, vom comuta stilul de editare al tabelului de tabel.

 #import  @protocol MTLocationsViewControllerDelegate; @ MTLocationsViewController interfață: UIViewController  @property (slab, nonatomic) id delega; @property (slab, nonatomic) IBOutlet UITableView * tableView; @property (slab, nonatomic) IBOutlet UIBarButtonItem * editButton; @end @ protocol MTLocationsViewControllerDelegate  - (void) controlerShouldAddCurrentLocation: (MTLocationsViewController *) controler; - (void) controler: (MTLocationsViewController *) controler didSelectLocation: (NSDictionary *) locație; @Sfârșit
 - (IBAction) edițieLocații: (id) expeditor [auto.tableView setEditing:! [Self.tableView isEditing] animat: YES]; 

Deschis MTLocationsViewController.xib, adăugați o bară de navigare în vizualizarea controlerului de vizualizare și adăugați un buton de modificare la bara de navigare. Conectați butonul de editare cu priza și acțiunea pe care am creat-o cu un moment în urmă.


Figura 4: Adăugarea unui buton de editare

S-ar putea să te întrebi de ce am creat o priză pentru butonul de editare. Motivul este că trebuie să putem modifica titlul butonului de editare de la Editați | × la Terminat, și invers, ori de câte ori se modifică stilul de editare al vizualizării tabelului. În plus, atunci când utilizatorul șterge ultima locație (cu excepția locației curente) în vizualizarea de tabelă, ar fi bine să comutați în mod automat stilul de editare al tabelului din tabel. Aceste caracteristici nu sunt greu de implementat, motiv pentru care le las la dispoziție ca un exercițiu. Dacă întâmpinați probleme sau aveți întrebări, nu ezitați să lăsați un comentariu în comentariile de mai jos acest articol.

Concluzie

Am integrat cu succes Prognoza API în aplicația noastră meteorologică. În tutorialul următor vom pune accent pe interfața cu utilizatorul și pe proiectarea aplicației.

Cod