Prototipado rápido e interactivo con áreas de juego de Xcode

Lo que vas a crear

Introducción

Desde su introducción en Xcode 6 junto con Swift, hasta su iteración actual en Xcode 7.3.1, los parques infantiles han recorrido un largo camino. Con nuevas características y mejor estabilidad, se están convirtiendo en una herramienta viable para la creación rápida de prototipos o en la piratería rápida de una prueba de concepto.

Como desarrollador, a veces tienes un destello de inspiración en forma de una idea interesante para una aplicación y quieres codificar rápidamente un prototipo que represente la esencia de tu idea. O simplemente desea verificar su comprensión de cómo se comportará una parte del código UIKit. Si es como yo, preferiría evitar la molestia y la sobrecarga mental de crear un proyecto Xcode y tener que lidiar con una gran variedad de factores, como los tipos de dispositivos y las resoluciones, y la configuración de compilación. Estas decisiones se pueden aplazar hasta después de que haya decidido que vale la pena seguir la idea central..

En este tutorial, creamos un juego de memoria basado en tarjetas, todo dentro de los límites de un área de juegos. Es un juego común y conocido, por lo que no hay crédito por la originalidad allí. El juego consta de ocho pares de cartas idénticas (un total de 16 cartas) colocadas boca abajo en una cuadrícula de 4x4.

El jugador debe voltear dos cartas cuyas caras se revelan brevemente y luego se dan vuelta rápidamente. El objetivo del juego es que el jugador intente y recuerde las posiciones de las cartas y descubra pares idénticos, que luego se eliminan del juego. El juego termina cuando se borra la cuadrícula.

El juego es táctil e incorpora animaciones de vista simples. Aprenderá cómo puede hacer modificaciones a su aplicación y ver el resultado de sus cambios en vivo..

1. Empezando

Arranca Xcode y selecciona Nuevo> Zona de juegos ... de Xcode's Expediente menú. Ponle un nombre al parque, como MemoryGameXCPTut, conjunto Plataforma a iOS, y salva el patio de recreo. Estoy usando Xcode 7.3.1 para este tutorial.

Encontrar su camino alrededor del patio de recreo

Dediquemos un tiempo a familiarizarnos con la interfaz del patio de recreo. Siéntase libre de leer esta sección si ya está familiarizado con los parques infantiles.

Un área de juegos puede tener varias páginas, cada una asociada con su propia vista en vivo y sus propias carpetas de fuentes / recursos. No usaremos varias páginas en este tutorial. Los campos de juego son compatibles con el formato de marcado que le permite agregar texto enriquecido a un patio de recreo y vincular entre las páginas de juegos.

Lo primero que ves después de crear un área de juegos es el editor de fuentes de juegos. Aquí es donde se escribe el código, que tiene un efecto inmediato en la vista en vivo. Una de las formas de alternar la (des) apariencia de la Navegador de proyectos está usando el atajo Comando-0. En el Navegador de proyectos, puedes ver dos carpetas, Fuentes y Recursos.

Fuentes

En el Fuentes carpeta, puede agregar código auxiliar en uno o más archivos Swift, como clases personalizadas, controladores de vista y vistas. Aunque la mayor parte del código que define la lógica de su prototipo va allí, es auxiliar en el sentido de que está escondido en segundo plano cuando está viendo su aplicación en vivo..

La ventaja de poner el código auxiliar en el Fuentes La carpeta es que se compila automáticamente cada vez que modifica y guarda el archivo. De esta manera, obtendrá una respuesta más rápida en la visualización en vivo de los cambios realizados en el patio de recreo. De vuelta en el patio de recreo, puede acceder público Propiedades y métodos que expones en el código auxiliar que afecta el comportamiento de tu aplicación..

Recursos

Puede agregar recursos externos, como imágenes, en el Recursos carpeta.

En este tutorial, con frecuencia debe saltar entre un archivo Swift que creamos en el Fuentes la carpeta y el archivo del área de juegos (técnicamente también es un archivo Swift, excepto que no lo referirá por su nombre de archivo). También hacemos uso de la Editor asistente en el tutorial, haciéndolo mostrar el Línea de tiempo, para ver la salida en vivo junto con el código del patio de recreo. Cualquier cambio que realice en el patio de recreo se reflejará instantáneamente (bueno, dentro de unos segundos) en la salida en vivo. También puede interactuar con la vista en vivo y los elementos de la interfaz de usuario. Para asegurarse de que puede hacer todo esto, eche un vistazo rápido a la figura a continuación.

Correspondiente a los números verdes que he agregado a la figura:

  1. Este botón oculta el Editor asistente para que solo el editor principal sea visible.
  2. Este botón revela la Editor asistente. los Editor asistente Es visible a la derecha del editor principal. Este editor puede ayudarnos mostrándonos archivos relevantes, como la contrapartida del archivo en el editor principal.
  3. De izquierda a derecha, estos dos botones se utilizan respectivamente para alternar la apariencia de Navegador de proyectos y la consola de depuración. En la consola, podemos inspeccionar el resultado de las declaraciones de impresión, entre otras cosas..
  4. los barra de salto en la parte superior del editor principal también se puede utilizar para navegar a un archivo en particular. Al hacer doble clic en el nombre del proyecto, regresa al área de juegos. Alternativamente, también puede utilizar el Navegador de proyectos.

A veces, al ver el patio de recreo, debe asegurarse de que Editor asistente está mostrando el Línea de tiempo en lugar de algún otro archivo. La siguiente figura muestra cómo hacer esto. En el Editor asistente, seleccionar Línea de tiempo, la contraparte del patio de recreo, en lugar de Manual, lo que le permite mostrar cualquier archivo en el Editor asistente.

Cuando está editando un archivo fuente de la Fuentes carpeta, como su contraparte, la Editor asistente muestra la interfaz de su código, es decir, declaraciones y prototipos de funciones sin sus implementaciones. Prefiero ocultar el Editor asistente cuando estoy trabajando en un archivo en el Fuentes carpeta y solo expone la Editor asistente En el patio de recreo para ver la vista en vivo..

Para acceder a las habilidades especiales de los parques infantiles, debe importar el módulo XCPlayground.

importar XCPlayground

Usted establece el vista en vivo propiedad de la página actual del XCPlaygroundPage objeto a un objeto que se ajusta a la  XCPlaygroundLiveViewable protocolo. Esto puede ser una clase personalizada o puede ser una clase personalizada. Vista o UIViewController ejemplo.

Agregar archivos a la carpeta Fuentes / Recursos

He agregado algunas imágenes con las que podemos trabajar en este tutorial. Descargue las imágenes, extraiga el archivo y agregue las imágenes en el Imágenes carpeta a la Recursos Carpeta del parque infantil en el. Navegador de proyectos.

Asegúrese de arrastrar solo las imágenes para que cada archivo de imagen se encuentre en el Recursos carpeta, no en Recursos / Imágenes.

Eliminar el código en el patio de recreo. Haga clic derecho en el Fuentes carpeta y seleccione Archivo nuevo desde el menu. Establece el nombre del archivo en Game.swift.

2. Escribir clases y métodos de ayuda

Agregue el siguiente código a Game.swift. Asegúrese de guardar el archivo después de cada adición de código.

importar UIKit importar XCPlayground importar GameplayKit // (1) extensión pública UIImage // (2) public convenence init? (color: UIColor, tamaño: CGSize = CGSize (ancho: 1, altura: 1)) let rect = CGRect ( origen: .zero, tamaño: tamaño) UIGraphicsBeginImageContextWithOptions (rect.size, false, 0.0) color.setFill () UIRectFill (rect) let image = UIGraphicsGetImageFromCerca de las partes de las partes de la casa: Cálculo de la imagen () .init (CGImage: cgImage) let cardWidth = CGFloat (120) // (3) let cardHeight = CGFloat (141) public class Tarjeta: UIImageView // (4) public let x: Int public let y: Int public init (imagen: UIImage ?, x: Int, y: Int) self.x = x self.y = y super.init (image: image) self.backgroundColor = .grayColor () self.layer.cornerRadius = 10.0 self .userInteractionEnabled = true ¿Se requiere inicio público? (codificador aDecoder: NSCoder) fatalError ("init (coder :) no se ha implementado")

He añadido algunos comentarios numerados para explicar algunas secciones de la implementación:

  1. Además de UIKit y XCPlayground, nosotros tambien estamos importando GamePlayKit. Este marco incluye un método conveniente que nos ayudará a implementar un método para mezclar aleatoriamente una matriz.
  2. Esta extensión en UIImage nos permite, con la ayuda de UIKit Métodos, para hacer imágenes con un color sólido de cualquier tamaño que queramos. Usaremos esto para establecer la imagen de fondo inicial de las cartas.
  3. los tarjeta altura y Ancho de tarjeta las constantes representan los tamaños de imagen de la tarjeta en función de los cuales calcularemos otros tamaños.
  4. los Tarjeta clase, heredando de UIImageView, representa una carta. A pesar de que establecemos algunas propiedades en el Tarjeta clase, el propósito principal de crear esta clase es ayudarnos a identificar e iterar las subvistas que corresponden a las cartas en el juego. Las cartas también tienen propiedades. X y y para recordar su posición en la grilla.

3. Controlador de vista

Agregue el siguiente código a Game.swift, Inmediatamente después del código anterior:

clase pública GameController: UIViewController // (1): variables públicas para que podamos manipularlas en el patio de juegos public var padding = CGFloat (20) / * didSet resetGrid () * / public var backImage: UIImage = UIImage ( color: .redColor (), tamaño: CGSize (ancho: ancho de tarjeta, altura: altura de la tarjeta))! // (2): propiedades calculadas var viewWidth: CGFloat get return 4 * cardWidth + 5 * padding var viewHeight: CGFloat get return 4 * cardHeight + 5 * padding var shuffledNumbers = [Int] () // almacena números de tarjetas barajadas // var firstCard: ¿Tarjeta? // descomentar más tarde public init () super.init (nibName: nil, bundle: nil) preferredContentSize = CGSize (width: viewWidth, height: viewHeight) shuffle () setupGrid () // uncment later: // let tap = UITapGestureRecognizer (target: self, action: #selector (GameController.handleTap (_ :))) // view.addGestureRecognizer (tap) public init? necesario (codificador aDecoder: NSCoder) fatalError ("init (coder :) no ha sido implementado ") public override func loadView () view = UIView () view.backgroundColor = .blueColor () view.frame = CGRect (x: 0, y: 0, width: viewWidth, height: viewHeight) // ( 3): uso de GameplayKit API para generar una mezcla de la matriz [1, 1, 2, 2,…, 8, 8] func shuffle () let numbers = (1… 8) .flatMap [$ 0, $ 0] shuffledNumbers = GKRandomSource.sharedRandom (). arrayByShufflingObjectsInArray (números) como! [Int] // (4): Convertir de la posición de la tarjeta en la cuadrícula a índice en la matriz de números de tarjeta aleatoria func cardNumberAt (x: Int, _ y: Int) -> Int assert (0 <= x && x < 4 && 0 <= y && y < 4) return shuffledNumbers[4 * x + y]  // (5): Position of card's center in superview func centerOfCardAt(x: Int, _ y: Int) -> CGPoint afirmar (0 <= x && x < 4 && 0 <= y && y < 4) let (w, h) = (cardWidth + padding, cardHeight + padding) return CGPoint( x: CGFloat(x) * w + w/2 + padding/2, y: CGFloat(y) * h + h/2 + padding/2)  // (6): setup the subviews func setupGrid()  for i in 0… <4  for j in 0… <4  let n = cardNumberAt(i, j) let card = Card(image: UIImage(named: String(n)), x: i, y: j) card.tag = n card.center = centerOfCardAt(i, j) view.addSubview(card)    // (7): reset grid /* func resetGrid()  view.frame = CGRect(x: 0, y: 0, width: viewWidth, height: viewHeight) for v in view.subviews  if let card = v as? Card  card.center = centerOfCardAt(card.x, card.y)    */ override public func viewDidAppear(animated: Bool)  for v in view.subviews  if let card = v as? Card  // (8): failable casting UIView.transitionWithView( card, duration: 1.0, options: .TransitionFlipFromLeft, animations:  card.image = self.backImage , completion: nil)    
  1. Las dos propiedades, relleno y VolverImagen, son declarados público Para que podamos acceder a ellos en el patio de recreo más adelante. Representan el espacio en blanco que rodea a las tarjetas en la cuadrícula y la imagen que se muestra en la parte posterior de cada tarjeta, respectivamente. Tenga en cuenta que a ambas propiedades se les han dado valores iniciales, que representan un relleno de 20 y un color rojo sólido para la imagen lateral sin cara de la tarjeta. Puedes ignorar el código comentado por ahora.
  2. Estamos calculando el ancho y la altura deseados de las vistas por medio de propiedades computadas. Para entender el vista ancho cálculo, recuerde que hay cuatro tarjetas en cada fila y también debemos tener en cuenta el relleno de cada tarjeta. La misma idea se aplica a la vistaHeight cálculo.
  3. El código (1… 8) .flatMap [$ 0, $ 0] Es una forma concisa de producir la matriz. [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8]. Si no está familiarizado con la programación funcional, también puede escribir un para-bucle para generar la matriz. Usando métodos de la GamePlayKit marco, barajamos los números en la matriz. Los números corresponden a los ocho pares de cartas. Cada número representa la imagen de la tarjeta del mismo nombre (por ejemplo, un valor de 1 en ShuffledArray corresponde a 1.png).
  4. Escribimos un método que mapea la ubicación de una tarjeta en la cuadrícula 4x4 a su ubicación en el números aleatorios matriz de longitud 16. El factor 4 en el cálculo aritmético refleja el hecho de que tenemos cuatro cartas por fila.
  5. También tenemos un método que determina la posición de una tarjeta (su centrar propiedad) en la cuadrícula basada en las dimensiones de la tarjeta y el relleno.
  6. los setupGrid () El método se llama durante la inicialización del controlador de vista. Se establece el 4x4. Tarjeta cuadrícula. También asigna la identidad de cada tarjeta en función de la números aleatorios matriz y lo almacena en el etiqueta Propiedad heredada de la clase base de la tarjeta., Vista. En la lógica del juego, comparamos la etiqueta valores para averiguar si dos cartas coinciden o no. Este esquema de modelado bastante rudimentario sirve lo suficientemente bien para nuestras necesidades actuales.
  7. Esta pieza de código no utilizada actualmente nos ayudará a reposicionar las tarjetas en caso de que el relleno cambie. Recuerden que declaramos la relleno propiedad como propiedad pública para que podamos acceder a ella en el patio de recreo.
  8. El codigo en viewDidAppear (_ :) se ejecuta inmediatamente después de que la vista del controlador de vista se vuelve visible. Recorremos las subvistas de la vista y, si la subvista es una instancia de Tarjeta clase, (verificada a través del como? operador de downcasting failable) el cuerpo de la Si-declaración define la transición a realizar. Aquí es donde cambiaremos la imagen que se muestra en las tarjetas, de la imagen de caricatura que define la cara de cada tarjeta a la (común) VolverImagen De todas las cartas. Esta transición se acompaña de una animación de giro de izquierda a derecha que da la apariencia de que las cartas se han volcado físicamente. Si no estás familiarizado con cómo Vista Las animaciones funcionan, esto puede parecer un poco extraño. Aunque agregamos la animación de cada tarjeta de forma secuencial en un bucle, las animaciones se agrupan en una sola transacción de animación y se ejecutan al mismo tiempo, es decir, las tarjetas se juntan.

Vuelva a visitar el patio de recreo y reemplace cualquier texto en el editor con lo siguiente:

importar XCPlayground importar UIKit dejar gc = GameController () XCPlaygroundPage.currentPage.liveView = gc

Asegúrese de que la línea de tiempo es visible. La vista del controlador de vista debería cobrar vida y mostrarnos una cuadrícula de tarjetas 4x4 con dibujos animados lindos de animales que se voltean para mostrarnos el reverso de las tarjetas. En este momento, no podemos hacer mucho con esta vista porque aún no hemos programado ninguna interacción en ella. Pero definitivamente es un comienzo..

4. Modificar variables en el patio de recreo

Ahora cambiemos las caras posteriores de las tarjetas de rojo sólido a una imagen, específicamente b.png en el Recursos carpeta. Agrega la siguiente línea al fondo del patio.

gc.backImage = UIImage (llamado: "b")!

Después de uno o dos segundos, verás que los lados posteriores de las cartas han cambiado de rojo a una mano de dibujos animados..

Ahora intentemos alterar el relleno propiedad, que asignamos un valor predeterminado de 20 en Game.swift. El espacio entre las tarjetas debe aumentar como resultado. Agregue la siguiente línea a la parte inferior del patio de recreo:

gc.padding = 75

Espere a que se actualice la vista en vivo y vea que ... nada ha cambiado.

5. Un breve desvío

Para comprender lo que está sucediendo, debe tener en cuenta que las entidades, como los controladores de vista y sus vistas asociadas, tienen un ciclo de vida complejo. Vamos a enfocarnos en lo último, es decir, las opiniones. La creación y actualización de la vista de un controlador de vista es un proceso de varias etapas. En puntos específicos del ciclo de vida de la vista, se envían notificaciones al UIViewController, Informándolo de lo que está pasando. Más importante aún, el programador puede conectarse a estas notificaciones al insertar código para dirigir y personalizar este proceso..

los loadView () y viewDidAppear (_ :) Los métodos son dos métodos que utilizamos para engancharnos en el ciclo de vida de la vista. Este tema está algo involucrado y está más allá del alcance de esta discusión, pero lo que nos importa es que el código en el patio de recreo, después de la asignación del controlador de vista como el de patio de recreo. vista en vivo, Se ejecuta algún tiempo entre la llamada a viewWillAppear (_ :) y la llamada a viewDidAppear (_ :). Puede verificar esto modificando alguna propiedad en el patio de recreo y agregue declaraciones impresas a estos dos métodos para mostrar el valor de esta propiedad.

El problema con el valor de relleno no tener el efecto visual esperado es que, para ese momento, la vista y sus subvistas ya se han presentado. Tenga en cuenta que, siempre que realice un cambio en el código, el patio de recreo se volverá a ejecutar desde el principio. En ese sentido, este problema no es específico de los parques infantiles. Incluso si estuviera desarrollando un código para ejecutar en el simulador o en un dispositivo físico, muchas veces necesitaría escribir un código adicional para asegurarse de que el cambio en el valor de una propiedad tenga el efecto deseado en la apariencia o el contenido de la vista..

Podría preguntar por qué pudimos cambiar el valor de la VolverImagen Propiedad y ver el resultado sin hacer nada especial. Observar que el VolverImagen La propiedad se utiliza realmente por primera vez en viewDidAppear (_ :), En ese momento ya ha recogido su nuevo valor..

6. Observando propiedades y tomando acción

Nuestra forma de lidiar con esta situación será monitorear los cambios en el valor de relleno y redimensionar / reposicionar la vista y subvistas. Afortunadamente, esto es fácil de hacer con Swift. propiedad observando característica. Comience por descomentar el código de la resetGrid () método en Game.swift:

// (7): restablecer la cuadrícula func resetGrid () view.frame = CGRect (x: 0, y: 0, width: viewWidth, height: viewHeight) para v en view.subviews si let card = v as? Tarjeta card.center = centerOfCardAt (card.x, card.y)

Este método vuelve a calcular la posición del marco de la vista y la de cada Tarjeta objeto basado en los nuevos valores de vista ancho y vistaHeight. Recuerde que estas propiedades se calculan en función del valor de relleno, que acaba de ser modificado.

Además, modifique el código para relleno usar el se estableció observador cuyo cuerpo, como su nombre lo indica, se ejecuta cada vez que configuramos el valor de relleno:

// (1): variables públicas para que podamos manipularlas en el patio de juegos public var padding = CGFloat (20) didSet resetGrid ()

los resetGrid () El método se activa y la vista se actualiza para reflejar el nuevo espaciado. Puedes verificar esto en el patio de recreo..

Parece que pudimos arreglar las cosas con bastante facilidad. En realidad, cuando decidí por primera vez que quería poder interactuar con el relleno propiedad, tuve que volver y hacer cambios en el código en Game.swift. Por ejemplo, tuve que abstraer la Tarjeta cálculo del centro en una función separada (centerOfCardAt (_: _ :)) para (re) calcular de forma limpia e independiente las posiciones de las tarjetas cuando sea necesario colocarlas.

Haciendo propiedades computadas para vista ancho y vistaHeight También ayudó. Si bien este tipo de reescritura es algo para lo que debe estar preparado como una compensación por no hacer mucho diseño por adelantado, se puede reducir con cierta previsión y experiencia..

7. Lógica de juego e interacción táctil

Ahora es el momento de implementar la lógica del juego y permitirnos interactuar con él a través del toque. Comience por descomentar el FirstCard declaración de propiedad en el Control de juego clase:

var firstCard: Tarjeta?

Recordemos que la lógica del juego consiste en revelar dos cartas, una después de la otra. Esta variable realiza un seguimiento de si una carta lanzada por el jugador es la primera de las dos o no.

Agregue el siguiente método a la parte inferior de la Control de juego clase, antes de la llave de rizo de terminación:

func handleTap (gr: UITapGestureRecognizer) let v = view.hitTest (gr.locationInView (view), withEvent: nil)! si deja la tarjeta = v como? Tarjeta UIView.transitionWithView (tarjeta, duración: 0.5, opciones: .TransitionFlipFromLeft, animaciones: card.image = UIImage (llamado: String (card.tag))) // controlador de finalización al final: _ en card.userInteractionEnabled = falso si se deja pCard = self.firstCard si pCard.tag == card.tag UIView.animateWithDuration (0.5, animaciones: card.alpha = 0.0, finalización: _ en card.removeFromSuperview ()) UIView.animateWithDuration (0.5, animaciones: pCard.alpha = 0.0, finalización: _ en pCard.removeFromSuperview ()) else UIView.transitionWithView (tarjeta, duración: 0.5, opciones: .TransitionFlipFromLeft, animaciones: card.image = self.backImage) _ in card.userInteractionEnabled = true UIView.transitionWithView (pCard, duración: 0.5, opciones: .TransitionFlipFromLeft, animaciones: pCard.image = self.backImage) _ in pCard.userInteractionEnabled = true  self.firstCard = nil else self.firstCard = card

Ese es un método largo. Esto se debe a que reúne todo el manejo táctil requerido, la lógica del juego y las animaciones asociadas en un solo método. Veamos cómo funciona este método:

  • Primero, hay una verificación para asegurarse de que el usuario realmente tocó un Tarjeta ejemplo. Esto es lo mismo como? construcción que usamos antes.
  • Si el usuario toca un Tarjeta Por ejemplo, lo volteamos usando una animación similar a la que implementamos anteriormente. El único aspecto nuevo es que usamos el controlador de finalización, que se ejecuta después de que se complete la animación, para deshabilitar temporalmente las interacciones táctiles para esa tarjeta en particular al configurar el userInteractionEnabled Propiedad de la tarjeta. Esto evita que el jugador voltee la misma carta. Nota la _ en Construcción que se usa varias veces en este método. Esto es solo para decir que queremos ignorar el Bool parámetro que toma el controlador de finalización.
  • Ejecutamos código basado en si el FirstCard se le ha asignado un valor no nulo mediante un enlace opcional, el familiar de Swift si se deja construir.
  • Si FirstCard es no nula, entonces esta fue la segunda carta de la secuencia que el jugador entregó. Ahora necesitamos comparar la cara de esta tarjeta con la anterior (comparando la etiqueta valores) para ver si tenemos una coincidencia o no. Si lo hiciéramos, animamos las tarjetas desvaneciéndose (estableciendo su alfa a 0). También eliminamos estas tarjetas de la vista. Si las etiquetas no son iguales, lo que significa que las tarjetas no coinciden, simplemente las volteamos hacia abajo y configuramos userInteractionEnabled a cierto Para que el usuario pueda seleccionarlos de nuevo..
  • Basado en el valor actual de FirstCard, lo configuramos a cualquiera nulo o a la tarjeta actual. Así es como cambiamos el comportamiento del código entre dos toques sucesivos..

Finalmente, descomente las siguientes dos afirmaciones en el Control de juegoInicializador que agrega un reconocedor de gestos de toque a la vista. Cuando el reconocedor del gesto de toque detecta un toque, el handleTap () Se invoca el método:

let tap = UITapGestureRecognizer (target: self, action: #selector (GameController.handleTap (_ :))) view.addGestureRecognizer (tap)

Regresa a la línea de tiempo del patio de recreo y juega el juego de memoria. Siéntase libre de disminuir el gran relleno le asignamos un poco antes.

El codigo en handleTap (_ :) Es prácticamente la versión sin adornos de lo que escribí la primera vez. Uno podría plantear la objeción de que, como un solo método, hace demasiado. O que el código no esté lo suficientemente orientado a objetos y que la lógica y las animaciones de volteado de la tarjeta deben abstraerse cuidadosamente en métodos de Tarjeta clase. Si bien estas objeciones no son inválidas per se, recuerde que la creación rápida de prototipos es el objetivo de este tutorial y como no previmos ninguna necesidad de interactuar con esta parte del código en el patio de recreo, podríamos permitirnos ser un poco más "hack-ish".

Una vez que tengamos algo funcionando y decidamos que queremos continuar con la idea, ciertamente tendremos que considerar la refactorización del código. En otras palabras, primero haz que funcione, luego hazlo rápido / elegante / bonito / ...

8. Toque de manejo en el patio de recreo

Si bien la parte principal del tutorial ha finalizado, como un aparte interesante, quiero mostrarle cómo podemos escribir el código de manejo táctil directamente en el patio de recreo. Primero agregaremos un método a la Control de juego Clase que nos permite echar un vistazo a las caras de las cartas. Agregue el siguiente código a la Control de juego clase, inmediatamente después de la handleTap (_ :) método:

public func quickPeek () for v in view.subviews if let card = v as? Tarjeta card.userInteractionEnabled = false UIView.transitionWithView (tarjeta, duración: 1.0, opciones: .TransitionFlipFromLeft, animaciones: card.image = UIImage (llamada: String (card.tag)))) _ in UIView.transitionWithView (tarjeta , duración: 1.0, opciones: .TransitionFlipFromLeft, animaciones: card.image = self.backImage) _ in card.userInteractionEnabled = true

Supongamos que deseamos la posibilidad de activar o desactivar esta función de "vista rápida" desde el patio de recreo. Una forma de hacerlo sería crear un público Bool propiedad en el Control de juego Clase que podríamos poner en el patio de recreo. Y, por supuesto, tendríamos que escribir un gestor de gestos en el Control de juego Clase, activada por un gesto diferente, que invocaría. vistazo rápido().

Otra forma sería escribir el código de manejo de gestos directamente en el patio de recreo. Una ventaja de hacerlo de esta manera es que podríamos incorporar algún código personalizado además de llamar vistazo rápido(). Esto es lo que haremos a continuación. Agregue el siguiente código a la parte inferior del patio de recreo:

class LPGR static var counter = 0 @objc static func longPressed (lp: UILongPressGestureRecognizer) if lp.state == .Began gc.quickPeek () counter + = 1 print ("Usted asomó \ (counter) time (s) . ") let longPress = UILongPressGestureRecognizer (target: LPGR.self, action: #selector (LPGR.longPressed)) longPress.minimumPressDuration = 2.0 gc.view.addGestureRecognizer (longPress)

Para activar la función de búsqueda rápida, utilizaremos un gesto de pulsación prolongada, es decir, el jugador mantendrá el dedo en la pantalla durante un período de tiempo determinado. Usamos dos segundos como umbral..

Para manejar el gesto, creamos una clase., LPGR (pulsador largo del reconocedor de gestos abreviado), con un estático propiedad variable, mostrador, para realizar un seguimiento de cuántas veces miramos, y una estático método Presionado largo (_ :) para manejar el gesto.

Utilizando el estático calificador, podemos evitar tener que crear un LPGR instancia porque las entidades declaradas estáticas están asociadas con el LPGR tipo (clase) en lugar de con una instancia particular.

Aparte de eso, no hay una ventaja particular para este enfoque. Por razones complicadas, tenemos que marcar el método como @objc para mantener el compilador feliz. Tenga en cuenta