[Udemy] Section 9

서희찬·2022년 5월 29일
0

swift

목록 보기
12/17
post-thumbnail

이번 섹션을 통해서 우린 아래를 배우게 된다.

  • show Quiz Question
  • Check the Answers
  • Learn About Swift Structures.
  • Learn about design patterns and use the Model View Controller pattern
  • Learn about Swift Functions that can have outputs.
  • Understand immutability

그럼 시작해보자.

우선, 이렇게 아웃렛이랑 액션을 짜준다.

이제 우리가 실행했을때 Question Text에 글을 넣을려면 어떻게 해야할까?

questionLabel.text = "Four ~ "

을 viewDidLoad함수안에 작성해주면 된다.

그렇게 되면 화면이 켜지고 텍스트에 우리가 작성한것이 들어가지게 된다.
근데 질문을 여러개 하고싶으면 이제 어떻게 바꿔주면 될까 ?!

바로 배열을 사용하면 된다.


import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var falseButton: UIButton!
    @IBOutlet weak var trueButton: UIButton!
    
    @IBOutlet weak var progressBar: UIProgressView!
    @IBOutlet weak var questionLabel: UILabel!
    
    let quiz = [
        "Four + Two is euqal is Six",
        "Six + Two is euqal is ten",
        "Five + Two is euqal is nine",
    ]
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        questionLabel.text = quiz[0]
        
        
        
    }
    @IBAction func answerButtonPressed(_ sender: UIButton) {
    }
    

}

이렇게 되면 배열의 한개의 요소를 가져올 수 있다.
하지만 우리는 버튼을 클릭할때마다 다음 요소를 가지고 오게 하고싶다.
그렇다면 어떻게 해야할까?

//
//  ViewController.swift
//  Quizzler-iOS13
//
//  Created by Angela Yu on 12/07/2019.
//  Copyright © 2019 The App Brewery. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var falseButton: UIButton!
    @IBOutlet weak var trueButton: UIButton!
    
    @IBOutlet weak var progressBar: UIProgressView!
    @IBOutlet weak var questionLabel: UILabel!
    
    let quiz = [
        "Four + Two is euqal is Six",
        "Six + Two is euqal is ten",
        "Five + Two is euqal is nine",
    ]
    
    var questionNumber = 0
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        questionLabel.text = quiz[questionNumber]
        
        
        
    }
    @IBAction func answerButtonPressed(_ sender: UIButton) {
        questionNumber += 1 
    }
    

}

변수를 만들어서 인덱스에 넣어주면 된당
근데 넣어서 작동해줘도 우리가 원하는방식대로 안넘어가진다.

그렇기 때문에

//
//  ViewController.swift
//  Quizzler-iOS13
//
//  Created by Angela Yu on 12/07/2019.
//  Copyright © 2019 The App Brewery. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var falseButton: UIButton!
    @IBOutlet weak var trueButton: UIButton!
    
    @IBOutlet weak var progressBar: UIProgressView!
    @IBOutlet weak var questionLabel: UILabel!
    
    let quiz = [
        "Four + Two is euqal is Six",
        "Six + Two is euqal is ten",
        "Five + Two is euqal is nine",
    ]
    
    var questionNumber = 0
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        updateUI()
        
    }
    @IBAction func answerButtonPressed(_ sender: UIButton) {
        questionNumber += 1
        updateUI()
    }
    
    func updateUI(){
        questionLabel.text = quiz[questionNumber]
        
    }

}

이런 방식으로 작성해주면 우리가 원하는 방식대로 넘어가는것이 확인 가능하다.

이제 어떻게 T/F인지 체크하는 법을 배워보자
2차원 배열을 만들어서
각 질문마다 참,거짓을 줘보자

//
//  ViewController.swift
//  Quizzler-iOS13
//
//  Created by Angela Yu on 12/07/2019.
//  Copyright © 2019 The App Brewery. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var falseButton: UIButton!
    @IBOutlet weak var trueButton: UIButton!
    
    @IBOutlet weak var progressBar: UIProgressView!
    @IBOutlet weak var questionLabel: UILabel!
    
    let quiz = [
        ["Four + Two is euqal is Six","True"],
        ["Six + Two is euqal is Eight","True"],
        ["Five + Two is euqal is nine","False"]
    ]
    
    var questionNumber = 0
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        updateUI()
        
    }
    @IBAction func answerButtonPressed(_ sender: UIButton) {
        
        let userAnswer = sender.currentTitle // True, False
        let actualAnswer = quiz[questionNumber][1] // 실제 답
        
        if userAnswer == actualAnswer{
            print("right!")
        }else {
            print("Worng!")
        }
        
                
        
        questionNumber += 1
        updateUI()
    }
    
    func updateUI(){
        questionLabel.text = quiz[questionNumber][0]
        
    }

}

이런식으로 2차원 배열을 줘서 질문에 True/False String값을 넣어주고, 사용자가 선택하는 userAnswer과 actualAnswer를 비교해서 Right! Worng!을 출력하는 코드를 추가하였다.

그렇게 테스트를 진행해보는데 3개의 질문이 끝나면

이렇게 에러가 뜨면서 튕긴다.
사용자는 영문도 모른채 튕기게 되어서 어랏..!?하게된다.
그러므로, 우리는 이 버그를 한번 고쳐보자 !

이렇게 if문을 추가해서 범위를 벗어난다면 no more을 출력하게 만들었다.

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var falseButton: UIButton!
    @IBOutlet weak var trueButton: UIButton!
    
    @IBOutlet weak var progressBar: UIProgressView!
    @IBOutlet weak var questionLabel: UILabel!
    
    let quiz = [
        ["Four + Two is euqal is Six","True"],
        ["Six + Two is euqal is Eight","True"],
        ["Five + Two is euqal is nine","False"]
    ]
    
    var questionNumber = 0
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        updateUI()
        
    }
    @IBAction func answerButtonPressed(_ sender: UIButton) {
        
        let userAnswer = sender.currentTitle // True, False
        let actualAnswer = quiz[questionNumber][1] // 실제 답
        
        if userAnswer == actualAnswer{
            print("right!")
        }else {
            print("Worng!")
        }
        
        if questionNumber<2{
            questionNumber += 1
        }else{
            print("N O M O R E !")
        }
        
        updateUI()
    }
    
    func updateUI(){
        questionLabel.text = quiz[questionNumber][0]
        
    }

}
if questionNumber+1<quiz.count{
            questionNumber += 1
        }else{
            print("N O M O R E !")
        }

대신 quiz.count를 사용하면 리스트안의 요소 개수를 가져올 수 있다.
2차원 리스트는 읽기 힘드니깐 좀 더 가독성 있게 바꿔보자.
그를 위해서 Structures를 배워보자 !

struct 는 methods or properties랑은 좀 다르게 선언한다.
첫 문자를 대문자로 써야한다.

struct Mystruct{
}

이 structures는 안에 여러가지 자료형을 한곳에 넣을 수 있다.

struct Town{
    let name = "angelaLand"
    var citizens = ["Angela","Jack Bauer"] 
    var resource = ["Grain":100,"Ore":42,"wool":75]
}

var myTown = Town()

print(myTown.citizens)
print("\(myTown.name) has \(myTown.resource["Grain"]!) begs of grain.")

이와 같이 말이다.

이렇게 구조체 안에 들어가있는 함수는 메서드라고 부른다.
그 외에 선언 한 것들은 프로퍼티라고 보면된다

이 구조체는 블루프린트 즉, 청사진과 같다.
우리는 여기에 계획을 세운다.

Properties = What it's like
Methods = What it can do

라고 생각하면 된다.

자동차를 예시로 들면 차가 움직이는건 메서드, 색은 프로퍼티라고 보면된다.
init을 해줘야하는이유,

struct Town{
    let name : String
    var citizens : [String]
    var resource : [String : Int]
    
    init(townName: String, people: [String], stats :[String : Int]){
        name = townName
        citizens = people
        resource = stats
    }
    
    func fortify(){
        print("Defences increased!")
    }
}

var anotherTown = Town(townName: "Nameless", people: ["chans"], stats: ["coco":100])
anotherTown.citizens.append("willsion")

print(anotherTown.citizens)

self 키워드

struct Town{
    let name : String
    var citizens : [String]
    var resource : [String : Int]
    
    init(name: String, citizens: [String], resource :[String : Int]){
        self.name = name
        self.citizens = citizens
        self.resource = resource
    }
    
    func fortify(){
        print("Defences increased!")
    }
}

var anotherTown = Town(name: "Nameless", citizens: ["chans"], resource: ["coco":100])
anotherTown.citizens.append("willsion")

print(anotherTown.citizens)

이런식으로 self키워드를 사용할 수 있다.

struct Town{
    let name : String
    var citizens : [String]
    var resource : [String : Int]
    
    init(name: String, citizens: [String], resource :[String : Int]){
        self.name = name
        self.citizens = citizens
        self.resource = resource
    }
    
    func fortify(){
        print("Defences increased!")
    }
}

var anotherTown = Town(name: "Nameless", citizens: ["chans"], resource: ["coco":100])
anotherTown.citizens.append("willsion")

print(anotherTown.citizens)

var ghostTown = Town(name: "Ghosty ", citizens: [], resource: ["banana":100])

ghostTown.fortify()

이렇게 우리는 Twon이라는 BluePrint를 활용할 수 있다.

func exercise() {

    // Define the User struct here
    struct User{
        let name : String
        var email : String
        var followers : Int
        var isActive : Bool


    // Initialise a User struct here
        init(name: String, email: String, followers :Int, isActive: Bool){
            self.name = name
            self.email = email
            self.followers = followers
            self.isActive = isActive
        }
        
        func logStatus(){
            if(isActive){
                Swift.print("\(self.name) is working hard")
            }else{
                Swift.print("\(self.name) has left earth")
            }
        }


    // Diagnostic code - do not change this code
    print("\nDiagnostic code (i.e., Challenge Hint):")
    var musk = User(name: "Elon", email: "elon@tesla.com", followers: 2001, isActive: true)
    musk.logStatus()
    print("Contacting \(musk.name) on \(musk.email!) ...")
    print("\(musk.name) has \(musk.followers) followers")
    // sometime later
    musk.isActive = false
    musk.logStatus()
    
}

.. 챌린지 뭦ㅇ..

이제 구조체로 바꿔주자
Question.swift를 만든다


import Foundation

struct Question {
    let text : String
    let answer : String
    
}

구조체를 만들어서

    let quiz = [
        Question(text: "Four + Two is euqal is Six", answer: "True"),
        Question(text: "Six + Two is euqal is Eight", answer: "True"),
        Question(text: "Five + Two is euqal is nine", answer: "False")
    ]

퀴즈를 이런방식으로 변경한다.

let userAnswer = sender.currentTitle // True, False
        let actualQuestion = quiz[questionNumber]
        let actualAnswer = actualQuestion.answer

그 후 밑에 코드들도 이런식으로 작성하여 좀 더 알아보기 쉽게 만들 수 있다.

//
//  ViewController.swift
//  Quizzler-iOS13
//
//  Created by Angela Yu on 12/07/2019.
//  Copyright © 2019 The App Brewery. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var falseButton: UIButton!
    @IBOutlet weak var trueButton: UIButton!
    
    @IBOutlet weak var progressBar: UIProgressView!
    @IBOutlet weak var questionLabel: UILabel!
    
    let quiz = [
        Question(text: "Four + Two is euqal is Six", answer: "True"),
        Question(text: "Six + Two is euqal is Eight", answer: "True"),
        Question(text: "Five + Two is euqal is nine", answer: "False")
    ]
    
    var questionNumber = 0
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        updateUI()
        
    }
    @IBAction func answerButtonPressed(_ sender: UIButton) {
        
        let userAnswer = sender.currentTitle // True, False
        let actualQuestion = quiz[questionNumber]
        let actualAnswer = actualQuestion.answer
        
        if userAnswer == actualAnswer{
            print("right!")
        }else {
            print("Worng!")
        }
        
        // quiz.count = 요소 수
        if questionNumber+1<quiz.count{
            questionNumber += 1
        }else{
            questionNumber=0
        }
        
        updateUI()
    }
    
    func updateUI(){
        questionLabel.text = quiz[questionNumber].text
    }

}

이렇게 구조체로 작성해주면 좋다!
클릭했을때 색이 0.2초정도 바꼈다가 없어지게 하는코드는 아래와같다.

    @IBAction func answerButtonPressed(_ sender: UIButton) {
        
        let userAnswer = sender.currentTitle // True, False
        let actualQuestion = quiz[questionNumber]
        let actualAnswer = actualQuestion.answer
        
        if userAnswer == actualAnswer{
            sender.backgroundColor = UIColor.green
        }else {
            sender.backgroundColor = UIColor.red
        }
        
        // quiz.count = 요소 수
        if questionNumber+1<quiz.count{
            questionNumber += 1
        }else{
            questionNumber=0
        }
        
        //timer로 updateUI를 진행한다.
        Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: #selector(updateUI), userInfo: nil, repeats: false)
        
        
    }
    
    @objc func updateUI(){
        questionLabel.text = quiz[questionNumber].text
        trueButton.backgroundColor = UIColor.clear
        falseButton.backgroundColor = UIColor.clear
    }

에그에서 배운 timer를 가지고와서 우리입맛대로 고치면된다.
그런데 method를 selector하기 위해서 @objc 이 붙어줘야한다.

progressBar.progress = Float(questionNumber+1) / Float(quiz.count)

progressBar는 updateUI에 이렇게 추가하면 된다.

MVC Design Pattern

Model
View
Controller

View : Sends input eventes -> Controller : Makes request -> Model : sends data to controller -> Controller : Modifies View

Functions with Outputs

반환값인가!

func getMilk(money:Int)->Int{
	let change = money -2
    return change 
}

이것을 적용해서 코드를 확 바꿔주자
그리고 MVC를 적용해주자.
![](https://velog.velcdn.com/images/seochan99/post/2da06fdf-06e8-481d-b47e-70d85939ad56/image.png

QuizBrain.swift
import Foundation

struct QuizBrain {
    let quiz = [
        Question(q: "A slug's blood is green.", a: "True"),
                Question(q: "Approximately one quarter of human bones are in the feet.", a: "True"),
                Question(q: "The total surface area of two human lungs is approximately 70 square metres.", a: "True"),
                Question(q: "In West Virginia, USA, if you accidentally hit an animal with your car, you are free to take it home to eat.", a: "True"),
                Question(q: "In London, UK, if you happen to die in the House of Parliament, you are technically entitled to a state funeral, because the building is considered too sacred a place.", a: "False"),
                Question(q: "It is illegal to pee in the Ocean in Portugal.", a: "True"),
                Question(q: "You can lead a cow down stairs but not up stairs.", a: "False"),
                Question(q: "Google was originally called 'Backrub'.", a: "True"),
                Question(q: "Buzz Aldrin's mother's maiden name was 'Moon'.", a: "True"),
                Question(q: "The loudest sound produced by any animal is 188 decibels. That animal is the African Elephant.", a: "False"),
                Question(q: "No piece of square dry paper can be folded in half more than 7 times.", a: "False"),
                Question(q: "Chocolate affects a dog's heart and nervous system; a few ounces are enough to kill a small dog.", a: "True")
    ]
    
    var questionNumber = 0
    
    func checkAnswer(_ userAnswer : String) -> Bool {
        if userAnswer == quiz[questionNumber].answer{
            // User got it right
            return true
            
        }else{
            //User got it Wrong
            return false
        }
    }
    
    func getQuestionText() -> String {
        return quiz[questionNumber].text
    }
    
    func getProgress() -> Float {
        let progress = Float(questionNumber+1) / Float(quiz.count)
        return progress
    }
    
    mutating func nextQuestion(){
        if questionNumber+1<quiz.count{
            questionNumber += 1
        }else{
            questionNumber=0
        }
    }
}
ViewController.swift

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var falseButton: UIButton!
    @IBOutlet weak var trueButton: UIButton!
    
    @IBOutlet weak var progressBar: UIProgressView!
    @IBOutlet weak var questionLabel: UILabel!
    
    
    var quizBrain = QuizBrain()
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        updateUI()
        
    }
    @IBAction func answerButtonPressed(_ sender: UIButton) {
        
        let userAnswer = sender.currentTitle! // True, False
        
        
        let userGotItRight = quizBrain.checkAnswer(userAnswer)
        
        if userGotItRight{
            sender.backgroundColor = UIColor.green
        }else {
            sender.backgroundColor = UIColor.red
        }
        
        // quiz.count = 요소 수
        quizBrain.nextQuestion()

        
        //timer로 updateUI를 진행한다.
        Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: #selector(updateUI), userInfo: nil, repeats: false)
        
        
    }
    
    @objc func updateUI(){
        questionLabel.text = quizBrain.getQuestionText()
        progressBar.progress = quizBrain.getProgress()
        trueButton.backgroundColor = UIColor.clear
        falseButton.backgroundColor = UIColor.clear
        
        // 
    }

}

Immutablity

Mutating
https://hyerios.tistory.com/190
d

최종코드

viewController


import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var scoreLabel: UILabel!
    @IBOutlet weak var falseButton: UIButton!
    @IBOutlet weak var trueButton: UIButton!
    
    @IBOutlet weak var progressBar: UIProgressView!
    @IBOutlet weak var questionLabel: UILabel!
    
    
    var quizBrain = QuizBrain()
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        updateUI()
        
    }
    @IBAction func answerButtonPressed(_ sender: UIButton) {
        
        let userAnswer = sender.currentTitle! // True, False
        
        
        let userGotItRight = quizBrain.checkAnswer(userAnswer)
        
        if userGotItRight{
            sender.backgroundColor = UIColor.green
        }else {
            sender.backgroundColor = UIColor.red
        }
        
        // quiz.count = 요소 수
        quizBrain.nextQuestion()

        
        //timer로 updateUI를 진행한다.
        Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: #selector(updateUI), userInfo: nil, repeats: false)
        
        
    }
    
    @objc func updateUI(){
        questionLabel.text = quizBrain.getQuestionText()
        progressBar.progress = quizBrain.getProgress()
        
        scoreLabel.text = "Score : \(quizBrain.getScore())"
        
        trueButton.backgroundColor = UIColor.clear
        falseButton.backgroundColor = UIColor.clear
        
        // 
    }

}

QuizBrain

import Foundation

struct QuizBrain {
    let quiz = [
        Question(q: "A slug's blood is green.", a: "True"),
                Question(q: "Approximately one quarter of human bones are in the feet.", a: "True"),
                Question(q: "The total surface area of two human lungs is approximately 70 square metres.", a: "True"),
                Question(q: "In West Virginia, USA, if you accidentally hit an animal with your car, you are free to take it home to eat.", a: "True"),
                Question(q: "In London, UK, if you happen to die in the House of Parliament, you are technically entitled to a state funeral, because the building is considered too sacred a place.", a: "False"),
                Question(q: "It is illegal to pee in the Ocean in Portugal.", a: "True"),
                Question(q: "You can lead a cow down stairs but not up stairs.", a: "False"),
                Question(q: "Google was originally called 'Backrub'.", a: "True"),
                Question(q: "Buzz Aldrin's mother's maiden name was 'Moon'.", a: "True"),
                Question(q: "The loudest sound produced by any animal is 188 decibels. That animal is the African Elephant.", a: "False"),
                Question(q: "No piece of square dry paper can be folded in half more than 7 times.", a: "False"),
                Question(q: "Chocolate affects a dog's heart and nervous system; a few ounces are enough to kill a small dog.", a: "True")
    ]
    
    var questionNumber = 0
    var score = 0
    
    mutating func checkAnswer(_ userAnswer : String) -> Bool {
        if userAnswer == quiz[questionNumber].answer{
            // User got it right
            score += 1
            return true
        }else{
            //User got it Wrong
            return false
        }
    }
    
    func getQuestionText() -> String {
        return quiz[questionNumber].text
    }
    
    func getProgress() -> Float {
        let progress = Float(questionNumber+1) / Float(quiz.count)
        return progress
    }
    
    mutating func nextQuestion(){
        if questionNumber+1<quiz.count{
            questionNumber += 1
        }else{
            questionNumber=0
            score = 0
        }
    }
    
    func getScore() -> Int{
        return score
    }
}

20min..

profile
부족한 실력을 엉덩이 힘으로 채워나가는 개발자 서희찬입니다 :)

0개의 댓글