Crear una aplicación de clima con pronóstico - Integración API

En el primer artículo de esta serie, sentamos las bases del proyecto configurando el proyecto y creando la estructura de la aplicación. En este artículo, aprovechamos la biblioteca AFNetworking para interactuar con la API de Forecast.


Introducción

En la primera entrega de esta serie, sentamos las bases de nuestra aplicación meteorológica. Los usuarios pueden agregar su ubicación actual y cambiar entre ubicaciones. En este tutorial, usaremos la biblioteca de AFNetworking para solicitar a la API de Pronóstico los datos meteorológicos de la ubicación seleccionada actualmente.

Si desea seguir adelante, necesitará una clave de API de pronóstico. Puede obtener una clave API al registrarse como desarrollador en Forecast. La inscripción es gratuita, por lo que le invito a probar el servicio meteorológico Forecast. Puede encontrar su clave API en la parte inferior de su panel de control (figura 1).


Figura 1: Obtención de su clave API

1. Subclase AFHTTPClient

Como escribí anteriormente en este artículo, usaremos el Red de redes Biblioteca para comunicarse con la API de Forecast. Hay varias opciones al trabajar con AFNetworking, pero para que nuestra aplicación sea una prueba de futuro, optaremos por la AFHTTPClient clase. Esta clase está diseñada para consumir servicios web, como la API Forecast. Aunque solo accederemos a un punto final de API, sigue siendo útil hacer uso de AFHTTPClient Como aprenderás en unos instantes..

Se recomienda crear un AFHTTPClient subclase para cada servicio web. Debido a que ya agregamos AFNetworking a nuestro proyecto en el tutorial anterior, podemos comenzar a crear subclases inmediatamente. AFHTTPClient.

Paso 1: Crea la clase

Crear una nueva clase Objective-C, nombrarla MTForecastClient, y convertirlo en una subclase de AFHTTPClient (Figura 2).

Figura 2: Subclasificando AFHTTPClient

Paso 2: Creando un objeto Singleton

Adoptaremos el patrón de singleton para facilitar el uso del MTForecastClient Clase en nuestro proyecto. Esto significa que solo una instancia de la clase está activa a la vez durante la vida útil de la aplicación. Lo más probable es que ya esté familiarizado con el patrón de singleton ya que es un patrón común en muchos lenguajes de programación orientados a objetos. A primera vista, el patrón de singleton parece muy conveniente, pero hay una serie de advertencias a tener en cuenta. Puede aprender más sobre los singletons leyendo este excelente artículo de Matt Gallagher.

Crear un objeto singleton es bastante sencillo en Objective-C. Comience declarando un método de clase en MTForecastClient.h para proporcionar acceso público al objeto singleton (ver más abajo).

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

La implementación de sharedClient Puede parecer desalentador al principio, pero no es tan difícil una vez que entiendes lo que está pasando. Primero declaramos dos variables estáticas, (1) predicado de tipo dispatch_once_t y 2) _sharedClient de tipo MTForecastClient. Como su nombre lo indica, predicado es un predicado que utilizamos en combinación con el dispatch_once función. Cuando se trabaja con una variable de tipo dispatch_once_t, Es importante que se declare estáticamente. La segunda variable, _sharedClient, almacenará una referencia al objeto singleton.

los dispatch_once función lleva un puntero a una dispatch_once_t Estructura, el predicado, y un bloque. La belleza de dispatch_once es que ejecutará el bloque una vez durante la vida útil de la aplicación, que es exactamente lo que queremos. los dispatch_once La función no tiene muchos usos, pero este es definitivamente uno de ellos. En el bloque al que pasamos. dispatch_once, Creamos el objeto singleton y almacenamos una referencia en. _sharedClient. Es más seguro invocar asignar y en eso por separado para evitar una condición de carrera que podría conducir a un punto muerto. Euh ... ¿qué? Puedes leer más acerca de los detalles esenciales del desbordamiento de pila.

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

Lo importante a entender acerca de la implementación del sharedClient método de clase es que el inicializador, initWithBaseURL:, Se invoca una sola vez. El objeto singleton se almacena en el _sharedClient variable estática, que es devuelta por el sharedClient método de clase.

Paso 3: Configurando el cliente

En sharedClient, invocamos initWithBaseURL:, que a su vez invoca baseURL, Otro método de clase. En initWithBaseURL:, establecemos un encabezado predeterminado, lo que significa que el cliente agrega este encabezado a cada solicitud que envía. Esta es una de las ventajas de trabajar con el AFHTTPClient clase. En initWithBaseURL:, También registramos una clase de operación HTTP invocando registerHTTPOperationClass:. La biblioteca AFNetworking proporciona una serie de clases de operaciones especializadas. Una de estas clases es la AFJSONRequestOperation clase, lo que hace que la interacción con una API JSON sea muy fácil. Debido a que la API de pronóstico devuelve una respuesta JSON, AFJSONRequestOperation La clase es una buena opción. los registerHTTPOperationClass: método funciona de manera similar a como el registerClass: forCellReuseIdentifier: del UITableView trabajos de clase Al decirle al cliente qué clase de operación queremos usar para interactuar con el servicio web, creará instancias de esa clase para nosotros bajo el capó. Por qué esto es útil se aclarará en unos instantes..

 - (id) initWithBaseURL: (NSURL *) url self = [super initWithBaseURL: url]; if (self) // Aceptar HTTP Header [self setDefaultHeader: @ "Accept" value: @ "application / json"]; // Registrar la clase de operación HTTP [self registerHTTPOperationClass: [AFJSONRequestOperation class]];  devuélvete a ti mismo; 

La implementación de baseURL no es más que un método conveniente para construir la URL base del cliente. La URL base es la URL que el cliente utiliza para acceder al servicio web. Es la URL sin ningún nombre de método o parámetros. La URL base para la API de pronóstico es https://api.forecast.io/forecast//. La clave API es parte de la URL como puedes ver. Esto puede parecer inseguro y en realidad lo es. No es difícil para alguien agarrar la clave API, por lo que es recomendable trabajar con un proxy para enmascarar la clave API. Debido a que este enfoque es un poco más complicado, no cubriré este aspecto en esta serie.

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

Es posible que hayas notado en la implementación de baseURL que he usado otra constante de cadena para almacenar la clave API. Esto puede parecer innecesario ya que solo usamos la clave API en una ubicación. Sin embargo, es una buena práctica almacenar los datos de la aplicación en una ubicación o en una lista de propiedades..

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

Paso 4: Agregar un método de ayuda

Antes de continuar, me gustaría extender el MTForecastClient clase agregando un método auxiliar o de conveniencia que facilitará la consulta de la API de pronóstico. Este método de conveniencia aceptará una ubicación y un bloque de finalización. El bloque de finalización se ejecuta cuando finaliza la solicitud. Para facilitar el trabajo con bloques, se recomienda declarar un tipo de bloque personalizado como se muestra a continuación. Si aún te sientes incómodo con los bloques, te recomiendo que leas este excelente artículo de Akiel Khan..

El bloque toma dos argumentos, (1) un booleano que indica si la consulta fue exitosa y (2) un diccionario con la respuesta de la consulta. El método de conveniencia, requestWeatherForCoordinate: finalización:, toma las coordenadas de una ubicación (CLLocationCoordinate2D) y un bloque de finalización. Al usar un bloque de finalización, podemos evitar crear un protocolo de delegado personalizado o recurrir al uso de notificaciones. Los bloques son un ajuste perfecto para este tipo de escenario.

 #import "AFHTTPClient.h" typedef void (^ MTForecastClientCompletionBlock) (éxito BOOL, respuesta NSDictionary *); @interface MTForecastClient: AFHTTPClient #pragma mark - #pragma mark Shared Client + (MTForecastClient *) sharedClient; #pragma mark - #pragma mark Métodos de instancia - (void) requestWeatherForCoordinate: (CLLocationCoordinate2D) completado de coordenadas: (MTForecastClientCompletionBlock) completado; @fin

En requestWeatherForCoordinate: finalización:, invocamos getPath: éxito: fracaso, un método declarado en AFHTTPClient. El primer argumento es la ruta que se agrega a la URL base que creamos anteriormente. El segundo y tercer argumento son bloques que se ejecutan cuando la solicitud se realiza correctamente y falla, respectivamente. Los bloques de éxito y fracaso son bastante simples. Si un bloque de finalización fue pasado a requestWeatherForCoordinate: finalización:, ejecutamos el bloque y pasamos un valor booleano y el diccionario de respuesta (o nulo en el bloque de falla). En el bloque de falla, registramos el error del bloque de falla en la consola para facilitar la depuración.

 - (void) requestWeatherForCoordinate: (CLLocationCoordinate2D) coordenada completada: (MTForecastClientCompletionBlock) terminación NSString * path = [NSString stringWithFormat: @ "% f,% f", coordenadas.latitud, coordenada.longitud]; [self getPath: parámetros de ruta: éxito nulo: ^ (operación AFHTTPRequestOperation *, respuesta de identificación) si (finalización) finalización (SÍ, respuesta);  fallo: ^ (operación AFHTTPRequestOperation *, error NSError *) si (finalización) finalización (NO, nil); NSLog (@ "No se pueden obtener datos meteorológicos debido al error% @ con la información del usuario% @.", Error, error.userInfo); ]; 

Usted se estará preguntando qué respuesta Objeto en los bloques de éxito es o referencias. Aunque la API de Forecast devuelve una respuesta JSON, respuesta objeto en el bloque de éxito es una NSDiccionario ejemplo. El beneficio de trabajar con el AFJSONHTTPRequestOperation clase, que nos registramos en initWithBaseURL:, es que acepta la respuesta JSON y crea automáticamente un objeto a partir de los datos de respuesta, un diccionario en este ejemplo.


2. Consultar la API de pronóstico

Paso 1: Modificar escoger localización:

Armado con el MTForecastClient En esta clase, es hora de consultar la API de pronóstico y obtener los datos del clima para la ubicación seleccionada actualmente. El lugar más adecuado para hacer esto es en el escoger localización: método de la MTWeatherViewController clase. Enmendar el escoger localización: método como se muestra a continuación. Como puedes ver, todo lo que hacemos es invocar. fetchWeatherData, otro método de ayuda.

 - (void) setLocation: (NSDictionary *) location if (_location! = location) _location = location; // Actualizar valores predeterminados de usuario NSUserDefaults * ud = [NSUserDefaults standardUserDefaults]; [ud setObject: location forKey: MTRainUserDefaultsLocation]; [ud sincronizar]; // Notificación posterior a la notificación NSNotification * notification1 = [NSNotification notificationWithName: MTRainLocationDidChangeNotification object: self userInfo: location]; [[NSNotificationCenter defaultCenter] postNotification: notification1]; // Vista de actualización [self updateView]; // Ubicación de la solicitud [self fetchWeatherData]; 

¿Alguna vez te has preguntado por qué uso tantos métodos de ayuda en mi código? La razón es simple. Al ajustar la funcionalidad en los métodos de ayuda, es muy fácil reutilizar el código en varios lugares de un proyecto. El principal beneficio, sin embargo, es que ayuda a combatir la duplicación de códigos. La duplicación de código es algo que siempre debe tratar de evitar tanto como sea posible. Otra ventaja de usar métodos de ayuda es que hace que su código sea mucho más legible. Al crear métodos que hacen una cosa y proporcionar un nombre de método bien elegido, es más fácil leer y procesar su código rápidamente.

Paso 2: Enviando la Solicitud

Es hora de poner el SVProgressHUD biblioteca para usar. Realmente me gusta esta biblioteca porque es muy fácil de usar sin saturar el código base del proyecto. Eche un vistazo a la implementación de fetchWeatherData abajo. Comenzamos mostrando el progreso de HUD y luego aprobamos una estructura (CLLocationCoordinate2D) al método de conveniencia que creamos anteriormente, requestWeatherForCoordinate: finalización:. En el bloque de finalización, ocultamos el HUD de progreso y registramos la respuesta en la consola.

 - (void) fetchWeatherData // Show HUD de progreso [SVProgressHUD showWithMaskType: SVProgressHUDMaskTypeGradient]; // Query Forecast API double lat = [[_location objectForKey: MTLocationKeyLatitude] doubleValue]; doble lng = [[_location objectForKey: MTLocationKeyLongitude] doubleValue]; [[MTForecastClient sharedClient] requestWeatherForCoordinate: CLLocationCoordinate2DMake (lat, lng) complete: ^ (Exito BOOL, NSDictionary * response) // Dismiss Progress HUD [SVProgressHUD despedido]; NSLog (@ "Respuesta>% @", respuesta); ]; 

Antes de crear y ejecutar su aplicación, importe el archivo de encabezado de MTForecastClient clase en MTWeatherViewController.m.

 #importar "MTWeatherViewController.h" #importar "MTForecastClient.h" @interface MTWeatherViewController ()  BOOL _locationFound;  @ propiedad (fuerte, no atómica) ubicación NSDictionary *; @property (strong, nonatomic) CLLocationManager * locationManager; @fin

¿Qué sucede cuando el dispositivo no está conectado a la web? ¿Has pensado en ese escenario? En términos de la experiencia del usuario, es una buena práctica notificar al usuario cuando la aplicación no puede solicitar datos de la API de Pronóstico. Permítame mostrarle cómo hacer esto con la biblioteca de AFNetworking.


3. Alcance

Hay una serie de bibliotecas que proporcionan esta funcionalidad, pero nos mantendremos con AFNetworking. Apple también proporciona código de ejemplo, pero está un poco desactualizado y no admite ARC.

AFNetworking realmente ha abrazado los bloques, lo que definitivamente es una de las razones por las que esta biblioteca se ha vuelto tan popular. Monitorear los cambios de accesibilidad es tan simple como pasar un bloque a setReachabilityStatusChangeBlock:, otro método de la AFHTTPClient clase. El bloque se ejecuta cada vez que cambia el estado de accesibilidad y acepta un solo argumento de tipo AFNetworkReachabilityStatus. Echa un vistazo a la actualización. initWithBaseURL: método de la MTForecastClient clase.

 - (id) initWithBaseURL: (NSURL *) url self = [super initWithBaseURL: url]; if (self) // Aceptar HTTP Header [self setDefaultHeader: @ "Accept" value: @ "application / json"]; // Registrar la clase de operación HTTP [self registerHTTPOperationClass: [AFJSONRequestOperation class]]; // Alcance __ tipo débil de (self) débilSelf = self; [self setReachabilityStatusChangeBlock: ^ (estado de AFNetworkReachabilityStatus) [[NSNotificationCenter defaultCenter] postNotificationName: MTRainReachabilityStatusDidChangeNotification object: weakSelf]; ];  devuélvete a ti mismo; 

Para evitar un ciclo de retención, pasamos una referencia débil al objeto singleton en el bloque al que pasamos setReachabilityStatusChangeBlock:. Incluso si usa ARC en sus proyectos, debe ser consciente de problemas sutiles de memoria como este. El nombre de la notificación que publicamos es otra constante de cadena declarada en MTConstants.h / .m.

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

La razón para publicar una notificación en el bloque de cambio de estado de accesibilidad es facilitar que otras partes de la aplicación se actualicen cuando cambie la accesibilidad del dispositivo. Para asegurarse de que el MTWeatherViewController se notifica a la clase sobre los cambios de accesibilidad, se agregan instancias de la clase como observador de las notificaciones enviadas por el cliente de Forecast como se muestra a continuación.

 - (id) initWithNibName: (NSString *) paquete nibNameOrNil: (NSBundle *) nibBundleOrNil self = [super initWithNibName: nibNameOrNil paquete: nibBundleOrNil]; if (self) // Inicializar el administrador de ubicación self.locationManager = [[CLLocationManager alloc] init]; // Configurar el Administrador de ubicaciones [self.locationManager setDelegate: self]; [self.locationManager setDesiredAccuracy: kCLLocationAccuracyKilometer]; // Agregar Observer NSNotificationCenter * nc = [NSNotificationCenter defaultCenter]; [nc addObserver: selector automático: @selector (reachabilityStatusDidChange :) nombre: MTRainReachabilityStatusDidChangeNotification object: nil];  devuélvete a ti mismo; 

Esto también significa que debemos eliminar la instancia como observador en el Dealloc método. Este es un detalle que a menudo se pasa por alto..

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

La implementación de alcanzabilidadStatusDidChange: Es bastante básico en este momento. Actualizaremos su implementación una vez que creemos la interfaz de usuario de la aplicación..

 - (void) reachabilityStatusDidChange: (NSNotification *) notificación MTForecastClient * forecastClient = [objeto de notificación]; NSLog (@ "Estado de accesibilidad>% i", forecastClient.networkReachabilityStatus); 

4. Actualización de datos

Antes de finalizar esta publicación, quiero agregar dos funciones adicionales, (1) obtener datos del clima cada vez que la aplicación se active y (2) agregar la capacidad de actualizar manualmente los datos del clima. Podríamos implementar un temporizador que obtenga datos nuevos cada hora aproximadamente, pero en mi opinión no es necesario para una aplicación meteorológica. La mayoría de los usuarios iniciarán la aplicación, observarán el clima y pondrán la aplicación en segundo plano. Por lo tanto, solo es necesario obtener datos nuevos cuando el usuario inicia la aplicación. Esto significa que tenemos que escuchar UIApplicationDidBecomeActiveNotification notificaciones en el MTWeatherViewController clase. Como hicimos para monitorear los cambios de accesibilidad, agregamos instancias de la clase como observadores de notificaciones de tipo UIApplicationDidBecomeActiveNotification.

 - (id) initWithNibName: (NSString *) paquete nibNameOrNil: (NSBundle *) nibBundleOrNil self = [super initWithNibName: nibNameOrNil paquete: nibBundleOrNil]; if (self) // Inicializar el administrador de ubicación self.locationManager = [[CLLocationManager alloc] init]; // Configurar el Administrador de ubicaciones [self.locationManager setDelegate: self]; [self.locationManager setDesiredAccuracy: kCLLocationAccuracyKilometer]; // Agregar Observer NSNotificationCenter * nc = [NSNotificationCenter defaultCenter]; [nc addObserver: selector automático: @selector (applicationDidBecomeActive :) nombre: UIApplicationDidBecomeActiveNotification object: nil]; [nc addObserver: selector automático: @selector (reachabilityStatusDidChange :) nombre: MTRainReachabilityStatusDidChangeNotification object: nil];  devuélvete a ti mismo; 

En applicationDidBecomeActive:, verificamos que ubicación se establece (no nulo) porque esto no siempre será verdad. Si la ubicación es válida, traemos los datos del clima..

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

También he enmendado fetchWeatherData para consultar solo la API de pronóstico si el dispositivo está conectado a la web.

 - (void) fetchWeatherData if ([[MTForecastClient sharedClient] networkReachabilityStatus] == AFNetworkReachabilityStatusNotReachable) return; // Mostrar progreso HUD [SVProgress HUD showWithMaskType: SVProgressHUDMaskTypeGradient]; // Query Forecast API double lat = [[_location objectForKey: MTLocationKeyLatitude] doubleValue]; doble lng = [[_location objectForKey: MTLocationKeyLongitude] doubleValue]; [[MTForecastClient sharedClient] requestWeatherForCoordinate: CLLocationCoordinate2DMake (lat, lng) complete: ^ (Exito BOOL, NSDictionary * response) // Dismiss Progress HUD [SVProgressHUD despedido]; // NSLog (@ "Respuesta>% @", respuesta); ]; 

Agreguemos un botón al controlador de vista del clima que el usuario puede tocar para actualizar manualmente los datos del clima. Crear una salida en MTWeatherViewController.h y crea un refrescar: acción en MTWeatherViewController.m.

 #importar  #importar "MTLocationsViewController.h" @interface MTWeatherViewController: UIViewController  @ propiedad (débil, no atómica) IBOutlet UILabel * labelLocation; @ propiedad (débil, no atómica) IBOutlet UIButton * buttonRefresh; @fin
 - (IBAction) actualizar: (id) remitente if (self.location) [self fetchWeatherData]; 

Abierto MTWeatherViewController.xib, añada un botón a la vista del controlador de vista con un título de Refrescar, y conecte la salida y la acción con el botón (figura 3). La razón para crear una salida para el botón es poder desactivarlo cuando no hay conexión de red disponible. Para que esto funcione, necesitamos actualizar el alcanzabilidadStatusDidChange: método como se muestra a continuación.

Figura 3: Agregar un botón de actualización
 - (void) reachabilityStatusDidChange: (NSNotification *) notificación MTForecastClient * forecastClient = [objeto de notificación]; NSLog (@ "Estado de accesibilidad>% i", forecastClient.networkReachabilityStatus); // Actualizar botón de actualización self.buttonRefresh.enabled = (forecastClient.networkReachabilityStatus! = AFNetworkReachabilityStatusNotReachable); 

No es necesario desactivar temporalmente el botón de actualización cuando se procesa una solicitud en fetchWeatherData porque el HUD de progreso agrega una capa en la parte superior de la vista del controlador de vista que evita que el usuario toque el botón más de una vez. Construye y ejecuta la aplicación para probar todo.


Bonus: Eliminando Ubicaciones

Un lector me preguntó cómo eliminar ubicaciones de la lista, así que lo incluyo aquí para que esté completo. Lo primero que debemos hacer es indicar a la vista de tabla qué filas se pueden editar implementando tableView: canEditRowAtIndexPath: del UITableViewDataSource protocolo. Este método devuelve si la fila en indexPath es editable y NO si no es. La implementación es simple como se puede ver a continuación. Cada fila es editable, excepto la primera fila y la ubicación seleccionada actualmente.

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

Para comprobar si ubicación Es la ubicación actual, usamos otro método auxiliar., isCurrentLocation:, en el que buscamos la ubicación actual y comparamos las coordenadas de las ubicaciones. Hubiera sido mejor (y más fácil) si hubiésemos asignado un identificador único a cada ubicación almacenada en la base de datos predeterminada del usuario. No solo facilitaría la comparación de ubicaciones, sino que también nos permitiría almacenar el identificador único de la ubicación actual en la base de datos de valores predeterminados del usuario y buscarlo en el conjunto de ubicaciones. El problema con la implementación actual es que las ubicaciones con las mismas coordenadas exactas no se pueden distinguir unas de otras.

 - (BOOL) isCurrentLocation: (NSDictionary *) location // Fetch Current Location NSDictionary * currentLocation = [[NSUserDefaults standardUserDefaults] objectForKey: MTRainUserDefaultsLocation]; if ([location [MTLocationKeyLatitude] doubleValue] == [currentLocation [MTLocationKeyLatitude] doubleValue] && [ubicación [MTLocationKeyLongitude] doubleValue] == [currentLocation [MTLocationKeyLongitude] doubleValue]) return YES;  devuelve NO; 

Cuando el usuario toca el botón de eliminar de una fila de vista de tabla, el origen de datos de vista de tabla se envía a tableView: commitEditingStyle: forRowAtIndexPath: mensaje. En este método, necesitamos (1) actualizar el origen de datos, (2) guardar los cambios en la base de datos predeterminada del usuario y (3) actualizar la vista de tabla. Si ediciónEstilo es igual a UITableViewCellEditingStyleDelete, eliminamos la ubicación de la localizaciones Arregle y almacene la matriz actualizada en la base de datos predeterminada de usuario. También eliminamos la fila de la vista de tabla para reflejar el cambio en el origen de datos.

 - (void) tableView: (UITableView *) tableView commitEditingStyle: (UITableViewCellEditingStyle) editingStyle forRowAtIndexPath *) indexPath Synth Sync Sync Sync SyncPacationLotationPowerPacerationTratationTraccesimentationTrat de lalacencia de la embarcación unicacorriente de la confianza de la personaPersonalización de la personaPersonalización de la personaPersonalización de la personalidad ; // Actualizar valores predeterminados de usuario [[NSUserDefaults standardUserDefaults] setObject: self.locations forKey: MTRainUserDefaultsLocations]; // Actualizar vista de tabla [tableView deleteRowsAtIndexPaths: @ [indexPath] withRowAnimation: UITableViewRowAnimationTop]; 

Para alternar el estilo de edición de la vista de tabla, debemos agregar un botón de edición a la interfaz de usuario. Crear una salida para el botón en MTLocationsViewController.h y una acción llamada editLocations: en MTLocationsViewController.m. En editLocations:, Cambiamos el estilo de edición de la vista de tabla..

 #importar  @protocol MTLocationsViewControllerDelegate; @interface MTLocationsViewController: UIViewController  ID de propiedad (débil, no atómica) delegar; @ propiedad (débil, no atómica) IBOutlet UITableView * tableView; @ propiedad (débil, no atómica) IBOutlet UIBarButtonItem * editButton; @end @protocol MTLocationsViewControllerDelegate  - (void) controllerShouldAddCurrentLocation: (MTLocationsViewController *) controller; - Controlador (vacío): (MTLocationsViewController *) controlador didSelectLocation: (NSDictionary *) ubicación; @fin
 - (IBAction) editLocations: (id) sender [self.tableView setEditing:! [Self.tableView isEditing] animated: YES]; 

Abierto MTLocationsViewController.xib, agregue una barra de navegación a la vista del controlador de vista y agregue un botón de edición a la barra de navegación. Conecte el botón de edición con la salida y la acción que creamos hace un momento..


Figura 4: Agregar un botón de edición

Quizás se esté preguntando por qué creamos una salida para el botón de edición. La razón es que necesitamos poder cambiar el título del botón de edición desde Editar a Hecho, y viceversa, siempre que cambie el estilo de edición de la vista de tabla. Además, cuando el usuario elimina la última ubicación (excepto la ubicación actual) en la vista de tabla, sería bueno cambiar automáticamente el estilo de edición de la vista de tabla. Estas características no son difíciles de implementar, por eso las dejo a usted como ejercicio. Si tiene problemas o tiene preguntas, no dude en dejar un comentario en los comentarios a continuación de este artículo..

Conclusión

Hemos integrado con éxito la API de pronóstico en nuestra aplicación de clima. En el siguiente tutorial, implementaremos el enfoque en la interfaz de usuario y el diseño de la aplicación..