VISITAS:

lunes, 24 de agosto de 2015

SWIFT (V) : Control de flujo

Sea cual sea el lenguaje de programación en el que estemos trabajando, el desarrollo de aplicaciones es siempre un ejercicio de lógica: tomar decisiones en función de ciertas condiciones y ejecutar múltiples veces cierta lógica sobre unos datos. Todo esto se suele denominar control de flujo, y swift tiene su propia sintaxis, no muy diferente por cierto a la de otros lenguajes.

Sentencia for

Swift ofrece dos tipos de for: condition-increment y for-in

Condition-increment for

La sintaxis es similar (aunque no idéntica) a otros lenguajes C-like:

    for var i = 0; i < 15; i++ {
        println(i)
    }

For-in for

Esta estructura de control permite iterar por los datos de una colección (las colecciones se explican más adelante) o por un rango numérico.
La sintaxis es la siguiente:

    for nombre in colección_o_rango {
        ....
    }

Por ejemplo:

    for i in 1...5 {
        println(i)
    }

Este tipo de for también sirve para iterar por los caracteres de un string:

    for c in "Esto es un ejemplo" {
    }

La variable no es obligatorio declararla. Esto puede ser útil cuando, por ejemplo, sólo se quieren contar los elementos de la colección. En este caso se pone el carácter _:

    for  _  in "Esto es una cadena" {
        counter++
    }

Sentencia while

El bucle for se utiliza cuando sabemos de antemano el número de repeticiones. Pero en otras ocasiones necesitamos repetir hasta que se cumpla una determinada condición y no sabemos de antemano el número de repeticiones que habrá que hacer.

Sintaxis de while:

    while condición {
    }

La condición tiene que se de tipo Bool, y se ejecuta hasta que sea false.

Sentencia do .. while

Este bucle es similar al bucle while, excepto que siempre se ejecuta al menos una vez.

    do {
    } while condición

Salir de un bucle: break

A veces es necesario salir del bucle cuando en su interior se detecta alguna condición que obliga a salir. Para ello se utiliza la sentencia break:

    while a > 10 {
        ....
        if otra_condicion {
            break
        }
        ....
    }

Sentencia continue

La sentencia continue hace que volvamos al principio del bucle, saltando todo lo que queda. Suele ser útil para no hacer nada cuando se cumple una determinada condición.

    while a > 10 {
        if personas[a] == "001" {
            continue
        }
        .....
    }

Sentencia if

La sentencia if (y todas sus variantes) es muy similar a la de otros lenguajes de programación:

    if condición_1 {
        .....
    } else if condición_2 {
        .....
    } else if condición_3 {
        .....
    } else {
        .....
    }

Sentencia switch

La sentencia switch es equivalente a if/else if/else, pero se utiliza cuando el número de casos a comprobar es grande:

    switch expresión {
        case valor1:
            .....  // no se pone break
        case valor2:
            .....  // no se pone break
        default:
            ....
    }

Se pueden combinar varios valores:

    switch expresión {
        case valor1, valor2, valor3:
            ..... 
        case valor4, valor5:
            ..... 
        default:
            ....
    }

También se pueden poner rangos:

    switch expresión {
        case valor1 ... valor2:
            ..... 
        default:
            ....
    }

Algo muy novedoso de la sentencia switch en swift es el uso de la cláusula where, que añade una condición adicional a evaluar:

    switch expresión {
        case valor1 where condición:
            ..... 
        default:
            ....
    }




SWIFT (VIII) : Arrays y Diccionarios

Los arrays y los diccionarios de swift son colecciones de otros objetos y son muy similares a los que ya existen en otros lenguajes de programación. La diferencia entre ambos es el modo de acceso a los objetos que contienen: en los arrays se suele acceder por índice y en los diccionarios se suele acceder por clave. Los diccionarios en otros lenguajes de programación se denominan mapas.

Colecciones mutables e inmutables

Antes de empezar a ver tanto arrays como diccionarios, es preciso indicar que estas colecciones en swift pueden ser mutables o inmutables. Una colección inmutable no se puede modificar una vez que se ha inicializado. Para hacer una colección inmutable, la asignamos a una constante (let constante = colección). Por otro lado, las colecciones mutables sí se pueden modificar después de haberse inicializado. Para hacer una colección mutable, se asigna a una variable (var variable = colección).

Arrays

Un array es una colección que guarda múltiples objetos de forma ordenada. Todos los valores que se guardan en un array tienen que ser del mismo tipo.

Inicialización de arrays

Un array puede inicializarse en la creación mediante una colección de valores (array literal):

    var array : [type] = [ value1, value2, value3 ]

Por ejemplo:

    var cadenas : [String] = [ "Hola", "Mundo" ]

Igualmente, podemos crear un array inmutable:

    let plantas : [String] = [ "helecho", "tomillo" ]

No es obligatorio declarar el tipo del array, se puede dejar que el compilador lo infiera (tanto en constantes como en variables):

    let plantas = [ "helecho", "tomillo" ]

También podemos crear un array vacío:

    var lista = [String]()

Existe una forma de crear un array con una serie de elementos repetidos:

    var nombres = [String](count: 12, repeatedValue: "nombre")

Por último, se puede crear un array con la concatenación de otros arrays:

    var lista = lista1 + lista2

Trabajando con arrays

Tamaño de un array:

    array.count

Comprobar si un array está vacío:

    array,isEmpty

Acceder (en lectura y en escritura) a los items de un array (el primer elemento es el índice 0):

    var a = array[3]
    array[3] = 4

Añadir items a un array

Se pueden añadir elementos a un array mediante la función append o mediante el operador +. Los nuevos items se añaden al final del array, aumentando su tamaño:

    array.append("Hola")
    array += [ "Pepe", "Mundo" ]

También se pueden insertar elementos en una posición concreta de un array, desplazando los demás elementos hacia la derecha:

    array.insert("Manzano", atIndex: 3)

Eliminar items de un array

Se puede eliminar un elemento de una posición concreta de un array, desplazando los demás elementos hacia la izquierda:

    array.removeAtIndex(2)

También se puede eliminar el último elemento de un array:

    array.removeLast()

Iterar por un array

Se puede iterar por un array mediante for:

    for arbol in listaArboles {
        println(arbol)
    }

Diccionarios

Los diccionarios en swift, al igual que en otros lenguajes de programación, permiten almacenar datos en forma de parejas clave-valor. Las claves son únicas y permiten acceder a los valores correspondientes. Los diccionarios son colecciones no ordenadas.
Actualmente, swift sólo permite que las claves sean de tipo String, Int, Double y Bool (supongo que más adelante se ampliarán estos tipos utilizando técnicas similares a las de Java).

Inicialización de diccionarios

Forma de crear e inicializar un diccionario:

    var diccionario : [tipo-key: tipo-valor] = [ key1: value1, key2 : value2 ]

Por ejemplo:

    var personas = [ "001":"José Luis", "005":"Pedro" ]

En este caso se podría haber especificado el tipo, pero se puede dejar que el compilador lo infiera:

    var personas : [String: String] = [ "001":"José Luis", "005":"Pedro" ]

Creación de un diccionario vacío:

    var personas = [String: String]()

Trabajando con diccionarios

Número de elementos en un diccionario:

    personas.count

Acceso a un elemento:

    personas["001"]

Escritura:

    personas["001"] = "Samuel"

Añadir elementos a un diccionario

Se puede añadir un elemento a un diccionario directamente, por su clave:

    personas["045"] = "John"

Eliminar elementos de un diccionario

Para eliminar un elemento de un diccionario, ponemos su valor a nil:

    personas["001"] = nil

Iterar por un diccionario

Al igual que ocurría con los arrays, se puede iterar por todos los elementos de un diccionario utilizando un bucle for:

    for (id, nombre) in personas {
        println("Id = \(id) es \(nombre)")
    }






jueves, 20 de agosto de 2015

SWIFT (IV) Operadores y expresiones

Swift tiene prácticamente los mismos operadores que casi todos los lenguajes C-like (C, C++, Java, Objective-C, Javascript, etc), así que este artículo no será muy novedoso para los programadores experimentados en alguno de estos lenguajes.

Operador de asignación

El operador de asignación simplemente asigna el resultado de una expresión (a la derecha) a una variable (a la izquierda):

    var x : Int?
    x = 10 + 45
    x = x! + 1

Operadores aritméticos

    -  si es unario cambiar el signo, si es binario resta
    * multiplicación
    / división
    + suma
    % módulo o resto

Operador de asignación compuesta

    x += 3

Es equivalente a:

    x = x + 3

Operadores de incremento y decremento

    x++;

Es equivalente a:

    x = x + 1

Y también:

    x--

Es equivalente a:

    x = x - 1

Operadores de comparación

Igualdad:    ==
Desigualdad:   !=
Comparaciones numéricas:   <, >, <=, >=

Operadores lógicos booleanos

NOT ! invierte el valor de un booleano
OR || retorna true si uno de sus operandos es true
AND && retorna true si sus dos operandos son true
XOR ^ retorna true si sus dos operandos son distintos (true, false o false,true)

Operadores de rango

Los operadores de rango se suelen emplear en bucles.
La sintaxis del operador de rango básico es:

    a ... b

Esto representa el rango de valores entre a y b, ambos incluidos. Por ejemplo, 3 ... 6 representa los números 3, 4, 5 y 6.
Si se desea no incluir el último número:

    a ..< b

En este caso, el número b no va incluido en el rango.

Operador ternario

Al igual que en muchos lenguajes C-like en swift existe también el operador ternario:

    condición ? expresión_true : expresión_false

El resultado de esta expresión depende del valor de la condición. Si la condición es true, entonces el resultado de la expresión será la expresión_true. Si la condición es false, entonces el resultado de la expresión será la expresión_false.

Operadores de bit

Operador unario NOT de bit, cambia los bits a 1 por 0 y los bits 0 por 1:

    var x : UInt8 = 3      // 00000011
    var y : UInt8 = ~x    // 11111100       

Operador binario AND de bit, realiza un AND a nivel de bit de los dos operandos:

    var x : UInt8 = 12        // 00001100
    var y : UInt8 = 5          // 00000101
    var z : UInt8 = x & y   // 00000100

Operador binario OR de bit, realiza un OR a nivel de bit de los dos operandos:

    var x : UInt8 = 12        // 00001100
    var y : UInt8 = 5          // 00000101
    var z : UInt8 = x | y     // 00001101

Operador binario XOR de bit, realiza un XOR a nivel de bit de los dos operandos:

    var x : UInt8 = 12        // 00001100
    var y : UInt8 = 5          // 00000101
    var z : UInt8 = x ^ y    // 00001001

Desplazamiento de bits a la izquierda, metiendo ceros por la derecha:

    var x : UInt8 = 12       // 00001100
    var z : UInt8 = x << 1 // 00011000

Desplazamiento de bits a la derecha, metiendo ceros por la izquierda:

    var x : UInt8 = 12       // 00001100
    var z : UInt8 = x >> 1 // 00000110

    

martes, 18 de agosto de 2015

SWIFT (VII) : Clases y Objetos

Swift es un lenguaje orientado a objetos para desarrollar aplicaciones iOS y OSX.
No es el objetivo de este capítulo explicar los conceptos de orientación a objetos (para eso existen multitud de libros y artículos), sino explicar cómo estos conceptos se implementan con swift.

Clases

Una clase se define de la siguiente forma:

class NombreDeLaClase : ClasePadre {
    // propiedades
    // métodos de instancia
    // metodos de clase
}

Las propiedades son variables y constantes. Se declaran de la misma forma que las variables y constantes en swift, es decir, con var y let.
Los métodos de instancia son funciones que se invocarán a través de un objeto de la clase. Se declaran de forma similar a las funciones de swift, es decir, con func.
Los métodos de clase son comunes a toda la clase y no se necesita un objeto para invocarlos (son similares a los métodos estáticos de otros lenguajes). Se declaran como funciones normales, pero con class func.

Veamos un ejemplo:

class GraphicObject {
    var x : Float = 0
    var y : Float = 0
    func show() {
        println("x=\(x), y=\(y)")
    }
    class func getMaxWidth() -> Float {
        return 240.0
    }
}

Clase derivada:

class Rectangle : GraphicObject {
    var width : Float = 0
    var height : Float = 0
}

Objetos

Declaración de un objeto:

var obj : Rectangle = Rectangle()

Constructores

En swift, una forma más correcta de llamar a los constructores sería: inicializadores.
Declaración del constructor (nótese que no se pone func en el constructor):

class Rectangle : GraphicObject {
    var width : Float = 0
    var height : Float = 0
    init( w : Float, h : Float ) {
        width = w
        height = h
    }
}

Cuando se instancia el objeto hay que pasar los argumentos que necesita el constructor (si no se pasan los argumentos adecuados el compilador da un error, además, los argumentos deben pasarse nombrados obligatoriamente):

var obj = Rectangle( w: 20.0, h: 10.0 )

Como se puede ver, los constructores no son funciones normales: no empiezan con func; hay que nombrar los argumentos obligatoriamente en la llamada; tienen un nombre especial (init).

Deinicializadores

Un deinicializador (destructor en otros lenguajes) se invoca justo antes de que el objeto se destruya. La destrucción del objeto ocurre cuando el recolector de basura así lo decide.

La sintaxis del deinicializador es especial, aunque es similar a la de un inicializador sin parámetros:

class Rectangle : GraphicObject {
    var width : Float = 0
    var height : Float = 0
    init( w : Float, h : Float ) {
        width = w
        height = h
    }
    deinit {
        // labores de limpieza del objeto
        println("cleanup")
    }
}

Llamada a métodos y acceso a propiedades

El acceso a las propiedades y métodos de un objeto es muy sencillo en swift (similar a otros lenguajes):

var a = obj.width
obj.width = 70.0
obj.show()

Para llamar a un método de clase:

Rectangle.getMaxWidth()

Propiedades almacenadas y calculadas

Las propiedades pueden ser de uno de los siguientes tipos: almacenadas o calculadas.
Las propiedades almacenadas son las que hemos visto hasta ahora, contienen su valor en una variable o una constante.
Una propiedad calculada es el resultado de un cálculo o una lógica. Para implementar variables calculadas tenemos que declarar un método getter y (opcionalmente) un método setter, para leer y escribir el valor de la variable respectivamente. La variable en sí misma podría incluso no existir.

class Rectangle : GraphicObject {
    .......
    var area : Float {
        get {
            return w * h
        }
    }
}

Se puede declarar igualmente el setter con la palabra reservada set.

class Rectangle : GraphicObject {
    .......
    var widthInInches : Float {
        get {
            return width / 2.54
        }
        set(inches) {
            width = inches * 2.54
        }
    }
}

Self

En swift, al igual que en otros muchos lenguajes orientados a objetos, desde dentro de los métodos de instancia se puede acceder al propio objeto. Para ello, se utiliza self (similar a this en otros lenguajes). No es obligatorio utilizar self, pero sí es recomendable para aclarar. Aunque en algunos casos sí es obligatorio, por ejemplo cuando algún nombre de parámetro de función coincido con el nombre de una propiedad:

    func setValues(width : Float, height : Float) {
        self.width = width
        slef.height = height
    }

Sobreescribir métodos

La herencia de clases supone que la clase hija hereda todas las propiedades y métodos de la clase padre, pero además, la clase hija puede añadir nuevas propiedades y métodos, ampliando así la funcionalidad de la clase padre. Pero otra manera de ampliar (o especializar) la funcionalidad de la clase padre es sobreescribiendo métodos en la clase hija:

override func show() {
    println("width=\(width), height=\(height)")
}

Desde un método sobreescrito se puede invocar al método original de la clase padre de la siguiente forma:

override func show() {
    super.show()
    println("width=\(width), height=\(height)")
}

Inicialización de subclases

La inicialización de un objeto se realiza en el inicializador de la clase, como ya hemos visto. En el caso de subclases, la clase hija es responsable de llamar al inicializador de la clase padre:

init( x: Float, y : Float, w : Float, h : Float ) {
    width = w
    height = h
    super.init(x : x, y : y)
}

La llamada al constructor de la clase padre se puede realizar en cualquier punto del inicializador de la clase hija (en algunos lenguajes se obliga a llamarlo al principio).




lunes, 17 de agosto de 2015

SWIFT (I) Tipos de datos, constantes y variables

Tipos de datos

Todas las constantes y variables swift tienen un tipo de datos. El programador puede especificar el tipo de datos de una constante o variable, o bien puede dejar que sea el compilador el que decida el tipo en función del valor que se asigna inicialmente a la constante o variable.

Enteros

Los enteros pueden almacenar números positivos o negativos sin decimales.
Swift proporciona los siguientes tipos enteros: con signo (Int8, Int16, Int32, Int64) y sin signo (UInt8, UInt16, UInt32, UInt64).También proporciona el tipo Int que el compilador asignará al tipo más adecuado para la plataforma. Apple recomienda utilizar el tipo Int, salvo que se desee una longitud específica.
Todos los tipos tienen las propiedades min y max para obtener los valores mínimo y máximo respectivamente de cada tipo.

Punto flotante

Los números en punto flotante pueden tener dígitos decimales.
Swift proporciona dos tipos de punto flotante: Float (32 bits, precisión 6 decimales) y Double (64 bits, precisión 15 decimales).

Boolean

Swift proporciona el tipo Bool con las constantes true y false para definir el tipos booleano.

Carácter

El tipo Character para almacenar un único carácter. Las variables carácter pueden tener un carácter imprimible como "A" o bien un carácter UNICODE: "\u{0058}"
Existen algunos caracteres especiales:

    var newline = "\n"
    var backslash = "\\"
    var doublequote = "\""
    var singlequote = "\'"

String

Un String es una cadena de caracteres.
Los String en swift se pueden construir utilizando "string interpolation":

    var message = "Juan tiene \(edad) años"

Variables

Las variables en swift se declaran mediante la palabra reservada var y se puede añadir opcionalmente el tipo de datos:

    var contador = 7
    var otroContador : Int = 67

Constantes

Una constante es similar a una variable, pero no se puede modificar.
Al igual que las variables, las contantes se declaran mediante la palabra reservada let y se puede añadir opcionalmente el tipo de datos:

    let maximo = 6757
    let nombre : String = "Valor"

Apple recomienda utilizar constantes siempre que sea posible, ya que su rendimiento es superior ligeramente al de las variables.

Tuplas

Las tuplas en swift permiten almacenar varios valores en una única entidad. Los items de una tupla pueden ser de cualquier tipo (dentro de una misma tupla, cada valor puede ser de un tipo diferente).
Por ejemplo:

    let tupla = ( 10, 4.56, "Nombre" )

Se accede a sus componentes por índice (empezando en cero):

    tupla.1  --> 4.56

Se pueden crear tuplas asignando un nombre a cada valor de la tupla:

    let tupla = ( contador : 7, media : 47.5, nombre : "Nombre" )

    tupla.media --> 47.5

Las tuplas son muy útiles para retornar múltiples valores desde una función.






SWIFT & iOS en Español


Swift

  1. Swift: Tipos de datos, constantes y variables
  2. Swift: Tipos optional
  3. Swift: Type cast y chequeo de tipos
  4. Swift: Operadores y expresiones
  5. Swift: Control de flujo
  6. Swift: Funciones y closures
  7. Swift: Clases y objetos
  8. Swift: Arrays y diccionarios

iOS

iOS (I) Autosizing Masks

En primer lugar quiero aclarar que las autosizing masks son el método "antiguo" de crear layouts multi-tamaño en iOS. Apple sacó los autolayout constraints posteriormente y es lo que debería utilizarse en lugar de las autosizing masks. Sin embargo, en mi opinión, es importante conocer y practicar las autosizing masks antes de meterse con autolayouts. Los autolayouts son mucho más genéricos, permiten definir prácticamente todo de forma gráfica y soportan casi todos los tamaños y orientaciones de pantalla.
Podríamos pensar que lo ideal es empezar a aprender autolayouts, ya que es "lo último", y las autosizing masks están "deprecated" por Apple. Y en parte eso sería lo correcto. Pero... yo he intentado aprender los autolayouts sin conocer previamente autosizing masks, y me ha resultado muy muy complicado. Por eso, recomiendo empezar por aquí, a pesar de que tenemos que ser conscientes que quizás este esfuerzo no valga para hacer muchas aplicaciones en el futuro. Para programadores que vienen de versiones de iOS anteriores y que ya conocen autosizing masks, pueden ir directamente a aprender los autolayouts sin problemas. Y por supuesto, como recomendación final antes de meternos en harina: para hacer aplicaciones, utilizar autolayouts constraints, sin duda.

Struts y Springs

Las autoresizing masks están basadas en los conceptos de layoyut "struts y springs"  (Ojo, no viene de los frameworks para aplicaciones web struts y spring).
Se define cómo cambia el tamaño y los márgenes de una vista cuando cambia el tamaño de la vista padre. O sea, para cada vista definimos qué márgenes (top, left, bottom, right) pueden cambiar y qué tamaño (horizontal, vertical) puede cambiar cuando la vista padre cambia de tamaño. Al ancho y alto se les llama springs, y a los márgenes se les llama struts.
Para cada margen o dimensión podemos definir si es fija o flexible, Si es fija se quedará como está cuando la vista padre cambie de tamaño, si es flexible cambiará de tamaño con el padre.
Cada restricción es una máscara (6 en total) y se combinan mediante OR para definir el comportamiento de cada vista ante los cambios de tamaño.
Estas son las máscaras:

  • UIViewAutoresizingFlexibleBottomMargin: permite que cambie la distancia entre el borde inferior de la vista y el borde inferior del padre.





  • UIViewAutoresizingFlexibleTopMargin: permite que cambie la distancia entre el borde superior de la vista y el borde superior del padre.




  • UIViewAutoresizingFlexibleLeftMargin: permite que cambie la distancia entre el borde izquierdo de la vista y el borde izquierdo del padre.




  • UIViewAutoresizingFlexibleRightMargin: permite que cambie la distancia entre el borde derecho de la vista y el borde derecho del padre.




  • UIViewAutoresizingFlexibleHeight: permite que cambie el alto de la vista cambie cuando cambie el alto de la vista padre.
  • UIViewAutoresizingFlexibleWidth: permite que cambie el ancho de la vista cambie cuando cambie el ancho de la vista padre.
Combinando estas máscaras con el operador de bit OR | se define el comportamiento de cada vista ante cambios en la vista padre.
NOTA: Cuando hablo de cambios en la vista padre, no me refiero a que en una aplicación la vista padre vaya a estar cambiando de tamaño (Normalmente, una vez arrancada la aplicación, las vistas no cambian su tamaño). Me refiero a que a partir del diseño con Interface Builder (Xcode), los tamaños de pantalla en los que va a correr nuestra aplicación cambiarán, y es a esos cambios a los que me refiero, a cambios con respecto al diseño de la aplicación. Por ejemplo, nosotros podríamos haber diseñado nuestra aplicación para orientación portrait, pero el usuario la ejecuta en modo landscape; entonces la vista principal cambia su tamaño (ancho > alto, y otros tamaños) y por tanto todas las vistas hijas recursivamente.

Ejemplo

Por ejemplo, imaginemos una vista de dimensiones (10, 10, 50, 50) (x,y,w,h) dentro de otra vista de dimensiones (0, 0, 70, 70).



A esta vista le ponemos la siguiente máscara:

    UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth

De esta forma, sea cual sea el tamaño de la vista padre, la distancia de los bordes a la vista padre será siempre 10 puntos. O sea, si la vista padre aumenta su ancho al doble, o sea 140 puntos, el resultado sería el siguiente:

Y si lo que aumenta es el alto de la vista padre:

Como se ve, al cambiar el tamaño de la vista padre, la distancia de los bordes de la vista a los bordes de la vista padre no cambian, ya que no hemos definido para esta vista ninguna máscara de flexibilidad en los bordes. Lo que sí hemos hecho flexible son el ancho y el alto, y por tanto, eso es lo que se redimensiona al cambiar el tamaño de la vista padre.
Igualmente, si el tamaño de la vista padre se redujera, la vista reduciría su tamaño, pero no cambiarían los márgenes, es decir, la distancia de los bordes.
Hay que tener en cuenta que cuando no se define una máscara, entonces esa máscara es fija, no flexible,

Otro ejemplo

Pongamos ahora la siguiente máscara a la vista anterior:

    UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin

En este segundo caso, hacemos flexible los márgenes inferior y derecho (o sea, la distancia de los bordes inferior y derecho a los mismos márgenes de la vista padre). El resto de márgenes y las dimensiones son fijas y no pueden cambiar.
Si en este caso la vista padre aumenta al doble su tamaño, tanto ancho como alto, tendremos:

Como se ve, las dimensiones de la vista no cambian. Tampoco cambian los márgenes izquierdo y superior. Lo que sí es flexible, y de hecho cambia son los márgenes derecho e inferior.

Autosizing masks en Xcode

Quizás el lector esté pensando ahora mismo cómo puede probar las autosizing masks, ya que Apple las ha sustituido por autolayout constraints. Sin embargo, Apple ha dejado una opción en los proyectos para poder hacer el layout de nuestras escenas utilizando autosizing masks. Lo más probable es que Apple ha dejado esta opción para mantener la compatibilidad con proyectos antiguos que utilizaban esta técnica, pero ahora mismo nos viene bien para poder practicarla y aprender.
Creemos un nuevo proyecto con Xcode, iOS -> Application -> Single View Application. Abrir Main.storyboard
En el panel de Utilities (activarlo con el icono de la derecha)
seleccionar el File inspector (el icono de la izquierda):

En la sección Interface builder document, desactivar las opciones:
  • Use Auto Layout
  • Use Size Classes
Con estas dos opciones desactivadas, nuestro storyboard funcionará con el sistema antiguo de autosizing masks.
Cuando intentemos desactivar la opción Use Auto Layout, Xcode nos pedirá confirmación y además nos indica que también se desactivará la opción Use Size Classes. Y más aún, al no tener size classes, Xcode necesita saber para qué tamaño/dispositivo vamos a hacer el diseño, iPhone o iPad (size classes permite definir el layout para múltiples dispositivos, como se verá en otro artículo). Seleccionemos por ejemplo la opción iPhone.
Ahora ya tenemos nuestro proyecto preparado para trabajar con autosizing masks, en lugar de los modernos autolayout constraints.

En la parte inferior del panel de utilities, seleccionar la object library (el tercer icono):

Buscar el objeto View (Represents a rectangular region in which it draws and receives events) y arrastrarlo a la vista. Lo primero que haremos será cambiarle el color para poder ver cómo cambia y darle una posición y tamaño dentro de la vista padre.
Para cambiarle el color, seleccionar el cuarto icono:
y después cambiar Background.
Para situarlo y darle tamaño, seleccionar el quinto icono. El iPhone 5 tiene un tamaño de 320x568 puntos, así que a nuestra vista le daremos un tamaño algo más pequeño, por ej. (10, 10, 300, 500).
En la sección de autoresizing, quitar todas las máscaras. Por defecto, aparecerán todas activadas (en rojo en los bordes y dentro del recuadro). Para desactivarlas, pulsar sobre cada una de las 6 para que queden en un color muy clarito.
Vamos a previsualizar nuestra vista con distintos tamaños de la vista padre (el dispositivo en este caso). Para ello, tenemos la opción clásica de arrancar el simulador y ejecutar con distintos tipos de dispositivos. Pero esta opción es bastante engorrosa y lenta, ya que hay que cambiar el dispositivo, compilar, y ejecutar la aplicación. Xcode tiene una función de previsualización de una escena en el mismo Interface Builder, sin tener que arrancar el simulador. Veamos cómo hacerlo.
En primer lugar, abrir el asistente (el segundo icono con dos círculos):
Normalmente aparecerá el código swift del controlador asociado a la vista. Pero esto no es lo que queremos. En la parte de arriba de la ventana de código swift, tenemos una especie de pestaña con dos círculos y la etiqueta Automatic. Pulsar esa pestaña y en el menú que aparece, seleccionar Preview. Esto nos cambiará la vista del asistente a una previsualización. En la parte inferior izquierda hay un botón + para añadir nuevas previsualizaciones (nótese que son previsualizaciones de iPhone, no de iPad, ya que al desactivar size classes, hemos elegido el iPhone como dispositivo para nuestra aplicación). Cuando tengamos varias previsualizaciones es muy útil poder moverse por esta pantalla (nótese que no hay barras de scroll). Para ello, se utiliza la rueda del ratón (scroll hacia arriba y hacia abajo) y tecla SHIFT más rueda del ratón (scroll horizontal, izquierda y derecha). Si pulsamos sobre una de las previsualizaciones, podemos borrarla pulsando la tecla de borrar. Además, en cada previsualización podemos ver su versión landscape. Para ello, seleccionamos una previsualización (excepto la primera, que no se puede ni borrar ni rotar) y en la parte de abajo de la previsualización tenemos un botón que permite rotar el dispositivo.

Una vez que sabemos manejar el asistente de previsualizaciones, estamos en condiciones de ver cómo se comporta nuestra vista cuando por ejemplo, rotamos el teléfono. Y como podremos ver, el resultado es desastroso (si hemos quitado todas las autosizing masks). Pero podemos arreglarlo fácilmente: seleccionamos nuestra vista (no la previsualización), elegimos el inspector de tamaños (size inspector) y activamos las dos máscaras internas (width y height) en la sección de autoresizing. Sólo con estos dos cambios, nuestra vista crecerá y decrecerá para adaptarse al tamaño del dispositivo, pero manteniendo los márgenes fijos.

Ahora es el momento de que el lector se ponga manos a la obra y practique con distintos layouts, colocando varias vistas, unas dentro de otras y probando distintas máscaras. Éste es un ejercicio muy útil y que recomiendo (al menos durante una hora o dos) para hacerse con esta técnica (que vuelvo a insistir, está obsoleta hoy). Si el lector se plantea algunos problemas un poco complejos se dará cuenta de que hay algunas posibilidades de layout que son imposibles de llevar a cabo con autoresizing masks. En estos casos sería necesario meter código que trate los eventos de cambio de orientación y cambiar los tamaños y posiciones (esto es algo muy engorroso y que se resuelve con autolayouts, como veremos en otro artículo).

Ejercicio

Aquí propongo a modo de ejercicio, un layout para que el lector practique, aunque creo que es bueno intentar imaginar una aplicación y diseñar su layout con Xcode utilizando autoresizing masks.



Para terminar

Una vez más insisto que las autosizing masks no es la técnica recomendada por Apple hoy día (2015), sino los autolayouts junto con sizing classes.
Pero, ¿cuál es el motivo por el que Apple decidió dejar de utilizar autosizing masks? Realmente, los motivos de Apple no siempre son claros, pero en este caso sí hay razones técnicas que permiten explicar el cambio. Hasta hace poco, no era difícil diseñar una aplicación que funcionara en todos los dispositivos iOS existentes, simplemente había que pensar en iPhone portrait y landscape, iPad portrait y landscape. En total 4 posibilidades. Lo que solían hacer los desarrolladores era hacer 4 storyboards, uno para cada una de estas posibilidades.
Cuando Apple sacó los nuevos modelos de iPhone, el 6 y sobre todo el 6+, el espectro de posibilidades empezó a aumentar, y el futuro nos depara algunas otras posibilidades (por ejemplo, el iPad Pro, que parece que está al caer, aunque ahora mismo son sólo rumores). Esto podría complicar muchísimo el diseño de aplicaciones. Así que parece que Apple decidió abordar definitivamente el problema de múltiples tamaños y resoluciones en los dispositivos (cosa que por cierto, Google abordó desde el principio con Android).
El problema de autosizing masks es que no soporta todas las posibilidades de layout (no es difícil plantearse un ejemplo imposible de realizar), al menos con Interface Builder directamente (sería necesario resolver algunos temas por código). Otro problema es que para cada posibilidad (iPhone, iPad, portrait, landscape, etc) tenemos que hacer un diseño especial (no para casos sencillos, pero sí en la mayoría de los casos).
Para resolver todos estos problemas, Apple "inventó" los autolayouts y los size classes que veremos en un artículo posterior.