Vistas
Creación de vistas por código
Hasta ahora hemos visto como crear la interfaz visualmente con Xcode, pero todo lo que se puede hacer con dicha herramienta se puede hacer también de forma programática, ya que lo único que hace el entorno es crear objetos de la API de Cocoa Touch (o definidos por nosotros) y establecer algunas de sus propiedades.
Ventanas
Las aplicaciones iOS tienen una única ventana. Podríamos crear la ventana principal desde nuestro código instanciando un objeto de tipo UIWindow
e indicando el tamaño del marco de la ventana:
Lo habitual será inicializar la ventana principal con un marco del tamaño de la pantalla, tal como hemos hecho en el ejemplo anterior.
Para mostrar la ventana en pantalla debemos llamar a su método makeKeyAndVisible (esto se hará normalmente en el método application:didFinishLaunchingWithOptions:
del delegado de UIApplication). Esto se hará tanto cuando la ventana ha sido cargada de un NIB como cuando la ventana se ha creado de forma programática.
Es posible que necesitemos acceder a esta ventana, por ejemplo para cambiar la vista que se muestra en ella. Hacer esto desde el delegado de UIApplication es sencillo, porque normalmente contamos con la propiedad window
que hace referencia a la pantalla principal, pero acceder a ella desde otros lugares del código podría complicarse. Para facilitar el acceso a dicha ventana principal, podemos obtenerla a través del singleton UIApplication
:
Si podemos acceder a una vista que se esté mostrando actualmente dentro de la ventana principal, también podemos obtener dicha ventana directamente a través de la vista:
Vistas genéricas
También podemos construir una vista creando un objeto de tipo UIView
. En el inicializador deberemos proporcionar el marco que ocupará dicha vista. El marco se define mediante el tipo CGRect
(se trata de una estructura, no de un objeto). Podemos crear un nuevo rectángulo con la macro CGRectMake
. Por ejemplo, podríamos inicializar una vista de la siguiente forma:
Como alternativa, podríamos obtener el marco a partir del tamaño de la pantalla o de la aplicación:
Normalmente utilizaremos el primero para las vistas que queramos que ocupen todo el espacio disponible en la pantalla, y el segundo para definir la ventana principal. Hemos de destacar que UIWindow
es una subclase de UIView
, por lo que todas las operaciones disponibles para las vistas están disponibles también para las ventanas. Las ventanas no son más que un tipo especial de vista.
Las vistas (UIView
) también nos proporcionan una serie de métodos para consultar y modificar la jerarquía. El método básico que necesitaremos es addSubview
, que nos permitirá añadir una subvista a una vista determinada (o a la ventana principal):
Con esto haremos que la vista aparezca en la pantalla.
También podemos añadir una vista usando la propiedad view
del view controlller “activo”. Esta propiedad representa la vista “raíz” de la jerarquía de vistas manejadas por él.
Otra posibilidad es sobreescribir el loadView
del controlador, que es el método encargado por defecto de crear las vistas, usualmente desde un NIB o storyboard, y en él crear las vistas necesarias, asignando la vista raíz a self.view
.
Podemos eliminar una vista enviándole el mensaje removeFromSuperview
(se le envía a la vista hija que queremos eliminar). Podemos también consultar la jerarquía con los siguientes métodos:
superview
: Nos da la vista padre de la vista destinataria del mensaje.subviews
: Nos da la lista de subvistas de una vista dada.isDescendantOfView:
Comprueba si una vista es descendiente de otra.
Como vemos, una vista tiene una lista de vistas hijas. Cada vista hija tiene un índice, que determinará el orden en el que se dibujan. El índice 0 es el más cercano al observador, y por lo tanto tapará a los índices superiores. Podemos insertar una vista en un índice determinado de la lista de subvistas con insertSubview:atIndex:
.
Puede que tengamos una jerarquía compleja y necesitemos acceder desde el código a una determinada vista por ejemplo para inicializar su valor. Una opción es hacer un outlet para cada vista que queramos modificar, pero esto podría sobrecargar nuestro objeto de outlets. También puede ser complejo y poco fiable el buscar la vista en la jerarquía. En estos casos, lo más sencillo es darle a las vistas que buscamos una etiqueta (tag) mediante la propiedad Tag
del inspector de atributos (debe ser un valor entero), o asignando la propiedad tag
de forma programática. Podremos localizar en nuestro código una vista a partir de su etiqueta mediante viewWithTag
. Llamando a este método sobre una vista, buscará entre todas las subvistas aquella con la etiqueta indicada:
Propiedades de una vista
A continuación vamos a repasar las propiedades básicas de las vistas, que podremos modificar tanto desde Xcode como de forma programática.
Disposición
Entre las propiedades más importantes en las vistas encontramos aquellas referentes a su disposición en pantalla. Hemos visto que tanto cuando creamos la vista con Xcode como cuando la inicializamos de forma programática hay que especificar el marco que ocupará la vista en la pantalla.
Cuando se crea de forma visual, el marco se puede definir pulsando con el ratón sobre los márgenes de la vista y arrastrando para así mover sus límites. En el código estos límites se especifican mediante el tipo CGRect
, en el que se especifica posición (x,y)
de inicio, y el ancho y el alto que ocupa la vista. Estos datos se especifican en el sistema de coordenadas de la supervista.
El sistema de coordenadas tiene su origen en la esquina superior izquierda. Las coordenadas no se dan en pixels, sino en puntos, una medida que nos permite independizarnos de la resolución en pixels de la pantalla. Las coordenadas en puntos son reales, no enteras.
Otros frameworks de iOS definen sistemas de coordenadas distintos. Los de gráficos (Core Graphics y OpenGL ES) ponen el origen en la esquina inferior izquierda con el eje Y apuntando hacia arriba.
Algunos ejemplos de cómo obtener la posición y dimensiones de una vista:
A partir de bounds
y center
podremos obtener frame
Sin embargo, en muchas ocasiones nos interesa que el tamaño no sea fijo sino que se adapte al área disponible. De esta forma nuestra interfaz podría adaptarse de forma sencilla a distintas orientaciones del dispositivo (horizontal o vertical) o a distintas resoluciones de la pantalla. Esto lo podemos conseguir mediante el uso de la tecnología de autolayout, que calcula de manera automática el frame de cada vista basándose en un conjunto de restricciones. Veremos esta tecnología en sesiones posteriores.
Transformaciones
Podemos también aplicar una transformación a las vistas, mediante su propiedad transform
. Por defecto las vistas tendrán aplicada la transformación identidad CGAffineTransformIdentity
.
La transformación se define mediante una matriz de transformación 2D de dimensión 3x3. Podemos crear transformaciones de forma sencilla con macros como CGAffineTransformMakeRotation
o CGAffineTransformMakeScale
.
Si nuestra vista tiene aplicada una transformación diferente a la identidad, su propiedad
frame
no será significativa. En este caso sólo deberemos utilizarcenter
ybounds
.
Otras propiedades
En las vistas encontramos otras propiedades que nos permiten determinar su color o su opacidad. En primer lugar tenemos backgroundColor
, con la que podemos fijar el color de fondo de una vista. En el inspector de atributos (sección View
) podemos verlo como propiedad Background
. El color de fondo puede ser transparente, o puede utilizarse como fondo un determinado patrón basado en una imagen.
De forma programática, el color se especifica mediante un objeto de clase UIColor
. En esta clase podemos crear un color personalizado a partir de sus componentes (rojo, verde, azul, alpha), o a partir de un patrón.
Por otro lado, también podemos hacer que una vista tenga un cierto grado de transparencia, o esté oculta. A diferencia de backgroundColor
, que sólo afectaba al fondo de la vista, con la propiedad alpha
, de tipo CGFloat
, podemos controlar el nivel de transparencia de la vista completa con todo su contenido y sus subvistas. Si una vista no tiene transparencia, podemos poner su propiedad opaque
a YES
para así optimizar la forma de dibujarla. Esta propiedad sólo debe establecerse a YES
si la vista llena todo su contendo y no deja ver nada del fondo. De no ser así, el resultado es impredecible. Debemos llevar cuidado con esto, ya que por defecto dicha propiedad es YES
.
Por último, también podemos ocultar una vista con la propiedad hidden
. Cuando hagamos que una vista se oculte, aunque seguirá ocupando su correspondiente espacio en pantalla, no será visible ni recibirá eventos.
Controles básicos de interfaz de usuario
Aunque aquí hablemos de controles indistintamente para referirnos a las etiquetas, botones, … en realidad este término tiene un significado más preciso en iOS. La clase
UIControl
es de la que heredan los controles más “interactivos” como los botones, mientras que las etiquetas lo hacen deUIView
(no obstante todos losUIControl
son también vistas ya que a su vez esta clase hereda deUIView
).
Campos de texto
Un campo de texto nos proporciona un espacio donde el usuario puede introducir y editar texto. Se define en la clase UITextField
, y pertenece a un grupo de vistas denominados controles, junto a otros componentes como por ejemplo los botones. Esto es así porque permiten al usuario interactuar con la aplicación. No heredan directamente de UIView
, sino de su subclase UIControl
, que incorpora los métodos para tratar eventos de la interfaz mediante el patrón target-action como hemos visto anteriormente.
Sus propiedades se pueden encontrar en la sección Text Field
del inspector de atributos. Podremos especificar un texto por defecto (Text
), o bien un texto a mostrar sombreado en caso de que el usuario no haya introducido nada (Placeholder Text
). Esto será útil por ejemplo para dar una pista al usuario sobre lo que debe introducir en dicho campo.
Si nos fijamos en el inspector de conexiones del campos de texto, veremos la lista de eventos que podemos conectar a nuestra acciones. Esta lista de eventos es común para cualquier control. En el caso de un campo de texto por ejemplo nos puede interesar el evento Value Changed
.
Botones
Al igual que los campos de texto, los botones son otro tipo de control (heredan de UIControl
). Se definen en la clase UIButton
, que puede ser inicializada de la misma forma que el resto de vistas.
Si nos fijamos en el inspector de atributos de un botón (en la sección Button
), vemos que podemos elegir el tipo de botón (atributo Type
). Podemos seleccionar una serie de estilos prefedinidos para los botones, o bien darle un estilo propio (Custom
).
El texto que aparece en el botón se especifica en la propiedad Title
, y podemos configurar también su color, sombreado, o añadir una imagen como icono.
En el inspector de conexiones, el evento que utilizaremos más comúnmente en los botones es Touch Up Inside
, que se producirá cuando levantemos el dedo tras pulsar dentro del botón. Este será el momento en el que se realizará la acción asociada al botón.
Alertas
Se usan para informar al usuario de eventos importantes. Pueden simplemente informar de algo o además pedir al usuario que elija uno entre varios cursos de acción. No se crean gráficamente en Xcode sino por código
Como mínimo tendremos un botón (cancelButtonTitle
), para que la alerta desaparezca.
Aunque se llame
cancelButtonTitle
no quiere decir que sea el botón de cancelar, de hecho habitualmente será “Aceptar” u “OK”.
El delegate
es el objeto que se ocupará de qué hacer cuando la alerta se hace desaparecer o qué curso tomar en función del botón elegido. Debe implementar el protocolo UIAlertViewDelegate
. Por ejemplo alertView:didDismissWithButtonIndex:
se envía cuando desaparece la alerta y nos dice qué número de botón se ha pulsado (0 el cancelButtonTitle
y el resto por el orden de definición comenzando en 1).
Action Sheets
Dan a los usuarios la posibilidad de ofrecer datos adicionales ante una acción a realizar. Por ejemplo confirmar o cancelar la información.
Las aparición de una alerta suelen ser inesperada para el usuario. Sin embargo el usuario sabe cuándo es probable que aparezca un Action Sheet. Por ejemplo cuando realiza una acción destructiva como eliminar una foto.
No se crean gráficamente sino por código. El API es prácticamente idéntico al de las alertas.
Teclado en pantalla
Cuando un campo de texto adquiere el foco porque el usuario hace tap sobre él, automáticamente aparece el teclado software on screen. El problema es que por defecto no desaparece salvo que escribamos algo de código.
Si queremos que desaparezca cuando pulsamos sobre la tecla de “Aceptar” tenemos que escribir un action que responda al evento Did end on exit
del campo de texto. En teoría dentro de este action debemos hacer que el campo deje de ser el first responder para que el teclado deje de mostrarse
Ejercicios de vistas
Creación de ventana/controlador/vista de modo manual
Ya hemos visto que por defecto las aplicaciones de Xcode usan el storyboard. Este storyboard lo carga automáticamente el Application Delegate, haciendo además las siguientes tareas:
Crear una ventana con el tamaño de la pantalla
Cargar el controlador asociado a la pantalla inicial, y asignarle a su propiedad
view
las subvistas de esta pantallaHacer visible la ventana, que ahora contiene al controlador
Vamos a hacer lo mismo pero de modo manual, para comprender un poco mejor todo el proceso
Cread un nuevo proyecto en la carpeta
sesion2
de las plantillas llamadoVistaManual
Lo primero es desactivar el storyboard. Para ello en el navegador de Xcode hacemos clic sobre el icono del proyecto. Aparecerán los distintos apartados de configuración. Elegimos
info
y eliminamos una propiedad que pone “Main Storyboard name” pulsando sobre el botón de ‘-‘. Ahora el proyecto ya no usa el storyboardEn el
applicationDidFinishLaunching:withOptions
del AppDelegate tenemos que hacer lo siguiente
No obstante, en pantalla no se verá nada, ya que la vista del
ViewController
está vacía. Probad a hacer que aparezca algo para comprobar que todo funciona:Tened en cuenta que la vista es
self.window.rootViewController.view
cambiad el color, propiedad
backgroundColor
de la vista. Para especificar color lo más sencillo es usar una serie de métodos de clase (estático) deUIColor
:[UIcolor greenColor]
,[UIcolor redColor]
,…cread un botón (instanciad un objeto de la clase UIButton*), especificando el título (método
setLabel:forState
) y unas dimensiones
Uso de controles de interfaz de usuario
Cread un slider cuya escala vaya desde 0 hasta 100, y un label al lado. Al mover el slider, el label debería cambiar para reflejar su valor actual.
Cread un outlet para poder manipular la etiqueta
Crear un “action” con
ctrl+arrastrar
desde el slider al código deViewController.m
. En este action podéis obtener el valor actual del slider (propiedadvalue
del parámetrosender
y fijarlo como texto de la etiqueta. Tened en cuenta que el valor del slider es un número y el texto es unNSString*
, podéis usar[NSString stringWithFormat]
para convertir como ya hemos hecho varias veces.
Especificar que implementa el protocolo
UIActionSheetDelegate
Implementar el método
actionSheet:clickedButtonAtIndex:
que nos dirá el número de opción elegida, comenzando en 0.
Last updated