Hola iOS: introducción a la plataforma iOS y el lenguaje Objective-C
Introducción a iOS y al desarrollo en la plataforma
iOS
iOS está dividido en capas, como es habitual las capas superiores se apoyan en los servicios proporcionados por las inferiores.
Cocoa touch: contiene las funcionalidades de alto nivel, básicamente cuestiones relacionadas con el interfaz de usuario (multitouch, detección de gestos,…) pero también otras muy diversas: Airdrop, Mapkit para mostrar mapas en nuestra aplicacion, iAd, …
Media: contiene las tecnologías de video y audio, por ejemplo servicios de alto nivel como Airplay o APIs para gráficos 2D (Core Graphics) o animaciones (Core animation), gráficos 3D con OpenGL, procesamiento de audio (Core audio), …
Core services: contiene un conjunto de servicios básicos para las aplicaciones, por ejemplo la geolocalización, la concurrencia, el almacenamiento de datos con iCloud, SQLite o Core Data (para almacenar objetos de modo persistente),…
Core OS Funcionalidades básicas del sistema, como seguridad, control del hardware (bluetooth, accesorios externos, …).
En nuestras aplicaciones podemos usar los servicios proporcionados por cualquiera de las capas, pero se recomienda intentar usar los de las capas superiores si es posible. Por ejemplo, Cocoa Touch ofrece la posibilidad de usar los interfaces estándares del sistema para listar o seleccionar contactos de la agenda en nuestras aplicaciones. De esta forma nuestra aplicación tendrá un interfaz uniforme y coherente con el resto. Pero si necesitamos acceder a la información de contactos y queremos ofrecer un interfaz propio (o no necesitamos una interfaz) podemos usar las funcionalidades que nos ofrece la capa de “Core Services”.
Desarrollo en iOS
Para desarrollar aplicaciones en iOS empleamos un conjunto de lenguajes, herramientas de programación y librerías. A un nivel de abstracción algo más alto organizamos nuestro código siguiendo patrones de diseño software.
Frameworks: lo que en muchas otras plataformas se denomina librería. En Objective-C consisten físicamente en una librería dinámica y un conjunto de ficheros auxiliares, básicamente cabeceras (
.h
)Lenguajes: hasta junio del año pasado el único que estaba soportado de manera oficial era Objective-C, que usaremos al menos en los primeros módulos del curso. Ahora Apple también soporta un nuevo lenguaje: Swift.
Herramientas: la más importante es el IDE de Apple, Xcode, que se integra además con herramientas adicionales como Instruments para depuración y pruebas de rendimiento, el Emulador para probar nuestras aplicaciones, ….
Patrones de diseño: tanto los APIs de iOS como la propia arquitectura que deben seguir las aplicaciones hace un fuerte uso de determinados patrones de diseño. El más omnipresente es el Modelo-Vista-Controlador, pero hay algunos más (delegation, publish/subscribe,…).
Introducción básica a Xcode
Xcode es un IDE que nos sirve tanto para escribir el código de nuestra aplicación iOS como para
Diseñar la interfaz gráfica
Ejecutar pruebas unitarias y de rendimiento
Depurar el código
Gestionar el control de versiones
Distribuir la aplicación
Áreas de trabajo: La pantalla de Xcode está dividida en 5 zonas:
Navegadores
Barra de herramientas
Editor
Utilidades
Área de depuración (inicialmente está oculta, y se muestra cuando se ejecuta la aplicación)

Vamos a crear una app que imprima “hola mundo” con
NSLog
, paraEscribir algo de código y compilarlo
Ver cómo se usa el emulador: cómo se ejecuta una aplicación y cómo se puede poner un breakpoint y depurar a nivel básico.
Introducción a Objective-C y a las aplicaciones iOS con una aplicación de ejemplo
Algunas características de Obj-C
Es una extensión orientada a objetos de C
Es mucho más dinámico que C++. Por ejemplo, se puede decidir el método a llamar en tiempo de ejecución, se puede definir una variable como un objeto de un tipo cualquiera (``id```).
Todos los objetos se crean en memoria dinámica, las variables son referencias, al estilo Java, pero debemos explicitar que son punteros, al contrario que en Java.
Se usan includes “al estilo C”, pero la sentencia
#import
impide automáticamente que el mismo fichero sea incluído dos veces, eliminando la necesidad de guardas de inclusión.No se usa la sintaxis OO clásica de
objeto.metodo(parametro)
sino otra mucho más atípica:[objeto metodo: parametro]
La interfaz está separada de la implementación
La gestión de memoria se puede automatizar, aunque no usa recolección de basura, sino cuenta de referencias
La aplicación de ejemplo
Vamos a desarrollar una sencilla aplicación llamada UADivino. Podemos “preguntarle” algo en voz alta (que se pueda responder con sí/no) y al pulsar un botón la aplicación nos dará su respuesta.
Hacemos esta aplicación para ilustrar:
Cómo es el Objective-C básico
Qué estructura tiene el código de una aplicación iOS sencilla
Cómo se comunica el controlador con el modelo y con la vista
Modelo-Vista-Controlador
Todas las aplicaciones iOS siguen este patrón de diseño
Desde el punto de vista lógico nuestra aplicación se divide en tres componentes: modelo, vista y controlador
El modelo es el núcleo de la aplicación, qué es
La vista es el interfaz gráfico, cómo se presentan los datos
El controlador se comunica con ambos haciendo de nexo de unión. Más tarde veremos los distintos mecanismos de comunicación que hay en iOS.
Hay que tener claro que vista y modelo no deberían comunicarse directamente, siempre deberían hacerlo a través del controlador.
Estructura de una aplicación iOS
Para crear el nuevo proyecto 1. Iniciar la creación de nuevo proyecto desde la pantalla de bienvenida de Xcode:
Create a new Xcode project
En el siguiente diálogo, que permite elegir plantilla, seleccionar la de “Single View Application”
En la pantalla de opciones, elegir como se muestra:

En Xcode 5, en esta pantalla se podía seleccionar un “prefijo” para las clases. En Objective C no hay namespaces, lo que impide que puedan haber dos clases con el mismo nombre. Como “parche”, en iOS/OSX era una práctica comúnmente aceptada preceder los nombres de las clases con un prefijo arbitrario para evitar colisiones. Con Xcode 6, Apple ha cambiado la práctica habitual y no recomienda los prefijos salvo para el desarrollo de frameworks, ya que en estos sí hay riesgo de colisión de nombres al estar hechos para ser usados en diferentes proyectos. En una aplicación normal es raro reutilizar código de otra y por tanto no hay riesgo de colisiones en los nombres.
Finalmente, nos pedirá seleccionar la carpeta donde crear el proyecto. Automáticamente creará una carpeta con el mismo nombre del proyecto donde meterá todos los archivos. También nos preguntará si deseamos crear un repositorio Git para el proyecto. Marcad esta casilla, para poder probar su uso. Para ver más información sobre el uso de Git en Xcode, consultad el apéndice correspondiente.
Si no se marca la opción de Git es posible crear luego el repositorio de forma manual (con
git init
… etc) pero no con Xcode. Para ver cómo crear el repositorio manualmente, por ejemplo consultar el apartado “Creating a Git repository” del tutorial “Understanding Git Source Control in Xcode”
Si echamos un vistazo al código generado por Xcode podremos ver que tiene la siguiente estructura, común a todas las aplicaciones iOS
El esquema anterior es genérico, en nuestro caso concreto:
Por ahora no hay modelo, ya que ¡crearlo es nuestro trabajo!
El
UIApplication
se crea desde la función main que está enSupporting files/main.m
El application delegate se ha creado automáticamente y se llama
AppDelegate
. Aquí es donde metemos el código “global” de la aplicaciónSolo hay una vista y un controlador. El controlador es la clase
ViewController
. Para la vista no tenemos código Obj-C sino que la editamos gráficamente con elMain.storyboard
En el área del navegador, a la izquierda de la pantalla, podremos ver la estructura del proyecto recién creado. Veremos que está dividido en tres carpetas, o grupos: uno para los fuentes, otro para las pruebas unitarias y otro para el producto final (la aplicación compilada).

En Xcode los grupos o groups son simplemente una forma interna de organizar el código. Son carpetas “virtuales” que no tienen por qué corresponderse con carpetas “reales” del sistema de archivos. Como ejemplo, podéis pulsar con botón derecho sobre el icono del proyecto y seleccionar “Show in finder” para ver la estructura de carpetas en el Finder (el explorador de archivos de OSX). Podréis ver que los grupos
UAdivino
yUAdivinoTests
sí se corresponden con carpetas del sistema de archivos, pero no asíproducts
oUAdivino/Supporting files
.
Implementación del modelo
En resumen:
Es el núcleo de nuestra aplicación, la “lógica de negocio”
Debe ser independiente del interfaz, por lo que no se comunicará directamente con la vista
Nuestra “lógica de negocio” es muy simple, se limitará a darnos una respuesta elegida al azar de entre las posibles. Crearemos una clase que implemente esto.
Crear la clase
UADivinoModel
: MenúFile > New > File...
y en la categoríaiOS
elegirCocoa Touch class
Automáticamente se creará un fichero de interfaz pública (
.h
) y otro de implementación (.m
).Añadir un método en el fichero de la interfaz:
- (NSString\*) obtenerRespuesta;
implementar el método en el fichero de implementación. Por el momento solo debería dar las respuestas “SI” o “NO” con una probabilidad del 50%
Ayuda: la función
arc4random()
es similar alrand()
de C. Por ejemplo para obtener enteros entre 0 y 9 haríamos:arc4random()%10
.
Constructores
Crear un constructor en el que se inicialice un array de respuestas
Más sobre métodos
Añadir un método con argumentos:
addRespuesta
, para insertar una nueva posible respuesta en el array, con un argumentoNSString*
.Tendremos que cambiar el
NSArray
por unNSMutableArray
Propiedades
Añadirle a UAdivino un nombre: Zoltar, Rappel, …
El compilador “sintetiza” automáticamente un getter y un setter. Por convenio el getter tiene el mismo nombre que la propiedad y el setter comienza por ``set```(al estilo Java).
Interfaz gráfico: vista y controlador
En el panel de la derecha del todo desmarcar la casilla “Use size classes”
Añadir un label para explicar la mecánica de UAdivino “formula en voz alta una pregunta y pulsa el botón para obtener la respuesta”
Añadir un botón “obtener Respuesta”
Crear un action vinculado al botón y en el
ViewController.h
a un nuevo método del controller “botonPulsado”Crear un outlet vinculado al label y a una nueva propiedad en el
ViewController.h
Asset catalogs
La mayoría de aplicaciones tienen imágenes asociadas al interfaz de usuario. Como mínimo deberían tener un icono y una imagen de launch, que aparece mientras se carga la aplicación. Además durante la ejecución se pueden mostrar más imágenes incluidas en el bundle de la aplicación (no son descargadas de la red en tiempo real). Para organizar las imágenes de modo sencillo podemos usar los Asset catalogs
Por defecto cada proyecto tiene inicialmente un catálogo de imágenes vacío, que aparece en el navegador con el nombre
Images.xcassets
Para los iconos y launch image usaremos formato .png y los tamaños recomendados. Podemos consultarlos por ejemplo en las Human Inteface Guidelines de Apple
Podemos añadir una imagen al catálogo arrastrándolas allí dentro desde el Finder o bien pulsando con el botón derecho en el catálogo y seleccionando
Import...
. En realidad lo que estamos creando es un “image set” más que una única imagen física. Para cada imagen podemos tener la versión en 1x, 2x y 3x. iOS usará automáticamente la apropiada para la resolución del dispositivo actual.Cada “image set” tiene un nombre, que no tiene por qué coincidir con el nombre físico del/los fichero/s. Para cargar la imagen en nuestro código podemos usar el método de clase
imageNamed:
Foundation framework
El framework Foundation define un conjunto de clases básicas y de utilidades que no están directamente incluídas en Objective-C, como colecciones, cadenas, fechas … Además define algunos elementos importantes como la raíz de la jerarquía de clases y algunos tipos primitivos para mejorar la portabilidad.
Tipos primitivos de C vs tipos primitivos de Foundation/Cocoa
Como Objective-C es compatible con C podemos seguir usando en nuestros programas los tipos
int
,float
,double
,…En Foundation también existen
NSInteger
yNSUInteger
como sustitutos de las variantes deint
. Son simplementetypedef
definidos como enteros con la longitud apropiada para la plataforma (32/64 bits). Es decir, no son objetos sino tipos primitivos, y se recomienda su uso por razones de portabilidad.Algo similar ocurre con
CGFloat
, usado ampliamente en Cocoa cuando se trabaja con números reales.
Clases básicas
En Foundation existe el concepto de objeto mutable e inmutable inmutable significa que no se pueden insertar, eliminar o cambiar elementos de la colección. Para estas operaciones debemos usar objetos mutables. Por ejemplo, las cadenas
NSSString
que hemos visto hasta ahora son objetos inmutables.
NSObject
: la raíz de la jerarquía de clases
NSObject
: la raíz de la jerarquía de clasesTodas las clases deben heredar de una clase base, en caso contrario dará error de compilación. Lo habitual es hacer que nuestras clases hereden de
NSObject
, la “clase raíz” de Foundation.
Esto es similar a otros lenguajes, por ejemplo Java, en la que todas las clases deben heredar de
Object
. Lo que ocurre es que en Java se hace implícitamente mientras que en Objective-C se debe especificar de manera explícita. De hecho en versiones antiguas de Java también se debía hacer explícitamente.
La clase
NSObject
incluye una serie de métodos de utilidad para nuestras clases, entre otros:initialize
es un método de clase llamado cuando ésta se carga por primera vez. Puede servir para inicializar variables estáticas.En los dos siguientes apartados vamos a ver los métodos de
NSObject
para copiar objetos y obtener información sobre la instancia actual.
Copia de objetos
los métodos
copy
/mutableCopy
: sirven para hacer una copia inmutable/mutable de un objeto, respectivamente.Con clases de Foundation/Cocoa, podremos hacer una copia usando estos métodos cuando la clase en cuestión sea conforme al protocolo
NSCopying
. Podremos comprobar este extremo en la documentación

Posteriormente veremos más a fondo los protocolos en Objective-C. Por ahora nos basta con saber que más o menos equivalen a los
Interface
de Java
Con clases propias, para que
copy
funcione, debemosImplementar un método
copyWithZone
(al quecopy
acaba llamando). En este método debemos devolver la copia. Para ayudarnos podemos hacer uso de NSCopyObject, que hace la copia shallow del objeto.Especificar que nuestra clase es conforme a
NSCopying
.
Podéis ver más detalles del proceso anterior por ejemplo en este tutorial
Con clases propias, para
mutableCopy
debemos hacer lo mismo pero implementando un métodomutableCopyWithZone
Información sobre una instancia
NSObject
implementa una serie de métodos que ofrecen información sobre una instancia y que podemos sobreescribir para adaptar a nuestras necesidades. Como ahora veremos, la mayoría de clases de Foundation ya los sobreescriben.
isEqual
: como siempre se utilizan referencias para los objetos, el operador==
lo que hará es comprobar si apuntan a la misma dirección de memoria. Si queremos comprobar igualdad de contenido, podemos usarisEqual
.La mayoría de clases de Cocoa/Foundation sobreescriben este método o bien proporcionan versiones algo más sofisticadas. Por ejemplo,
NSString
implementa también un métodoisEqualToString
, especializado en comparar cadenas.En nuestras propias clases deberíamos sobreescribir el
isEqual
ya que la versión deNSObject
usa simplemente un==
para comparar.
hash
: debe calcular (cómo no) un hash para nuestro objeto. La implementación por defecto simplemente devuelve la dirección del objeto, de modo que en nuestras propias clases deberíamos sobreescribirlo si lo necesitamos. La mayoría de clases de Cocoa lo sobreescriben.
hasta iOS 7
hash
era un método. En iOS 8 es una propiedadComo especifica la documentación de Apple, si sobreescribimos
isEqual
también debemos hacer lo propio conhash
si queremos usar la clase en colecciones. En caso contrario podemos tener problemas, ya que dos objetos iguales deberían tener el mismo valor de hash.
description
: debería devolver unNSString*
con una descripción del objeto (al estilotoString
de Java). Como en los métodos anteriores, deberíamos sobreescribirlo en nuestras propias clases ya que la implementación por defecto imprime simplemente el nombre de la clase y la dirección en memoria de la instancia.
Cadenas
Ya hemos usado múltiples veces NSString
. La clase tiene numerosos métodos para manipulación de cadenas, búsqueda de subcadenas, conversión entre cadenas de C y NSString
, etc. La clase es inmutable pero su subclase NSMutableString
sí es mutable. Podéis consultar este tutorial para ver ejemplos de uso
Fechas
Otra clase básica es NSDate
, para el manejo de fechas. Podéis ver ejemplos de su uso en este tutorial.
Colecciones
Todas las colecciones de Foundation vienen en una versión “inmutable” y otra “mutable”.
Cuando se añade un objeto a una colección mutable lo que se está añadiendo es una referencia, no una copia, por lo que modificar el objeto original modificará la colección.
Wrappers de tipos primitivos
Las colecciones son conjuntos de objetos, por lo que directamente no pueden almacenar valores primitivos de C. Necesitaremos “empaquetar” estos tipos primitivos dentro de objetos usando wrappers.
NSNumber
es un wrapper que puede empaquetar en forma de objeto cualquier tipo numérico de C. Además de para las colecciones, ciertos métodos de Cocoa requieren de datos numéricos como objetos en lugar de tipos primitivos.Podemos definir un
NSNumber
precediendo un literal del símbolo@
. Con una expresión debemos usar además paréntesis, por ejemplo
Un
NSNumber
no es directamente un número por lo que no puede participar en operaciones aritméticas, etc. Pero sí podemos obtener el valor empaquetado dentro con los métodosintValue
,floatValue
, etc.
El compilador no chequea tipos al desempaquetar los valores, de modo que no dará error si llamamos por ejemplo a
intValue
para un valor empaquetado comofloat
. En este caso simplemente truncará el valor.
Por otro lado, no es posible añadir
nil
a una colección ya que significa “vacío”. Si queremos representar esto en una colección usaremos la claseNSNull
. Es un singleton, y para obtener su única instancia se usa el método de clasenull
Listas
La versión inmutable es
NSArray
y la mutable es su subclaseNSMutableArray
Un
NSArray
puede contener objetos de distintas clases. Por otro lado, losNSMutableArray
pueden cambiar no solo de contenido sino también de tamaño. Es decir, son más parecidos a las listas de Java o a los arrays de Python/Ruby… que a los arrays de C.La manera más sencilla de inicializar un
NSArray
es con un literalNSArray *a = @[@1, @"hola"];
Para inicializar un
NSMutableArray
no podemos usar literales, sino un método de clase que actúa como una factoría:
Podemos obtener el número de elementos que tiene la lista con el método
count
. Este método sirve también para las otras colecciones.Podemos añadir un elemento a un
NSMutableArray
conaddObject
. O insertarlo en una posición específica coninsertObject:atIndex
. El elemento que estuviera en esa posición y los siguientes se moverán una posición “a la derecha”.
También podemos eliminar elementos, reemplazarlos, ordenarlos, filtrarlos,… se pueden ver ejemplos de uso en este tutorial.
Diccionarios
Un diccionario es un conjunto de pares
(clave, valor)
. Las claves suelen serNSString
, pero se puede usar cualquier objeto que sea copiable (sea conforme aNSCopying
) e implementeisEqual
yhash
.`NSDictionary```es la versión inmutable y
NSMutableDictionary` la mutable.Podemos crear un
NSDictionary
con un literal. Se usan las llaves como delimitador, la clave se separa del valor por ``:```y las parejas clave-valor por comas
Podemos obtener un valor a partir de la clave usando notación de array
meses[@"Enero"]
Al igual que con los
NSArray
, para crear la versión mutable necesitamos un método factoría. Hay varios, por ejemplo podemos crearlo a partir de dos arrays, uno para los valores y otro para las claves
Para añadir un elemento a un diccionario mutable podemos usar la notación de array o bien usar el método
setObject:forKey
Podemos ver más ejemplos de uso de diccionarios en este tutorial.
Conjuntos
Un conjunto es una colección no ordenada de elementos distintos. Al igual que en el resto de colecciones, tenemos la versión inmutable
NSSet
y la mutableNSMutableSet
.No se pueden usar literales para inicializar conjuntos, pero sí hay varios métodos factoría, por ejemplo
setWithObjects
NSSet *conj = [NSSet setWithObjects:@1, @"Hola", nil];
Podemos comprobar si un objeto está en una colección con
containsObject
. Aunque esto también se puede hacer conNSArray
los conjuntos lo implementan de modo mucho más eficiente.
Podemos añadir un objeto con
addObject
y eliminarlo conremoveObject
Podemos ejecutar otras operaciones típicas de conjuntos, como la unión, la intersección, etc. Para más ejemplos de uso de conjuntos, consultar este tutorial.
Recorrer una colección
La forma más simple de recorrer una colección es con la denominada fast enumeration, que consiste en un
for-in
al estilo Java 5.
Si el tipo de objetos de la colección es uniforme y lo conocemos, podemos especificarlo, el código quedará más claro
El fast enumeration aplicado a diccionarios efectúa una iteración por las claves, pero a partir de ellas recuperar los valores es muy sencillo,como ya hemos visto
Además de la fast enumeration se pueden usar enumerators, que son como los iteradores de Java, pero al igual que en este lenguaje el código resulta más complejo.
Si mientras estamos recorriendo una colección mutable (tanto con for-in como con un enumerador) tratamos de modificarla, obtendremos un error en tiempo de ejecución.
Apéndice: Git y Xcode
Operaciones básicas de Git desde Xcode
Al crear el proyecto marcando la opción de usar un repositorio Git, automáticamente se ha inicializado el repositorio y hecho el commit de los archivos iniciales de la plantilla que hayamos elegido.
Cada vez que modifiquemos uno de los archivos, Xcode nos lo indicará en el navegador del proyecto con una M. Si añadimos alguno aparecerá una A.
Para hacer un commit,
Source Control > Commit...
. En el cuadro de diálogo podremos revisar los archivos cambiados y escribir el mensaje de commitPara anular todos los cambios, y volver al último commit
Source Control > Discard all changes...
Podemos trabajar con ramas desde la primera opción del menú
Source Control
, que pone el nombre de la rama actual (por defecto “master”) y nos permite crear una nueva, cambiar de rama, hacer un merge, …
https://developer.apple.com/library/ios/documentation/general/conceptual/devpedia-cocoacore/
Referencias
Algunas referencias para aprender Objective-C y de introducción básica a la plataforma iOS
Recursos generales
iOS Dev Center de Apple, con todos los recursos (guías, referencias, ejemplos de código, …) que Apple ofrece a los desarrolladores. Es necesario registrarse (gratuitamente) para ver algunos recursos (por ejemplo, los videos de las sesiones de la WWDC, muy recomendables).
Guías de Apple
Tutoriales en la web
Libros
Disponibles en versión electrónica desde las IPs de la UA a través del servicio Safari Books Online. Aunque esta plataforma ofrece decenas de libros sobre Objective-C e iOS, son muy recomendables los siguientes:
iOS 7 programming fundamentals, Matt Neuburg
Learning Cocoa with Objective-C, 4th Edition, Paris Buttfield-Addison, Jonathon Manning & Tim Nugent
Learn iOS 7 App Development, James Bucanek. Del estilo hands-on, en cada capítulo se desarrolla una pequeña aplicación.
Ejercicios de introducción a iOS
Mostrar imagen además de respuesta en modo texto
En lugar de mostrar solo el texto de la respuesta, también vamos a mostrar una imagen que la “ilustre”. Tendremos dos imágenes: una para cuando la respuesta es positiva y otra para cuando es negativa.
Parte 1: modificación del modelo
Primero habrá que modificar el modelo para que en lugar de almacenar simplemente los textos de las respuestas para cada una se almacene el texto y además si la respuesta es positiva/negativa. Crearemos la clase Respuesta
Despúes añadirle a la clase Respuesta un constructor
initWithTexto:andTipo:
para poderle pasar el texto y si es positiva/negativa. El constructor se usaría de este modo:
Para instanciar el
NSMutableArray
de respuestas lo más simple es usar el mismo método que hasta ahora, el método de clasearrayWithObjects
, pero ahora le pasaremos instancias de la claseRespuesta
en lugar de cadenas de Foundation. Es decir, aunque quede un código un poco “sucio”, para simplificar, creamos varias variables de tipoRespuesta *
y luego las añadimos alNSMutableArray
.
Parte 2: interfaz gráfico
Crear una “Image view” en el interfaz de usuario, usando la “Object Library” (panel inferior derecho de Xcode)
Crear un outlet que asocie esta “image view” con una propiedad en el
ViewController.h
. Podéis llamar a esta propiedad como queráis, por ejemplo “imagen”.Añadir las imágenes que están en las plantillas de la sesión al “images.xcassets”. Darles un nombre corto para referirnos luego a ellas en el código.
En el controller, dentro del método
botonPulsado
, además de mostrar la respuesta en la etiqueta, hay que actualizar la “image view”. Recordar que se puede cargar una imagen conimageNamed:
. Lo que tenemos que cambiar de la “image view” es la propiedadimage
Last updated