Opprett en Weather App med Forecast - API Integration

I den første artikkelen i denne serien lagde vi grunnlaget for prosjektet ved å sette opp prosjektet og skape søknadens struktur. I denne artikkelen bruker vi AFNetworking-biblioteket til å samhandle med Forecast API.


Introduksjon

I første del av denne serien lagde vi grunnlaget for vårt værprogram. Brukere kan legge til sin nåværende plassering og bytte mellom steder. I denne opplæringen vil vi bruke AFNetworking-biblioteket til å spørre prognose-APIen for værdataene på den valgte plasseringen.

Hvis du vil følge med, må du ha en prognose API-nøkkel. Du kan skaffe en API-nøkkel ved å registrere deg som utvikler ved Forecast. Registreringen er gratis, så jeg oppfordrer deg til å prøve ut værmeldingen for Forecast. Du kan finne API-nøkkelen din nederst på dashbordet (figur 1).


Figur 1: Oppnå API-nøkkelen

1. Subclassing AFHTTPClient

Som jeg skrev tidligere i denne artikkelen, vil vi bruke AFNetworking bibliotek for å kommunisere med Forecast API. Det er flere alternativer når du arbeider med AFNetworking, men for å gjøre søknaden fremtidig bevis, velger vi AFHTTPClient klasse. Denne klassen er designet for å konsumere webtjenester, for eksempel prognose-API. Selv om vi bare får tilgang til ett API-endepunkt, er det fortsatt nyttig å benytte seg av AFHTTPClient som du vil lære om noen få øyeblikk.

Det anbefales å lage en AFHTTPClient underklasse for hver webtjeneste. Fordi vi allerede har lagt til AFNetworking til vårt prosjekt i den forrige opplæringen, kan vi umiddelbart starte underklasse AFHTTPClient.

Trinn 1: Opprett klassen

Opprett en ny Objective-C-klasse, navn den MTForecastClient, og gjør det til en underklasse av AFHTTPClient (figur 2).

Figur 2: Subclassing AFHTTPClient

Trinn 2: Opprette et Singleton-objekt

Vi vil adoptere singleton mønsteret for å gjøre det enklere å bruke MTForecastClient klasse i prosjektet vårt. Dette betyr at bare én forekomst av klassen er i live på en gang for hele applikasjonens levetid. Sjansen er at du allerede er kjent med singleton mønster som det er et vanlig mønster i mange objektorienterte programmeringsspråk. Ved første øyekast virker singleton-mønsteret veldig praktisk, men det er mange advarsler å passe på. Du kan lære mer om singletoner ved å lese denne gode artikkelen av Matt Gallagher.

Å lage en singleton-objekt er ganske grei i Objective-C. Begynn med å erklære en klassemetode i MTForecastClient.h å gi offentlig tilgang til singleton-objektet (se nedenfor).

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

Gjennomføringen av sharedClient kan se skremmende først, men det er ikke så vanskelig når du forstår hva som skjer. Vi erklærer først to statiske variabler, (1) predikat av type dispatch_once_t og (2) _sharedClient av type MTForecastClient. Som navnet tilsier, predikat er et predikat som vi bruker i kombinasjon med dispatch_once funksjon. Når du arbeider med en variabel av typen dispatch_once_t, Det er viktig at det erklæres statisk. Den andre variabelen, _sharedClient, vil lagre en referanse til singleton-objektet.

De dispatch_once funksjonen tar en peker til a dispatch_once_t struktur, predikat og en blokk. Skjønnheten i dispatch_once er at den vil utføre blokken en gang for programmets levetid, noe som er akkurat det vi ønsker. De dispatch_once funksjonen har ikke mange bruksområder, men dette er definitivt en av dem. I blokken som vi sender til dispatch_once, vi lager singleton-objektet og lagrer en referanse i _sharedClient. Det er tryggere å påberope seg Alloc og i det separat for å unngå en løpstilstand som potensielt kan føre til en dødlås. Euh ... hva? Du kan lese mer om nitty gritty detaljer på Stack Overflow.

 + (MTForecastClient *) sharedClient static dispatch_once_t predikat; statisk MTForecastClient * _sharedClient = nil; dispatch_once (& predicate, ^ _sharedClient = [selvtillit]; _sharedClient = [_sharedClient initWithBaseURL: [self baseURL]];); returnere _sharedClient; 

Det viktige å forstå om implementeringen av sharedClient klassemetode er at initialisatoren, initWithBaseURL:, er påkalt bare en gang. Singleton-objektet er lagret i _sharedClient statisk variabel, som returneres av sharedClient klassemetode.

Trinn 3: Konfigurere kunden

I sharedClient, vi påberoper initWithBaseURL:, som igjen påberoper seg baseURL, en annen klassemetode. I initWithBaseURL:, Vi angir en standard header, som betyr at klienten legger til denne overskriften til hver forespørsel som den sender. Dette er en av fordelene ved å jobbe med AFHTTPClient klasse. I initWithBaseURL:, Vi registrerer også en HTTP-operasjonsklasse ved å påkalle registerHTTPOperationClass:. AFNetworking-biblioteket inneholder en rekke spesialiserte operasjonsklasser. En av disse klassene er AFJSONRequestOperation klassen, som gjør det enkelt å samhandle med en JSON API. Fordi prognosen API returnerer et JSON-svar, vil AFJSONRequestOperation klassen er et godt valg. De registerHTTPOperationClass: Metoden fungerer som hvordan Register: forCellReuseIdentifier: av UITableView klassearbeid. Ved å fortelle klienten hvilken operasjonsklasse vi vil bruke for å samhandle med webtjenesten, vil det gi øyeblikkelig forekomster av denne klassen for oss under hetten. Hvorfor dette er nyttig vil bli klart om noen få øyeblikk.

 - (id) initWithBaseURL: (NSURL *) url self = [super initWithBaseURL: url]; hvis (selv) // Godta HTTP Header [self setDefaultHeader: @ "Accept" verdi: @ "application / json"]; // Registrer HTTP-operasjonsklasse [selvregistreringHTTPOperationClass: [AFJSONRequestOperation class]];  returner selv; 

Gjennomføringen av baseURL er ikke noe mer enn en praktisk metode for å bygge klientens grunnadresse. Basisadressen er nettadressen som klienten bruker for å nå webtjenesten. Det er nettadressen uten noen metodenavn eller parametere. Basisadressen for prognose-APIen er https://api.forecast.io/forecast//. API-nøkkelen er en del av nettadressen som du kan se. Dette kan virke usikkert og det er faktisk. Det er ikke vanskelig for noen å ta tak i API-nøkkelen, så det er tilrådelig å jobbe med en proxy for å maskere API-nøkkelen. Fordi denne tilnærmingen er litt mer involvert, vil jeg ikke dekke dette aspektet i denne serien.

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

Du har kanskje lagt merke til i gjennomføringen av baseURL at jeg har brukt en annen streng konstant for å lagre API-nøkkelen. Dette kan virke unødvendig siden vi bare bruker API-nøkkelen på ett sted. Det er imidlertid god praksis å lagre programdata på ett sted eller i en eiendomsliste.

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

Trinn 4: Legge til en hjelpemetode

Før vi går videre, vil jeg gjerne utvide MTForecastClient klassen ved å legge til en hjelpemiddel eller bekvemmelighetsmetode som gjør det enklere å spørre prognoseprogrammet. Denne bekvemmelighetsmetoden vil akseptere et sted og en ferdigstillingsblokk. Fullføringsblokken blir utført når forespørselen er ferdig. For å gjøre det lettere å arbeide med blokker, anbefales det å deklarere en egendefinert blokktype som vist nedenfor. Hvis du fortsatt føler deg ubehagelig med å bruke blokker, anbefaler jeg at du leser denne flotte artikkelen av Akiel Khan.

Blokken tar to argumenter, (1) en boolean som indikerer om spørringen var vellykket og (2) en ordbok med svaret fra spørringen. Den praktiske metoden, requestWeatherForCoordinate: ferdigstillelse:, tar koordinatene til et sted (CLLocationCoordinate2D) og en ferdigstillingsblokk. Ved å bruke en ferdigstillingsblokk kan vi unngå å opprette en tilpasset delegatprotokoll eller gå tilbake til bruk av varsler. Blokker er en perfekt passform for denne typen scenario.

 #import "AFHTTPClient.h" typedef void (^ MTForecastClientCompletionBlock) (BOOL suksess, NSDictionary * respons); @interface MTForecastClient: AFHTTPClient #pragma mark - #pragma markert Delt Client + (MTForecastClient *) sharedClient; #pragma mark - #pragma marker Instance Methods - (void) requestWeatherForCoordinate: (CLLocationCoordinate2D) koordinat fullføring: (MTForecastClientCompletionBlock) fullføring; @slutt

I requestWeatherForCoordinate: ferdigstillelse:, vi påberoper getPath: suksess: fiasko:, en metode erklært i AFHTTPClient. Det første argumentet er banen som legges til grunnwebadressen som vi opprettet tidligere. Det andre og tredje argumentet er blokker som utføres når forespørselen lykkes og mislykkes. Suksess- og feilblokkene er ganske enkle. Hvis en ferdigstillingsblokk ble sendt til requestWeatherForCoordinate: ferdigstillelse:, vi utfører blokken og bestått en boolsk verdi og svarordlisten (eller nil i feilblokken). I feilblokken logger vi feilen fra feilblokken til konsollen for å lette feilsøkingen.

 - (void) requestWeatherForCoordinate: (CLLocationCoordinate2D) koordinat fullføring: (MTForecastClientCompletionBlock) fullføring NSString * path = [NSString stringWithFormat: @ "% f,% f", koordinat.latitude, koordinatlengde]; [self getPath: sti parametere: null suksess: ^ (AFHTTPRequestOperation * operasjon, id respons) if (ferdigstillelse) fullføring (JA, svar);  feil: ^ (AFHTTPRequestOperation * -operasjon, NSError * -feil) if (ferdigstillelse) fullføring (NO, null); NSLog (@ "Kunne ikke hente værdata på grunn av feil% @ med brukerinfo% @.", Feil, error.userInfo); ]; 

Du lurer kanskje på hva respons objekt i suksessblokkene er eller referanser. Selv om prognosen API returnerer et JSON-svar, vil respons objekt i suksessblokken er en NSDictionary forekomst. Fordelen med å jobbe med AFJSONHTTPRequestOperation klassen, som vi registrerte oss i initWithBaseURL:, er at den aksepterer JSON-responsen og automatisk oppretter et objekt fra svardata, en ordbok i dette eksemplet.


2. Spørring av prognosen API

Trinn 1: Endre setLocation:

Bevæpnet med MTForecastClient klassen, er det på tide å spørre prognoseprogrammet og hente værdataene for den valgte plasseringen. Det mest passende stedet å gjøre dette er i setLocation: metode av MTWeatherViewController klasse. Endre setLocation: metode som vist nedenfor. Som du kan se, er alt vi gjør, påkall fetchWeatherData, en annen hjelpemetode.

 - (void) setLocation: (NSDictionary *) sted if (_location! = location) _location = location; // Oppdater brukerstandarder NSUserDefaults * ud = [NSUserDefaults standardUserDefaults]; [ut setObject: location forKey: MTRainUserDefaultsLocation]; [synkronisere] // Postvarsling NSNotification * notification1 = [NSNotification notificationWithName: MTRainLocationDidChangeNotification objekt: self userInfo: location]; [[NSNotificationCenter defaultCenter] postNotification: notification1]; // Oppdater Vis [self updateView]; // Request Location [self fetchWeatherData]; 

Har du noen gang lurt på hvorfor jeg bruker så mange hjelpemetoder i koden min? Årsaken er enkel. Ved å pakke inn funksjonalitet i hjelpemetoder er det veldig enkelt å gjenbruke koden på ulike steder i et prosjekt. Den største fordelen er imidlertid at den hjelper kamp kode duplisering. Kode duplisering er noe du bør alltid forsøke å unngå så mye som mulig. En annen fordel ved å bruke hjelpemetoder er at det gjør koden mye mer lesbar. Ved å lage metoder som gjør en ting og gir et godt valgt metodenavn, er det lettere å raskt lese og behandle koden din.

Trinn 2: Sende forespørselen

Det er på tide å sette SVProgressHUD bibliotek å bruke. Jeg liker virkelig dette biblioteket fordi det er så enkelt å bruke uten å knytte prosjektets kodebase. Ta en titt på implementeringen av fetchWeatherData under. Vi starter med å vise fremdriften HUD og deretter passere en struktur (CLLocationCoordinate2D) til enkelhetsmetoden vi opprettet tidligere, requestWeatherForCoordinate: ferdigstillelse:. I ferdigstillingsblokken skjuler vi fremdriften HUD og logger svaret på konsollen.

 - (void) fetchWeatherData // Vis fremgang HUD [SVProgressHUD showWithMaskType: SVProgressHUDMaskTypeGradient]; // Query Forecast API dobbelt lat = [[_location objectForKey: MTLocationKeyLatitude] doubleValue]; dobbelt lng = [[_location objectForKey: MTLocationKeyLongitude] doubleValue]; [[MTForecastClient sharedClient] requestWeatherForCoordinate: CLLocationCoordinate2DMake (lat, lng) fullføring: ^ (BOOL suksess, NSDictionary * respons) // Dismiss Progress HUD [SVProgressHUD avskedig]; NSLog (@ "Response>% @", svar); ]; 

Før du bygger og kjører din søknad, importerer du headerfilen til MTForecastClient klasse i MTWeatherViewController.m.

 #import "MTWeatherViewController.h" #import "MTForecastClient.h" @interface MTWeatherViewController ()  BOOL _locationFound;  @property (sterk, ikke-atomisk) NSDictionary * sted; @property (sterk, ikkeatomisk) CLLocationManager * locationManager; @slutt

Hva skjer når enheten ikke er koblet til nettet? Har du tenkt på det scenariet? Når det gjelder brukeropplevelse, er det god praksis å varsle brukeren når programmet ikke kan be om data fra prognose-API. La meg vise hvordan du gjør dette med AFNetworking-biblioteket.


3. Reachability

Det finnes en rekke biblioteker som gir denne funksjonaliteten, men vi vil holde fast ved AFNetworking. Apple gir også prøvekode, men det er litt utdatert og støtter ikke ARC.

AFNetworking har virkelig omfavnet blokker, noe som definitivt er en av grunnene til at dette biblioteket har blitt så populært. Overvåking for nåbarhetsendringer er like enkelt som å sende en blokk til setReachabilityStatusChangeBlock:, en annen metode for AFHTTPClient klasse. Blokken blir utført hver gang nåbarhetsstatusen endres, og den aksepterer et enkelt argument av typen AFNetworkReachabilityStatus. Ta en titt på den oppdaterte initWithBaseURL: metode av MTForecastClient klasse.

 - (id) initWithBaseURL: (NSURL *) url self = [super initWithBaseURL: url]; hvis (selv) // Godta HTTP Header [self setDefaultHeader: @ "Accept" verdi: @ "application / json"]; // Registrer HTTP-operasjonsklasse [selvregistreringHTTPOperationClass: [AFJSONRequestOperation class]]; // Reachability __weak typeof (self) weakSelf = selv; [self setReachabilityStatusChangeBlock: ^ (AFNetworkReachabilityStatus status) [[NSNotificationCenter defaultCenter] postNotificationName: MTRainReachabilityStatusDidChangeNotification object: weakSelf]; ];  returner selv; 

For å unngå en beholdningssyklus, sender vi en svak referanse til singleton-objektet i blokken som vi sender til setReachabilityStatusChangeBlock:. Selv om du bruker ARC i prosjektene dine, må du fortsatt være oppmerksom på subtile minneproblemer som dette. Navnet på varselet vi legger inn er en annen streng som konstant er oppgitt i MTConstants.h / .m.

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

Årsaken til at du legger inn et varsel i nåværende statusendringsblokk er å gjøre det lettere for andre deler av programmet å oppdatere når enhetens nåbarhet endres. For å være sikker på at MTWeatherViewController klassen blir varslet om nåbarhetsendringer, forekomster av klassen legges til som observatør for meldingene som er sendt av prognosen klienten som vist nedenfor.

 - (id) initWithNibName: (NSString *) nibNameOrNil bundle: (NSBundle *) nibBundleOrNil self = [super initWithNibName: nibNameOrNil bundle: nibBundleOrNil]; hvis (selv) // Initialiser Location Manager self.locationManager = [[CLLocationManager alloc] init]; // Konfigurer Location Manager [self.locationManager setDelegate: self]; [self.locationManager setDesiredAccuracy: kCLLocationAccuracyKilometer]; // Legg til Observer NSNotificationCenter * nc = [NSNotificationCenter defaultCenter]; [nc addObserver: selvvelger: @selector (reachabilityStatusDidChange :) navn: MTRainReachabilityStatusDidChangeNotification object: null];  returner selv; 

Dette betyr også at vi må fjerne forekomsten som observatør i dealloc metode. Dette er en detalj som ofte overses.

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

Gjennomføringen av reachabilityStatusDidChange: er ganske grunnleggende for øyeblikket. Vi oppdaterer implementeringen når vi oppretter applikasjonens brukergrensesnitt.

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

4. Forfriskende data

Før vi pakker inn dette innlegget, vil jeg legge til to ekstra funksjoner, (1) hente værdata når programmet blir aktivt og (2) legge til muligheten til å manuelt oppdatere værdata. Vi kunne implementere en timer som henter friske data hver time eller så, men dette er ikke nødvendig for et værprogram, etter min mening. De fleste brukere vil starte programmet, ta en titt på været og legg applikasjonen i bakgrunnen. Det er derfor bare nødvendig å hente nye data når brukeren starter programmet. Dette betyr at vi må lytte til UIApplicationDidBecomeActiveNotification varsler i MTWeatherViewController klasse. Som vi gjorde for å overvåke nåbarhetsendringer, legger vi tilfeller av klassen som observatører av varslinger av typen UIApplicationDidBecomeActiveNotification.

 - (id) initWithNibName: (NSString *) nibNameOrNil bundle: (NSBundle *) nibBundleOrNil self = [super initWithNibName: nibNameOrNil bundle: nibBundleOrNil]; hvis (selv) // Initialiser Location Manager self.locationManager = [[CLLocationManager alloc] init]; // Konfigurer Location Manager [self.locationManager setDelegate: self]; [self.locationManager setDesiredAccuracy: kCLLocationAccuracyKilometer]; // Legg til Observer NSNotificationCenter * nc = [NSNotificationCenter defaultCenter]; [nc addObserver: selvvelger: @selector (applicationDidBecomeActive :) navn: UIApplicationDidBecomeActiveNotification objekt: null]; [nc addObserver: selvvelger: @selector (reachabilityStatusDidChange :) navn: MTRainReachabilityStatusDidChangeNotification object: null];  returner selv; 

I applicationDidBecomeActive:, vi bekrefter det plassering er satt (ikke nil) fordi dette ikke alltid vil være sant. Hvis plasseringen er gyldig, henter vi værdataene.

 - (ugyldig) applicationDidBecomeActive: (NSNotification *) varsel hvis (self.location) [self fetchWeatherData]; 

Jeg har også endret fetchWeatherData å bare spørre prognose API hvis enheten er koblet til nettet.

 - (void) fetchWeatherData if ([[MTForecastClient sharedClient] networkReachabilityStatus] == AFNetworkReachabilityStatusNotReachable) returnere; // Vis fremgang HUD [SVProgressHUD showWithMaskType: SVProgressHUDMaskTypeGradient]; // Query Forecast API dobbelt lat = [[_location objectForKey: MTLocationKeyLatitude] doubleValue]; dobbelt lng = [[_location objectForKey: MTLocationKeyLongitude] doubleValue]; [[MTForecastClient sharedClient] requestWeatherForCoordinate: CLLocationCoordinate2DMake (lat, lng) fullføring: ^ (BOOL suksess, NSDictionary * respons) // Dismiss Progress HUD [SVProgressHUD avskedig]; // NSLog (@ "Response>% @", svar); ]; 

La oss legge til en knapp i visningsregulatoren som brukeren kan trykke for å manuelt oppdatere værdataene. Opprett et uttak i MTWeatherViewController.h og opprett en forfriske: handling i MTWeatherViewController.m.

 #importere  #import "MTLocationsViewController.h" @interface MTWeatherViewController: UIViewController  @property (svak, ikkeatomisk) IBOutlet UILabel * labelLocation; @property (svak, ikkeatomisk) IBOutlet UIButton * buttonRefresh; @slutt
 - (IBAction) oppdatering: (id) avsender hvis (self.location) [self fetchWeatherData]; 

Åpen MTWeatherViewController.xib, legg til en knapp i visningsregulatorens visning med en tittel på Forfriske, og koble utløpet og handlingen med knappen (figur 3). Årsaken til å opprette et uttak for knappen er å kunne deaktivere det når det ikke er noen nettverkstilkobling tilgjengelig. For at dette skal fungere, må vi oppdatere reachabilityStatusDidChange: metode som vist nedenfor.

Figur 3: Legge til en Refresh-knapp
 - (void) reachabilityStatusDidChange: (NSNotification *) varsel MTForecastClient * forecastClient = [varselobjekt]; NSLog (@ "Reachability Status>% i", prognoseClient.networkReachabilityStatus); // Oppdater Refresh Button self.buttonRefresh.enabled = (forecastClient.networkReachabilityStatus! = AFNetworkReachabilityStatusNotReachable); 

Det er ikke nødvendig å deaktivere oppdateringsknappen midlertidig når en forespørsel blir behandlet i fetchWeatherData fordi fremdriften HUD legger til et lag øverst på visningskontrollens visning som forhindrer at brukeren trykker på knappen mer enn en gang. Bygg og kjør programmet for å teste alt ut.


Bonus: Fjerne plasseringer

En leser spurte meg hvordan du sletter steder fra listen, så jeg inkluderer det her for fullstendighetens skyld. Det første vi må gjøre er å fortelle tabellvisningen hvilke rader som kan redigeres ved å implementere Tableview: canEditRowAtIndexPath: av UITableViewDataSource protokoll. Denne metoden returnerer JA hvis rad på indexPath kan redigeres og NEI hvis det ikke er det. Gjennomføringen er enkel som du kan se nedenfor. Hver rad kan redigeres bortsett fra den første raden og den valgte plasseringen.

 - (BOOL) tableView: (UITableView *) tableView canEditRowAtIndexPath: (NSIndexPath *) indexPath if (indexPath.row == 0) return NO;  // Hent plassering NSDictionary * location = [self.locations objectAtIndex: (indexPath.row - 1)]; returnere! [selv er nåværende beliggenhet: sted]; 

For å sjekke om plassering er den nåværende plasseringen, bruker vi en annen hjelpemetode, isCurrentLocation:, der vi henter nåværende posisjon og sammenligner plasseringskoordinatene. Det ville vært bedre (og enklere) hvis vi hadde tildelt en unik identifikator til hvert sted lagret i brukerstandarddatabasen. Ikke bare vil det gjøre det enklere å sammenligne steder, men det vil også gi oss mulighet til å lagre den nåværende plasseringenes unike identifikator i brukerstandarddatabasen og se den opp i en rekke steder. Problemet med den nåværende implementeringen er at steder med nøyaktig samme koordinater ikke kan skilles fra hverandre.

 - (BOOL) isCurrentLocation: (NSDictionary *) plassering // Hent nåværende posisjon NSDictionary * currentLocation = [[NSUserDefaults standardUserDefaults] objectForKey: MTRainUserDefaultsLocation]; hvis ([plassering [MTLocationKeyLatitude] doubleValue] == [currentLocation [MTLocationKeyLatitude] doubleValue] && [plassering [MTLocationKeyLongitude] doubleValue] == [currentLocation [MTLocationKeyLongitude] doubleValue]) return YES;  returnere NO; 

Når brukeren tapper sletteknappen på en tabellvisningsrad, sendes datakilden for tabellvisning a Tableview: commitEditingStyle: forRowAtIndexPath: budskap. I denne metoden trenger vi (1) å oppdatere datakilden, (2) lagre endringene til brukerstandarddatabasen, og (3) oppdatere tabellvisningen. Hvis editingStyle er lik UITableViewCellEditingStyleDelete, Vi fjerner plasseringen fra steder array og lagre den oppdaterte gruppen i brukerstandarddatabasen. Vi sletter også raden fra tabellvisningen for å gjenspeile endringen i datakilden.

 - (void) tableView: (UITableView *) tableView commitEditingStyle: (UITableViewCellEditingStyle) editingStyle forRowAtIndexPath: (NSIndexPath *) indexPath if (editingStyle == UITableViewCellEditingStyleDelete) // Oppdateringssteder [self.locations removeObjectAtIndex: (indexPath.row - 1)] ; // Oppdater brukerstandarder [[NSUserDefaults standardUserDefaults] setObject: self.locations forKey: MTRainUserDefaultsLocations]; // Oppdater tabellvisning [tableView deleteRowsAtIndexPaths: @ [indexPath] withRowAnimation: UITableViewRowAnimationTop]; 

Hvis du vil bytte tabellvisningens redigeringsstil, må vi legge til en redigerings-knapp i brukergrensesnittet. Opprett et uttak for knappen i MTLocationsViewController.h og en handling kalt editLocations: i MTLocationsViewController.m. I editLocations:, vi bytter tabellvisningens redigeringsstil.

 #importere  @protocol MTLocationsViewControllerDelegate; @interface MTLocationsViewController: UIViewController  @property (svakt, ikkeatomisk) id delegat; @property (svak, ikkeatomisk) IBOutlet UITableView * tableView; @property (svak, ikkeatomisk) IBOutlet UIBarButtonItem * editButton; @end @protocol MTLocationsViewControllerDelegate  - (void) controllerShouldAddCurrentLocation: (MTLocationsViewController *) kontrolleren; - (void) controller: (MTLocationsViewController *) kontrolleren didSelectLocation: (NSDictionary *) sted; @slutt
 - (IBAction) editLocations: (id) avsender [self.tableView setEditing:! [Self.tableView isEditing] animated: YES]; 

Åpen MTLocationsViewController.xib, legg til en navigeringslinje i visningsregulatorens visning, og legg til en redigeringsknapp i navigasjonslinjen. Koble redigeringsknappen med uttaket og handlingen som vi opprettet for øyeblikket.


Figur 4: Legge til en redigerings knapp

Du lurer kanskje på hvorfor vi opprettet et uttak for redigeringsknappen. Årsaken er at vi må kunne endre tittelen på redigeringsknappen fra Redigere til Ferdig, og omvendt, når redigeringsstilen i tabellvisningen endres. I tillegg, når brukeren sletter den siste plasseringen (unntatt den nåværende plasseringen) i tabellvisningen, ville det være hyggelig å automatisk bytte tabellvisningens redigeringsstil. Disse funksjonene er ikke vanskelig å implementere, og derfor forlater jeg dem som en øvelse. Hvis du får problemer eller har spørsmål, kan du legge inn en kommentar i kommentarene under denne artikkelen.

Konklusjon

Vi har vellykket integrert prognose-API i vårt værprogram. I neste veiledning vil vi implementere fokus på brukergrensesnittet og utformingen av applikasjonen.