Creación de interfaces de usuario de iOS en código en Xamarin.iOS
La interfaz de usuario de una aplicación de iOS es como un escaparate: la aplicación normalmente obtiene una ventana, pero la puede llenar con los objetos que necesite. Dichos objetos y su disposición pueden cambiarse según lo que quiera mostrarse en la aplicación. Los objetos de este escenario, lo que el usuario ve, se denominan "vistas". Para compilar una sola pantalla en una aplicación, las vistas se apilan unas sobre otras en una jerarquía de vistas de contenido, que esta, a su vez, se administra a través de un controlador de vistas único. Las aplicaciones con varias pantallas tienen varias jerarquías de vistas de contenido, cada una con su propio controlador de vistas. La aplicación coloca las vistas en la ventana para crear una jerarquía de vistas de contenido diferente basándose en la pantalla en la que se encuentra el usuario.
En el diagrama siguiente se muestran las relaciones entre la ventana, las vistas, las subvistas y el controlador de vistas que, de forma conjunta, proporcionan la interfaz de usuario a la pantalla del dispositivo:
Estas jerarquías de vistas se pueden construir mediante el Interface Builder de Xcode, pero es bueno tener una comprensión fundamental de cómo trabajar completamente en el código. En este artículo se describen algunos puntos básicos para ponerse en marcha con el desarrollo de la interfaz de usuario de solo código.
Creación de un proyecto de solo código
Plantilla de proyecto en blanco de iOS
En primer lugar, cree un proyecto de iOS en Visual Studio con el proyecto Archivo > Nuevo proyecto > Visual C# > iPhone y iPad > Aplicación iOS (Xamarin), que se muestra a continuación:
A continuación, seleccione la plantilla de proyecto Aplicación en blanco:
La plantilla Proyecto vacío agrega 4 archivos al proyecto:
- AppDelegate.cs: contiene una subclase
UIApplicationDelegate
,AppDelegate
, que se usa para controlar eventos de aplicación desde iOS. La ventana de la aplicación se crea en el método deAppDelegate
llamadoFinishedLaunching
. - Main.cs: contiene el punto de entrada de la aplicación, que especifica la clase para el
AppDelegate
. - Info.plist: archivo de lista de propiedades que contiene información de configuración de la aplicación.
- Entitlements.plist: archivo de lista de propiedades que contiene información sobre las funcionalidades y permisos de la aplicación.
Las aplicaciones de iOS se compilan mediante el patrón MVC. La primera pantalla que muestra una aplicación se crea a partir del controlador de vista raíz de la ventana. Consulte la guía Hello, iOS Multiscreen para obtener más información sobre el propio patrón MVC.
La implementación del AppDelegate
agregado por la plantilla crea la ventana de aplicación, de la cual solo hay una para cada aplicación de iOS y hace que sea visible con el código siguiente:
public class AppDelegate : UIApplicationDelegate
{
public override UIWindow Window
{
get;
set;
}
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
// create a new window instance based on the screen size
Window = new UIWindow(UIScreen.MainScreen.Bounds);
// make the window visible
Window.MakeKeyAndVisible();
return true;
}
}
Si tuviera que ejecutar esta aplicación ahora, es probable que obtenga una excepción que indique que Application windows are expected to have a root view controller at the end of application launch
. Vamos a agregar un controlador y convertirlo en el controlador de vista raíz de la aplicación.
Agregar un controlador
La aplicación puede contener muchos controladores de vista, pero debe tener un controlador de vista raíz para controlar todos los controladores de vista. Agregue un controlador a la ventana mediante la creación de una instancia de UIViewController
y su establecimiento en la propiedad Window.RootViewController
:
public class AppDelegate : UIApplicationDelegate
{
// class-level declarations
public override UIWindow Window
{
get;
set;
}
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
// create a new window instance based on the screen size
Window = new UIWindow(UIScreen.MainScreen.Bounds);
var controller = new UIViewController();
controller.View.BackgroundColor = UIColor.LightGray;
Window.RootViewController = controller;
// make the window visible
Window.MakeKeyAndVisible();
return true;
}
}
Cada controlador tiene una vista asociada, a la que se puede acceder desde la propiedad View
. El código anterior cambia la propiedad BackgroundColor
de la vista a UIColor.LightGray
para que sea visible, como se muestra a continuación:
Podríamos establecer cualquier subclase de UIViewController
como la RootViewController
de esta manera, incluidos los controladores de UIKit, así como los que escribimos nosotros mismos. Por ejemplo, el código siguiente agrega un UINavigationController
como RootViewController
:
public class AppDelegate : UIApplicationDelegate
{
// class-level declarations
public override UIWindow Window
{
get;
set;
}
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
// create a new window instance based on the screen size
Window = new UIWindow(UIScreen.MainScreen.Bounds);
var controller = new UIViewController();
controller.View.BackgroundColor = UIColor.LightGray;
controller.Title = "My Controller";
var navController = new UINavigationController(controller);
Window.RootViewController = navController;
// make the window visible
Window.MakeKeyAndVisible();
return true;
}
}
Esto genera el controlador anidado dentro del controlador de navegación, como se muestra a continuación:
Creación de un controlador de vista
Ahora que hemos visto cómo agregar un controlador como el RootViewController
de la ventana, veamos cómo crear un controlador de vista personalizado en el código.
Agregue una nueva clase denominada CustomViewController
como se muestra a continuación:
La clase debe heredar de UIViewController
, que se encuentra en el espacio de nombres UIKit
, como se muestra:
using System;
using UIKit;
namespace CodeOnlyDemo
{
class CustomViewController : UIViewController
{
}
}
Inicialización de la vista
UIViewController
contiene un método denominado ViewDidLoad
al que se llama cuando el controlador de la vista se carga por primera vez en la memoria. Este es un lugar adecuado para realizar la inicialización de la vista, como establecer sus propiedades.
Por ejemplo, el código siguiente agrega un botón y un controlador de eventos para insertar un nuevo controlador de vista en la pila de navegación cuando se presiona el botón:
using System;
using CoreGraphics;
using UIKit;
namespace CodyOnlyDemo
{
public class CustomViewController : UIViewController
{
public CustomViewController ()
{
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
View.BackgroundColor = UIColor.White;
Title = "My Custom View Controller";
var btn = UIButton.FromType (UIButtonType.System);
btn.Frame = new CGRect (20, 200, 280, 44);
btn.SetTitle ("Click Me", UIControlState.Normal);
var user = new UIViewController ();
user.View.BackgroundColor = UIColor.Magenta;
btn.TouchUpInside += (sender, e) => {
this.NavigationController.PushViewController (user, true);
};
View.AddSubview (btn);
}
}
}
Para cargar este controlador en la aplicación y mostrar la navegación sencilla, cree una nueva instancia de CustomViewController
. Cree un nuevo controlador de navegación, pase la instancia del controlador de vista y establezca el nuevo controlador de navegación en el RootViewController
de la ventana en AppDelegate
como antes:
var cvc = new CustomViewController ();
var navController = new UINavigationController (cvc);
Window.RootViewController = navController;
Ahora, cuando se carga la aplicación, el CustomViewController
se carga dentro de un controlador de navegación:
Al hacer clic en el botón, se insertará un nuevo controlador de vista en la pila de navegación:
Creación de la jerarquía de vistas
En el ejemplo anterior, empezamos a crear una interfaz de usuario en el código agregando un botón al controlador de vista.
Las interfaces de usuario de iOS se componen de una jerarquía de vistas. Se agregan vistas adicionales, como etiquetas, botones, controles deslizantes, etc. como subvistas de alguna vista primaria.
Por ejemplo, vamos a editar el CustomViewController
para crear una pantalla de inicio de sesión donde el usuario puede escribir un nombre de usuario y una contraseña. La pantalla constará de dos campos de texto y un botón.
Adición de los campos de texto
En primer lugar, quite el botón y el controlador de eventos que se agregaron en la sección Inicialización de la vista.
Agregue un control para el nombre de usuario mediante la creación e inicialización de un UITextField
y, a continuación, agréguelo a la jerarquía de vistas, como se muestra a continuación:
class CustomViewController : UIViewController
{
UITextField usernameField;
public override void ViewDidLoad()
{
base.ViewDidLoad();
View.BackgroundColor = UIColor.Gray;
nfloat h = 31.0f;
nfloat w = View.Bounds.Width;
usernameField = new UITextField
{
Placeholder = "Enter your username",
BorderStyle = UITextBorderStyle.RoundedRect,
Frame = new CGRect(10, 82, w - 20, h)
};
View.AddSubview(usernameField);
}
}
Cuando creamos el UITextField
, establecimos la propiedad Frame
para definir su ubicación y tamaño. En iOS, la coordenada 0,0 está en la parte superior izquierda con +x a la derecha y +y hacia abajo. Después de establecer el Frame
junto con un par de otras propiedades, llamamos a View.AddSubview
para agregar el UITextField
a la jerarquía de vistas. Esto convierte a usernameField
en una subvista de la instancia UIView
a la que hace referencia la propiedad View
. Se agrega una subvista con un orden z superior a su vista primaria, por lo que aparece delante de la vista primaria en la pantalla.
A continuación se muestra la aplicación con el UITextField
incluido:
Podemos agregar un UITextField
para la contraseña de forma similar, solo que esta vez establecemos la propiedad SecureTextEntry
en true, como se muestra a continuación:
public class CustomViewController : UIViewController
{
UITextField usernameField, passwordField;
public override void ViewDidLoad()
{
// keep the code the username UITextField
passwordField = new UITextField
{
Placeholder = "Enter your password",
BorderStyle = UITextBorderStyle.RoundedRect,
Frame = new CGRect(10, 114, w - 20, h),
SecureTextEntry = true
};
View.AddSubview(usernameField);
View.AddSubview(passwordField);
}
}
Al establecer SecureTextEntry = true
se oculta el texto escrito en el UITextField
por el usuario como se muestra a continuación:
Adición del botón
A continuación, agregaremos un botón para que el usuario pueda enviar el nombre de usuario y la contraseña. El botón se agrega a la jerarquía de vistas como cualquier otro control, pasándolo como argumento al método AddSubview
de la vista primaria de nuevo.
El código siguiente agrega el botón y registra un controlador de eventos para el evento TouchUpInside
:
var submitButton = UIButton.FromType (UIButtonType.RoundedRect);
submitButton.Frame = new CGRect (10, 170, w - 20, 44);
submitButton.SetTitle ("Submit", UIControlState.Normal);
submitButton.TouchUpInside += (sender, e) => {
Console.WriteLine ("Submit button pressed");
};
View.AddSubview(submitButton);
Con esto en su lugar, la pantalla de inicio de sesión aparece ahora como se muestra a continuación:
A diferencia de las versiones anteriores de iOS, el fondo del botón predeterminado es transparente. Modificar la propiedad BackgroundColor
del botón cambia esto:
submitButton.BackgroundColor = UIColor.White;
Dará lugar a un botón cuadrado en lugar del botón redondeado típico. Para obtener el borde redondeado, use el siguiente fragmento de código:
submitButton.Layer.CornerRadius = 5f;
Con estos cambios, la vista tendrá este aspecto:
Adición de varias vistas a la jerarquía de vistas
iOS proporciona una instalación para agregar varias vistas a la jerarquía de vistas mediante AddSubviews
.
View.AddSubviews(new UIView[] { usernameField, passwordField, submitButton });
Adición de la funcionalidad del botón
Cuando se hace clic en un botón, los usuarios esperarán que suceda algo. Por ejemplo, se muestra una alerta o la navegación se realiza en otra pantalla.
Vamos a agregar código para insertar un segundo controlador de vista en la pila de navegación.
En primer lugar, cree el segundo controlador de vista:
var loginVC = new UIViewController () { Title = "Login Success!"};
loginVC.View.BackgroundColor = UIColor.Purple;
A continuación, agregue la funcionalidad al evento TouchUpInside
:
submitButton.TouchUpInside += (sender, e) => {
this.NavigationController.PushViewController (loginVC, true);
};
La navegación se muestra a continuación:
Tenga en cuenta que, de forma predeterminada, cuando se usa un controlador de navegación, iOS proporciona a la aplicación una barra de navegación y un botón Atrás para permitirle volver a través de la pila.
Iteración a través de la jerarquía de vistas
Es posible recorrer en iteración la jerarquía de subvistas y seleccionar cualquier vista determinada. Por ejemplo, para buscar cada UIButton
y asignar a ese botón otro BackgroundColor
, se puede usar el siguiente fragmento de código.
foreach(var subview in View.Subviews)
{
if (subview is UIButton)
{
var btn = subview as UIButton;
btn.BackgroundColor = UIColor.Green;
}
}
Sin embargo, esto no funcionará si la vista en iteración es como UIView
, ya que todas las vistas volverán a ser como UIView
dado que los objetos agregados a la vista primaria heredan de UIView
.
Control de giro
Si el usuario gira el dispositivo a horizontal, los controles no cambian el tamaño adecuado, como se muestra en la captura de pantalla siguiente:
Una manera de corregir esto es estableciendo la propiedad AutoresizingMask
en cada vista. En este caso, queremos que los controles se extiendan horizontalmente, por lo que estableceríamos cada AutoresizingMask
. El ejemplo siguiente es para usernameField
, pero lo mismo tendría que aplicarse a cada gadget en la jerarquía de vistas.
usernameField.AutoresizingMask = UIViewAutoresizing.FlexibleWidth;
Ahora, cuando giramos el dispositivo o el simulador, todo se extiende para rellenar el espacio adicional, como se muestra a continuación:
Creación de vistas personalizadas
Además de usar controles que forman parte de UIKit, también se pueden usar vistas personalizadas. Se puede crear una vista personalizada heredando de UIView
e invalidando Draw
. Vamos a crear una vista personalizada y agregarla a la jerarquía de vistas que se va a mostrar.
Heredar de UIView
Lo primero que debemos hacer es crear una clase para la vista personalizada. Para ello, usaremos la plantilla Clase en Visual Studio para agregar una clase vacía denominada CircleView
. La clase base debe establecerse en UIView
, que como recordaremos está en el espacio de nombres UIKit
. También necesitaremos el espacio de nombres System.Drawing
. Los otros espacios de nombres System.*
no se usarán en este ejemplo, por lo que no dude en quitarlos.
La clase debería tener el siguiente aspecto:
using System;
namespace CodeOnlyDemo
{
class CircleView : UIView
{
}
}
Dibujo en un UIView
Cada UIView
tiene un método Draw
al que llama el sistema cuando debe dibujarse. Draw
nunca debe llamarse directamente. El sistema lo llama durante el procesamiento del bucle de ejecución. La primera vez a través del bucle de ejecución después de agregar una vista a la jerarquía de vistas, se llama a su método Draw
. Las llamadas posteriores a Draw
se producen cuando la vista está marcada como necesaria para dibujarse llamando a SetNeedsDisplay
o SetNeedsDisplayInRect
en la vista.
Podemos agregar código de dibujo a nuestra vista agregando este código dentro del método invalidado Draw
, como se muestra a continuación:
public override void Draw(CGRect rect)
{
base.Draw(rect);
//get graphics context
using (var g = UIGraphics.GetCurrentContext())
{
// set up drawing attributes
g.SetLineWidth(10.0f);
UIColor.Green.SetFill();
UIColor.Blue.SetStroke();
// create geometry
var path = new CGPath();
path.AddArc(Bounds.GetMidX(), Bounds.GetMidY(), 50f, 0, 2.0f * (float)Math.PI, true);
// add geometry to graphics context and draw
g.AddPath(path);
g.DrawPath(CGPathDrawingMode.FillStroke);
}
}
Dado que CircleView
es una UIView
, también podemos establecer propiedades UIView
. Por ejemplo, podemos establecer el BackgroundColor
en el constructor:
public CircleView()
{
BackgroundColor = UIColor.White;
}
Para usar la CircleView
que acabamos de crear, podemos agregarla como subvista a la jerarquía de vistas en un controlador existente, como hicimos con UILabels
y UIButton
anteriores, o podemos cargarla como vista de un nuevo controlador. Vamos a hacer esto último.
Carga de una vista
UIViewController
tiene un método denominado LoadView
al que llama el controlador para crear su vista. Este es un lugar adecuado para crear una vista y asignarla a la propiedad View
del controlador.
En primer lugar, necesitamos un controlador, así que cree una nueva clase vacía denominada CircleController
.
En CircleController
agregue el código siguiente para establecer la View
en una CircleView
(no debe llamar a la implementación de base
en la invalidación):
using UIKit;
namespace CodeOnlyDemo
{
class CircleController : UIViewController
{
CircleView view;
public override void LoadView()
{
view = new CircleView();
View = view;
}
}
}
Por último, es necesario presentar el controlador en tiempo de ejecución. Para ello, agreguemos un controlador de eventos en el botón enviar que hemos agregado anteriormente, como se indica a continuación:
submitButton.TouchUpInside += delegate
{
Console.WriteLine("Submit button clicked");
//circleController is declared as class variable
circleController = new CircleController();
PresentViewController(circleController, true, null);
};
Ahora, cuando ejecutamos la aplicación y pulsamos el botón enviar, se muestra la nueva vista con un círculo:
Creación de una pantalla de inicio
Se muestra una pantalla de inicio cuando la aplicación se inicia como una manera de mostrar a los usuarios que es adaptable. Dado que se muestra una pantalla de inicio cuando se carga la aplicación, no se puede crear en el código, ya que la aplicación todavía se está cargando en la memoria.
Al crear un proyecto de iOS en Visual Studio, se proporciona una pantalla de inicio en forma de un archivo .xib, que se puede encontrar en la carpeta Recursos dentro del proyecto.
Esto se puede editar haciendo doble clic en él y abriéndolo en el Interface Builder de Xcode.
Apple recomienda que se use un archivo .xib o Storyboard para las aplicaciones que tienen como destino iOS 8 o posterior. Al iniciar cualquiera de los archivos en el Interface Builder de Xcode, puede usar clases de tamaño y diseño automático para adaptar el diseño para que se vea bien y se muestre correctamente para todos los tamaños de dispositivo. Se puede usar una imagen de inicio estático además de un archivo .xib o Storyboard para permitir la compatibilidad con aplicaciones destinadas a versiones anteriores.
Para obtener más información sobre cómo crear una pantalla de inicio, consulte los documentos siguientes:
- Crear una pantalla de inicio mediante un archivo .xib
- Administración de pantallas de inicio con guiones gráficos
Importante
A partir de iOS 9, Apple recomienda que los guiones gráficos se usen como método principal para crear una pantalla de inicio.
Creación de una imagen de inicio para aplicaciones anteriores a iOS 8
Se puede usar una imagen estática además de una pantalla de inicio de .xib o Storyboard si la aplicación tiene como destino versiones anteriores a iOS 8.
Esta imagen estática se puede establecer en el archivo Info.plist o como catálogo de activos (para iOS 7) en la aplicación. Deberá proporcionar imágenes independientes para cada tamaño de dispositivo (320x480, 640x960, 640x1136) en el que se pueda ejecutar la aplicación. Para obtener más información sobre los tamaños de la pantalla de inicio, vea la guía Imágenes de pantalla de inicio.
Importante
Si la aplicación no tiene ninguna pantalla de inicio, es posible que observe que no se ajusta completamente a la pantalla. Si este es el caso, debe asegurarse de incluir, al menos, una imagen de 640x1136 denominada Default-568@2x.png
a su Info.plist.
Resumen
En este artículo se describe cómo desarrollar aplicaciones iOS mediante programación en Visual Studio. Hemos visto cómo crear un proyecto a partir de una plantilla de proyecto vacía, que describe cómo crear y agregar un controlador de vista raíz a la ventana. A continuación, mostramos cómo usar controles de UIKit para crear una jerarquía de vistas dentro de un controlador para desarrollar una pantalla de aplicación. A continuación, hemos examinado cómo hacer que las vistas se establezcan adecuadamente en diferentes orientaciones y vimos cómo crear una vista personalizada mediante la subclases UIView
, además de cómo cargar la vista dentro de un controlador. Por último, hemos explorado cómo agregar una pantalla de inicio a una aplicación.