Para este segundo proyecto vamos a desarrollar un test de personalidad. La idea de este test es que nos de la respuesta a la siguiente pregunta: ¿Qué país deberías visitar en tus siguientes vacaciones? Es decir, tenemos que diseñar nuestras propias preguntas y respuestas.
**Ojo: el tema del proyecto depende completamente de ustedes, decidan qué tipo de test les gustaría diseñar.
Antes de comenzar a programar, decidan el tema que quieren abordar. Para esta app, hay que desarrollar las siguientes tres secciones:
- Mostrar la pantalla de inicio del quiz.
- Presentar las preguntas y respuestas.
- Desplegar los resultados.
Por esta razón, vamos a necesitar de al menos tres ViewControllers para construir nuestra app: un ViewController para cada sección.
Dentro de la sección de preguntas, considera tres tipos de preguntas:
1. Única respuesta
2. Respuesta múltiple
3. Respuesta en un rango -> ¿Del 1 al 10, qué tanto te gusta ....?
- UIKit: entender los conceptos de Auto Layout, UIStackViews, UINavigationControllers.
- Swift: entender los conceptos de Optionals, Class, Struct.
- Computer Science: entender los conceptos de la arquitectura Modelo Vista Controlador y ciclo de vida de una app.
- Soft skills: trabajar en equipo y fomentar la creatividad para solucionar el problema presentado en este proyecto.
- Link del repositorio en Github donde se encuentra el proyecto.
- Dentro del archivo README.md del repositorio, harás una descripción de tu app junto con screen shots de las vistas. Sean creativas en esta parte, contará como evaluación. Piensa en este archivo como un pitch de tu app donde el usuario será lo único que vea y con base en este archivo, el usuario decidirá si descarga tu app.
- Formen un squad de 4-5 integrantes y piensen en un nombre para su squad.
- Solo van a ocupar una computadora para programar pues es muy importante que entre todas trabajen para cumplir el proyecto; la idea es que se vayan rotando entre todas las integrantes el trabajo de programar y diseñar interfaces. Las demás computadoras las ocuparán para revisar el código visto en clase e investigar temas extra.
- Dentro de la computadora elegida, creen un nuevo repositorio en Github y llámenlo PersonalityQuiz. Al crearlo, indiquen a Github que quieren inicializar el repositorio con un archivo README.md. Dentro de este archivo, enlisten el nombre de todas las integrandes del squad.
- Clonen ese repositorio dentro del escritorio.
- Creen un nuevo proyecto y seleccionen la opción Single View App.
- Para la opción de Product Name: PersonalityQuiz-ECC.
- Team: None
- Organization Team: nombre de su squad.
- Organization Identifier: com.example
- Language: Swift
- User Interface: Storyboard
- Deseleccionen Use Core Data, Include Unit Tests, Include UI Tests
- Guarden el proyecto dentro de la carpeta del repositorio que clonaron en el escritorio. Asegúrense de deseleccionar la opción Create Git repository on my Mac.
-
Abre
Main.storyboard
y agrega dos ViewControllers extra. Para hacer esto, necesitas abrir el menú (Object Library) con todos los componentes de UIKit. -
El primer ViewController servirá como la vista de introducción. Como elementos básicos, necesitarás un label para el título y un botón para comenzar el quiz.
- Agrega un UILabel a la vista.
- Texto del UILabel: Which Country Should You Visit Next?
- Fuente: Georgia
- Tamaño: 30
- Agrega un UIButton a la vista:
- Título del UIButton: Begin Personality Quiz
- Tamaño: 15
- Agrega un UILabel a la vista.
-
Es buena idea usar un UIStackView aquí pues tenemos dos elementos alineados de manera vertical. Mete a los dos elementos (UILabel y UIButton) que creaste en el punto anterior dentro de un UIStackView.
-
Selecciona los dos elementos y da click en el ícono en la parte inferior derecha con una flecha apuntando hacía abajo dentro de un cuadrado y selecciona StackView.
-
Axis: Vertical
-
Distribution y Alignment: Fill
-
-
Usando constraints, alínea el stack view en el centro de la pantalla:
-
Ahora, hay que alinear el StackView para que tenga el mismo ancho que el Safe Area:
-
Finalmente, para que el label se ajuste correctamente selecciona el label y dentro del inspecto de atributos:
- Number of Lines: 0
- Line Break: Word Wrap
-
Agrega emojis dentro de labels en las esquinas y usa Auto Layour para dejarlas fijas en las cuatro esquinas:
-
Ahora, comenzemos a trabajar con el botón. Control + arrastra desde el botón al segundo view controller.
- Selecciona Present Modally
-
Ahora, hay que invocar un segue programático.
- Control + arrastra desde el círculo amarillo con un cuadrado blanco (barra de arriba del ViewController) del segundo ViewController a la vista del tercer ViewController y selecciona show.
- Dale click al segue y:
- Identifier: ResultsSegue
-
Selecciona el segundo ViewController y haz los siguientes pasos:
-
Crea la interfaz de resultados:
-
Desde la librería de Objetos, arrastra un Navigation Item al Navigation Bar del tercer ViewController:
- Cambia Title por Results en el inspector de atributos.
-
Desde la librería de Objetos, arrastra un Bar Button Item al lado derecho del Navigation Bar del tercer ViewController:
- System Item: Done
-
Crea un StackView vertical con dos labels.
- Usa la herramienta de Alineación para alinear el stack view verticalmente.
- Usa la herramienta de Add New Constraints para establecer leading y trailing a 0.
- Pon el número de letra del label de arriba en 50 puntos y 17 puntos para el label de abajo.
- Alignment: Center
- Para el segundo label:
- Number of lines: 0
- Line Break: Word Wrap
-
-
Dentro de la fase de planeación consideraste tres tipos de preguntas. Dentro del segundo ViewController haz lo siguiente:
-
Arrastra un vertical Stack View a la vista y agrega cuatro botones al stack view.
- Spacing: 20
-
Usando la herramienta de Align:
- Centra el stack view verticalmente
-
Usando la herramienta de Add New Constraints:
- Leading edges: 20 px
-
Selecciona el stack view y deselecciona en el inspector de atributos la opción de Installed hasta abajo del menú.
-
Agrega un label y un switch de la librería de objetos y mételos dentro de un stack view horizontal.
- Alignment y Distribution: Fill
-
Duplica el stack view anterior otras tres veces
-
Mete a esos cuatro stack views dentro de otro stack view:
- Axis: Vertical
- Alignment y Distribution: Fill
- Spacing: 20
-
Agrega un botón al centro de la vista y llámalo Submit Answer
-
Usa la herramienta de Align para centrar verticalmente el stack view
-
Usa la herramienta de Add New Constraints y:
- Trailing edges: 20
- Leading edges: 20
-
De nuevo, deseleccionla la opción de Installed para este stack view.
-
Agrega dos labels de la librería de objetos a la vista y mételos dentro de un stack view:
- Axis: Horizontal
- Alignment: Fill
- Distribution: Equal Spacing
-
Agrega un slider a la vista (por arriba del stack view anterior)
-
Selecciona el slider y el horizontal stack view y mételos dentro de un stack view:
- Axis: Vertical
- Distribution y Alignment: Fill
- Spacing: 20
-
Agrega un buton hasta abajo del stack view:
- Título: Submit Answer
-
Usa la herramienta de Align para centrar verticalmente el stack view
-
Usa la herramienta de Add New Constraints y:
- Trailing edges: 20
- Leading edges: 20
-
Finalmente, selecciona la opción de Installed para los primeros dos stack views.
-
Agrega un label hasta arriba de la vista
- Tamaño: 32
- Alignment: Centered
- Lines: 0
- Line break: Word Wrap
-
Usa la herramienta de Add New Constraints y:
- Trailing edges: 0
- Leading edges: 0
- Top edges: 20
-
Agrega un Progress View
- Usa la herramienta de Add New Constraints y:
- Trailing edges: 20
- Leading edges: 20
- Bottom edges: 20
- Usa la herramienta de Add New Constraints y:
-
-
Una vez que tienen los tres ViewControllers en el Storyboard, hay que crear tres clases.
-
Dentro de
Main.storyboard
selecciona cada ViewController y abre el inspector de clases para asignarle su clase correspondiente: [Repite este paso para los otros dos ViewControllers] -
Ahora pasaremos a diseñar las estructuras que modelarán nuestra app.
- File > New > File
- Swift file
- Save As: Question
- Repite el paso anterior pero ahora para un archivo llamado Answer
-
Question.swift
-
Answer.swift
-
Dentro de QuestionViewControllwer.swift
- Crea un arreglo de preguntas dentro de una variable llamada
questions
. Cada pregunta deberá ser de uno de los tres tipos definidos. El texto de las preguntas irá definiendo la personalidad de tu usuario. - Agrega una propiedad llamada
questionIndex
e inicializalo en 0. - Crea tres IBOutlets, uno para cada stack view:
singleStackView
multipleStackView
rangedStackView
- Crea un método llamado
updateUI
y llámalo desde enviewDidLoad
:- Esconde los tres stack views:
stackView.isHidden = true
- Pon el título del navigation bar según la variable
questionIndex
:navigationItem.title ="Question #\(questionIndex + 1)"
- Declara una constante que acceda a la variable
questions
en la casillaquestionIndex
. - Utiliza un
switch
para el tipo de pregunta y haz que aparezca en la vista según el tipo de pregunta.
- Esconde los tres stack views:
- Crea un arreglo de preguntas dentro de una variable llamada
-
Ahora, crea outlets para cada uno de los elementos de los tres stack views (probablemente te sea más fácil arrastrar desde el document outline en lugar desde la vista):
- 1 para el label de hasta arriba:
questionLabel
- 4 botones para el
singleStackView
- 4 labels para el
multipleStackView
- 2 labels para para el
rangedStackView
- 1 outlet para el
progressView
- 1 para el label de hasta arriba:
-
Actualiza el valor de
questionLabel
concurrentQuestion.text
-
Para
questionProgressView
:let totalProgress = Float(questionIndex) / Float(questions.count)
questionProgressView.setProgress(totalProgress, animated: true)
-
Crea una constante:
let currentAnswers = currentQuestion.answers
-
Crea las siguientes funciones:
-
func updateSingleStack(using answers: [Answer]) { singleStackView.isHidden = false singleButton1.setTitle(answers[0].text, for: .normal) singleButton2.setTitle(answers[1].text, for: .normal) singleButton3.setTitle(answers[2].text, for: .normal) singleButton4.setTitle(answers[3].text, for: .normal) }
func updateMultipleStack(using answers: [Answer]) { multipleStackView.isHidden = false multiSwitch1.isOn = false multiSwitch2.isOn = false multiSwitch3.isOn = false multiSwitch4.isOn = false multiLabel1.text = answers[0].text multiLabel2.text = answers[1].text multiLabel3.text = answers[2].text multiLabel4.text = answers[3].text }
func updateRangedStack(using answers: [Answer]) { rangedStackView.isHidden = false rangedSlider.setValue(0.5, animated: false) rangedLabel1.text = answers.first?.text rangedLabel2.text = answers.last?.text }
-
-
Manda a llamar cada una de las funciones dentro de los casos del switch.
-
Crea la siguiente variable:
var answersChosen: [Answer] = []
-
Conecta los 4 botones del single stack view con una conexión tipo IBAction
-
Crea una función IBAction y conéctala con los 4 botones del single stack view.
@IBAction func singleAnswerButtonClicked(_ sender: UIButton) { let currentAnswers = questions[questionIndex].answers switch sender { case singleButton1: answersChosen.append(currentAnswers[0]) case singleButton2: answersChosen.append(currentAnswers[1]) case singleButton3: answersChosen.append(currentAnswers[2]) case singleButton4: answersChosen.append(currentAnswers[3]) default: break } nextQuestion() }
-
Crea 4 outlets para los switches del multi stack view
-
Crea una conexión de tipo IBAction para el botón
Submit Answer
del multi stack view:@IBAction func multipleAnswerButtonClicked(_ sender: Any) { let currentAnswers = questions[questionIndex].answers if multiSwitch1.isOn { answersChosen.append(currentAnswers[0]) } if multiSwitch2.isOn { answersChosen.append(currentAnswers[1]) } if multiSwitch3.isOn { answersChosen.append(currentAnswers[2]) } if multiSwitch4.isOn { answersChosen.append(currentAnswers[3]) } nextQuestion() }
-
Crea un IBOutlet para el slider del ranged stack view
-
Agrega una conexión de tipo IBAction para el botón del ranged stack view:
@IBAction func rangedAnswerButtonClicked(_ sender: Any) { let currentAnswers = questions[questionIndex].answers let index = Int(round(rangedSlider.value * Float(currentAnswers.count - 1))) answersChosen.append(currentAnswers[index]) nextQuestion() }
-
Agrega la siguiente función:
-
func nextQuestion() { questionIndex += 1 if questionIndex < questions.count { updateUI() } else { performSegue(withIdentifier: "ResultsSegue", sender: nil) } }
-
-
Agrega la variable
responses
al ResultsViewController pues es donde recibiremos las respuestas del usuario en QuestionsViewControllervar responses: [Answer]!
-
Para pasar la información, hay que implementar el método
prepare(for segue)
dentro de QuestionsViewController:override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "ResultsSegue" { let resultsViewController = segue.destination as! ResultsViewController resultsViewController.responses = answersChosen } }
-
Agrega los siguientes outlets a ResultsViewController:
- Uno para el label de 50 puntos ->
resultsAnswerLabel
- Uno para el label de 17 puntos ->
resultDefinitionLabel
- Uno para el label de 50 puntos ->
-
Agrega las siguientes líneas en el método
viewDidLoad()
de ResultsViewController:calculatePersonalityResult() navigationItem.hidesBackButton = true
-
Y la función:
func calculatePersonalityResult() { var frequencyOfAnswers: [Type: Int] = [:] let responsesType = responses.map { $0.type } for response in responsesType { frequencyOfAnswers[response] = (frequencyOfAnswers[response] ?? 0) + 1 } let mostCommonAnswer = frequencyOfAnswers.sorted { $0.1 > $1.1}.first!.key resultAnswerLabel.text = "You should visit \(mostCommonAnswer.rawValue)" resultDefinitionLabel.text = mostCommonAnswer.definition }
-
Finalmente, para poder regresar al principio agrega el siguiente método en IntroductionViewController y control + arrastra el botón
Done
hacía el ícono de salida y seleccionaunwindToQuizIntroduction
@IBAction func unwindToQuizIntroduction(segue: UIStoryboardSegue) {}
1. Agrega más quizes y deja que el usuario elija desde la pantall de introducción.
2. Presenta las preguntas en un orden aleatorio.
-
App Development with Swift: https://books.apple.com/us/book/app-development-with-swift/id1219117996
-
The Swift Programing Guide: https://books.apple.com/us/book/the-swift-programming-language-swift-5-0/id881256329