궁금한 내용을 검색해보세요!
이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
서근 개발노트
티스토리에 팔로잉
SWIFT/Udemy iOS

[Udemy] 섹션9: MVC 패턴, Struct, mutating ( 퀴즈 앱 )

서근
QUOTE THE DAY

-
Written by SeogunSEOGUN

반응형

 

목표 : MVC 패턴에 대해 알아본다.

Quizzler Project

main storyboard elementsviewController로 연결해준다.

class ViewController: UIViewController {
    
    @IBOutlet weak var questionLabel: UILabel!
    @IBOutlet weak var progressBar: UIProgressView!
    @IBOutlet weak var trueBtn: UIButton!
    @IBOutlet weak var falseBtn: UIButton!

override func viewDidLoad()

viewDidLoad는 앱을 실행할 때 딱 한 번만 실행되는 view이다. 만약 앱을 실행하자마자 퀴즈의 텍스를 보여주려면 다음과 같이 퀴즈 배열을 작성하고 이 코드 안에 다음과 같이 작성하면 된다.

    let quiz = [
        "달걀은 어린 닭이 낳은 것일수록 그 크기가 크다",
        "달걀은 뾰족한 쪽에 호흡하는 공간이 있어서 둥근 쪽을 아래로 해서 보관해야 합니다",
        "꺼벙이란 꿩의 새끼를 말합니다",
        "외출 전에 자외선 차단제를 바르면 햇볕을 오래 받아도 무방하다"
    ]
    
    //viewDidLoad는 앱이 실행될때 단 한번만 실행됨.
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //questionLabel.text = quiz.randomElement()
        questionLabel.text = quiz[0]
    }

    @IBAction func PressedAnswerBtn(_ sender: UIButton) {
    
        // some code
        
    }
    
}

UpdateUI

버튼을 눌렀을 때 질문이 바뀌게 하는 방법

1. numberOfQuestion 변수 생성

2. questionLabel text의 배열을 numberOfQuestion으로 수정

3. PressedAnswerBtnnumberOfQuestion + 1 추가

 

문제점 viewDidLoad는 앱이 실행될 때 단 한 번만 업데이트하기 때문에 questionLabelButtonpress 되었을 때도 업데이트돼야 한다.

 

해결방법 UpdateUI 함수를 만들어 호출

class ViewController: UIViewController {
    
     ...
    
    let quiz = [
         ..
    ]
    
    var numberOfQuestion = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        updateUI()
    }
    
    @IBAction func PressedAnswerBtn(_ sender: UIButton) {
        numberOfQuestion += 1
        updateUI()
    }
    
    func updateUI() {
        questionLabel.text = quiz[numberOfQuestion]
    }
}

1D / 2D Array

1D Array 검색

2D Array 검색

연습

만약 2D Array에서 다음과 같은 배열이 있을 때 숫자 9를 프린트하려면 다음과 같다.

let twoDArray = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

print(twoDArray[2][2])  //9

Quiz 배열 수정

    let quiz = [
        ["달걀은 어린 닭이 낳은 것일수록 그 크기가 크다", "False"],
        ["달걀은 뾰족한 쪽에 호흡하는 공간이 있어서 둥근 쪽을 아래로 해서 보관해야 합니다", "False"],
        ["꺼벙이란 꿩의 새끼를 말합니다", "Ture"],
        ["외출 전에 자외선 차단제를 바르면 햇볕을 오래 받아도 무방하다", "Fasle"]
    ]
    
     ...
     
    func updateUI() {
        questionLabel.text = quiz[numberOfQuestion][0] // 2D Array
    }
}

Quiz 정답 버튼 구현

정확한 정답을 눌렀을 때를 구별하기 위한 코드를 구현해야 한다.

 

1. userAnswer 상수 생성 > sender.currentTitle

2. currentAnswer 상수 생성 > quiz의 numberOfQuestion 배열의 [1](true or false) 할당

3. if문을 사용하여 정답 혹은 오답 프린트

4. numberOfQuestionquiz.count 보다 작으면 +1을 더해줌

문제점 여전히 quiz의 질문이 끝나면 앱이 크래쉬 됨.

해결방법 만약 numberOfQuestion가 4이면 quiz.count가 4이기 때문에 다음 버튼을 눌렀을 때 앱이 크래쉬 난다. 그렇기 때문에 처음부터 numberOfQuestion+1을 하고 조건문을 작성하면 해결된다.

5. else문에 numberOfQuestion을 0으로 정해주어 퀴즈가 끝나도 계속 반복될 수 있도록 해준다.

    @IBAction func PressedAnswerBtn(_ sender: UIButton) {
        
        let userAnswer = sender.currentTitle
        //numberOfQuestion의 [1]이 정답(true or false)이 위치해있기 때문
        let currentAnswer = quiz[numberOfQuestion][1]
        
        // 정답 확인
        if userAnswer == currentAnswer {
            print("정답입니다!")
        } else {
            print("오답입니다! 정답은 \(quiz[numberOfQuestion][1])")
        }
        
        // 정답 개수 확인       
        if numberOfQuestion + 1 < quiz.count {
            numberOfQuestion += 1
        } else {
            numberOfQuestion = 0
        }
        updateUI()
    }

Quiz list - Struct

지저분한 quiz list를 새로운 sturct 파일로 만들어서 코드를 구현할 수 있다. struct에 대한 문법

// Questions.swift

import Foundation
import UIKit

struct Questions {
    var questionText: String
    var answer: String
    
    init(questionText: String, answer: String) {
        self.questionText = questionText
        self.answer = answer
    }
}

1. viewController에서 Question struct 호출

2. currentAnswer 정답 코드 수정

3. UpdateUI 함수 수정

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var questionLabel: UILabel!
    @IBOutlet weak var progressBar: UIProgressView!
    @IBOutlet weak var trueBtn: UIButton!
    @IBOutlet weak var falseBtn: UIButton!
    
    let quiz = [
        Questions(questionText: "달걀은 어린 닭이 낳은 것일수록 그 크기가 크다", answer: "False"),
        Questions(questionText: "달걀은 뾰족한 쪽에 호흡하는 공간이 있어서 둥근 쪽을 아래로 해서 보관해야 합니다", answer: "False"),
        Questions(questionText: "꺼벙이란 꿩의 새끼를 말합니다", answer: "Ture"),
        Questions(questionText: "외출 전에 자외선 차단제를 바르면 햇볕을 오래 받아도 무방하다", answer: "False"),
    ]
    
    
    var numberOfQuestion = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        updateUI()
    }
    
    @IBAction func PressedAnswerBtn(_ sender: UIButton) {
        
        let userAnswer = sender.currentTitle
        //numberOfQuestion의 정답이 있는 .answer로 수정
        let currentAnswer = quiz[numberOfQuestion].answer
        
        // if문
        if userAnswer == currentAnswer {
            print("정답입니다!")
        } else {
            print("오답입니다! 정답은 \(quiz[numberOfQuestion].answer)")
        }
        
        if numberOfQuestion + 1 < quiz.count {
            numberOfQuestion += 1
        } else {
            numberOfQuestion = 0
        }
        
        updateUI()
    }
    
    func updateUI() {
        questionLabel.text = quiz[numberOfQuestion].questionText
    }
}
  
    let quiz = [
        Questions(questionText: "달걀은 어린 닭이 낳은 것일수록 그 크기가 크다.", answer: "False"),
        Questions(questionText: "달걀은 뾰족한 쪽에 호흡하는 공간이 있어서 둥근 쪽을 아래로 해서 보관해야 하는가?", answer: "False"),
        Questions(questionText: "원숭이에게도 지문이 있다?", answer: "Ture"),
        Questions(questionText: "외출 전에 자외선 차단제를 바르면 햇볕을 오래 받아도 무방한가?", answer: "False"),
        Questions(questionText: "1970년대도 포도, 사과 다이어트 같은 원푸드 다이어트는 폭발적인 인기를 끌었는가?", answer: "True"),
        Questions(questionText: "승리의 V는 남북전쟁에서 승리한 후 에이브러험 링컨이 처음 사용하였는가?", answer: "False"),
        Questions(questionText: "뽀뽀뽀 노래 중에 뽀 가 총 24번 나온다?", answer: "True"),
        Questions(questionText: "국가대표 축구선수 안정환의 포지션은 센터백인가?", answer: "False"),
        Questions(questionText: "딸기는 장미과에 속하는가?", answer: "True"),
        Questions(questionText: "우리나라에 가장 넓은 차선은 광화문 앞에 16차선 인가?", answer: "True"),
        Questions(questionText: "물고기도 기침을 하는가?", answer: "True"),
        Questions(questionText: "오이는 과일이 아니라 채소인가", answer: "True")
    ]

ProgressView / UIColor

정답 또는 오답을 선택했을 때 Print를 하는 것이 아닌 버튼 배경색을 변경하고 지연 효과를 주어 오답인지 정답인지를 직관적으로 볼 수 있게 할 수 있다. 또 ProgressView를 사용해보려고 한다.

 

1. if문에 sender.background = UIColor를 사용한다.

2. view가 Update 할 때마다 배경색을 되돌리기 위해 UIColor.clear를 사용한다.

3. @IBAction 코드 내에 UpdateUI에 지연 효과를 주어 잠깐 동안 정답 배경색을 나타나게 한다.

4. progressBar를 보여주기 위해 UpdateUI 함수에 코드를 넣어준다.

5. ButtoncornerRadiusviewDidLoad안에서 수정해준다.

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var questionLabel: UILabel!
    @IBOutlet weak var progressBar: UIProgressView!
    @IBOutlet weak var trueBtn: UIButton!
    @IBOutlet weak var falseBtn: UIButton!
    
    
    let quiz = [
        Questions(questionText: "달걀은 어린 닭이 낳은 것일수록 그 크기가 크다.", answer: "False"),
        Questions(questionText: "달걀은 뾰족한 쪽에 호흡하는 공간이 있어서 둥근 쪽을 아래로 해서 보관해야 하는가?", answer: "False"),
        Questions(questionText: "원숭이에게도 지문이 있다?", answer: "Ture"),
        Questions(questionText: "외출 전에 자외선 차단제를 바르면 햇볕을 오래 받아도 무방한가?", answer: "False"),
        Questions(questionText: "1970년대도 포도, 사과 다이어트 같은 원푸드 다이어트는 폭발적인 인기를 끌었는가?", answer: "True"),
        Questions(questionText: "승리의 V는 남북전쟁에서 승리한 후 에이브러험 링컨이 처음 사용하였는가?", answer: "False"),
        Questions(questionText: "뽀뽀뽀 노래 중에 뽀 가 총 24번 나온다?", answer: "True"),
        Questions(questionText: "국가대표 축구선수 안정환의 포지션은 센터백인가?", answer: "False"),
        Questions(questionText: "딸기는 장미과에 속하는가?", answer: "True"),
        Questions(questionText: "우리나라에 가장 넓은 차선은 광화문 앞에 16차선 인가?", answer: "True"),
        Questions(questionText: "물고기도 기침을 하는가?", answer: "True"),
        Questions(questionText: "오이는 과일이 아니라 채소인가", answer: "True")
    ]
    
    var numberOfQuestion = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        updateUI()
        trueBtn.layer.cornerRadius = 20
        falseBtn.layer.cornerRadius = 20
    }
    
    @IBAction func PressedAnswerBtn(_ sender: UIButton) {
        
        let userAnswer = sender.currentTitle
        //numberOfQuestion의 정답이 있는 .answer로 수정
        let currentAnswer = quiz[numberOfQuestion].answer
        
        // if문
        if userAnswer == currentAnswer {
            sender.backgroundColor = UIColor.green
        } else {
            sender.backgroundColor = UIColor.red
        }
        
        if numberOfQuestion + 1 < quiz.count {
            numberOfQuestion += 1
        } else {
            numberOfQuestion = 0
        }
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
            UIView.animate(withDuration: 0.2) {
                self.updateUI()
            }
        }
    }
    
    func updateUI() {
        questionLabel.text = quiz[numberOfQuestion].questionText
        trueBtn.backgroundColor = UIColor.clear
        falseBtn.backgroundColor = UIColor.clear
        progressBar.progress = Float(numberOfQuestion + 1) / Float(quiz.count)
    }
}

MVC 패턴으로 코드 정리

MVC패턴을 사용해서 코드를 재사용하기 쉽고 관리하기 쉽게 수정할 수 있다. MVC 패턴에 대한 문법

New Group from Selection

폴더명을 Model, View, Controller로 바꾼다. Input / output 문법 

Model (QuizBrain.swift 생성)

QuizBrain Model을 구조체로 만들고 viewController에 있던 코드를 가져오고 몇 가지 메서드를 만들어줘야 한다. 또, UpdateUI 부분에 오류 부분도 수정해줘야 한다. 에러난 두 부분을 QuizBrain에 메서드로 만들어서 해결해줄 수 있다.

    func updateUI() {
        questionLabel.text = quiz[numberOfQuestion].questionText //error
        progressBar.progress = Float(numberOfQuestion + 1) / Float(quiz.count) //error
        trueBtn.backgroundColor = UIColor.clear
        falseBtn.backgroundColor = UIColor.clear
    }
// QuizBrain.swift

import Foundation
import UIKit

struct QuizBrain {
    let quiz = [
        Questions(questionText: "달걀은 어린 닭이 낳은 것일수록 그 크기가 크다.", answer: "False"),
        Questions(questionText: "달걀은 뾰족한 쪽에 호흡하는 공간이 있어서 둥근 쪽을 아래로 해서 보관해야 하는가?", answer: "False"),
        Questions(questionText: "원숭이에게도 지문이 있다?", answer: "Ture"),
        Questions(questionText: "외출 전에 자외선 차단제를 바르면 햇볕을 오래 받아도 무방한가?", answer: "False"),
        Questions(questionText: "1970년대도 포도, 사과 다이어트 같은 원푸드 다이어트는 폭발적인 인기를 끌었는가?", answer: "True"),
        Questions(questionText: "승리의 V는 남북전쟁에서 승리한 후 에이브러험 링컨이 처음 사용하였는가?", answer: "False"),
        Questions(questionText: "뽀뽀뽀 노래 중에 뽀 가 총 24번 나온다?", answer: "True"),
        Questions(questionText: "국가대표 축구선수 안정환의 포지션은 센터백인가?", answer: "False"),
        Questions(questionText: "딸기는 장미과에 속하는가?", answer: "True"),
        Questions(questionText: "우리나라에 가장 넓은 차선은 광화문 앞에 16차선 인가?", answer: "True"),
        Questions(questionText: "물고기도 기침을 하는가?", answer: "True"),
        Questions(questionText: "오이는 과일이 아니라 채소인가", answer: "True")
    ]
    
    var numberOfQuestion = 0
    
    func checkAnswer(_ userAnser: String) -> Bool {
        if userAnser == quiz[numberOfQuestion].answer {
            return true
        } else {
            return false
        }
    }
    
    func UpdateQuestionText() -> String {
        return quiz[numberOfQuestion].questionText
    }
    
    func updateProgressBar() -> Float {
        let progress = Float(numberOfQuestion + 1) / Float(quiz.count)
        return progress
    }
}
//Questions.swift

import Foundation
import UIKit

struct Questions {
    var questionText: String
    var answers: [String]
    var correctAnswer: String
    
    init(questionText: String, answers: [String], correctAnswer: String) {
        self.questionText = questionText
        self.answers = answers
        self.correctAnswer = correctAnswer
    }
}

viewController 수정

viewController의 코드를 위와 같이 수정하고 보면 Question이 넘어가는 부분(if numberOfQuestion + 1 < quiz.count)이 오류가 생긴 것을 확인할 수 있다.

// viewController.swift

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var questionLabel: UILabel!
    @IBOutlet weak var progressBar: UIProgressView!
    @IBOutlet weak var trueBtn: UIButton!
    @IBOutlet weak var falseBtn: UIButton!
    
    // quizBrain Model을 init해서 가져옴
    var quizBrain = QuizBrain()

    override func viewDidLoad() {
        super.viewDidLoad()
        updateUI()
        trueBtn.layer.cornerRadius = 20
        falseBtn.layer.cornerRadius = 20
    }
    
    @IBAction func PressedAnswerBtn(_ sender: UIButton) {
        
        let userAnswer = sender.currentTitle!
        let trueOrFalse = quizBrain.checkAnswer(userAnswer)

        if trueOrFalse {
            sender.backgroundColor = UIColor.green
        } else {
            sender.backgroundColor = UIColor.red
        }

     // Error   
        if numberOfQuestion + 1 < quiz.count {
            numberOfQuestion += 1
        } else {
            numberOfQuestion = 0
        }
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
            UIView.animate(withDuration: 0.2) {
                self.updateUI()
            }
        }
        
    }
    
    func updateUI() {
        questionLabel.text = quizBrain.UpdateQuestionText()
        progressBar.progress = quizBrain.updateProgressBar()
        trueBtn.backgroundColor = UIColor.clear
        falseBtn.backgroundColor = UIColor.clear
       
    }
}

mutating

viewcontroller에서 오류가 나있는 if문은 퀴즈의 개수가 numberOfQuestion+1 보다 클 때 +1을 하게하는 조건문이었다. 그것도 Model로 함수를 새로 만들어 옮겨 주겠음! mutating 문법

문제점 그럼 이러한 오류가 발생하는데, selfimmutable(변경 불가성) 이기 때문에 사용이 불가능하다고 한다.

 

해결방법 func 키워드 앞에 mutating 키워드 사용

updateProgressBar 메서드와 nextQuestion 메서드의 차이점을 보면 

updateProgressBar - 아무것도 변경하지 않고 progress의 값만 변경

nextQuestion - QuizBrain의 속성을 변경

 

그렇기 때문에 nextQuestion 메서드를 Mutating 키워드를 사용하여 속성이 변경 가능하게 만들어 준 것이다.

완성 파일

Quizzler.zip
0.98MB

객관식 퀴즈 형태로 변경

위에서는 O, X 퀴즈로 두 가지 중 하나만 고를 수 있었는데 객관식으로 변경하려면 코드를 조금 변경해야 한다.

1. 객관식 레이블 추가

2. @IBOutlet 연결

3. QuizBrain 객관식 문제 수정 및 getAnswerList 메서드 추가

4. viewController updateUI 함수에 getAnswerListlet 으로 가져옴

5. UIButton의 텍스트 업데이트는 .text 가 아닌 .setTitle(String, for: )을 사용함

완성 코드

Controller

<hide/>

// controllView.swift

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var questionLabel: UILabel!
    @IBOutlet weak var progressBar: UIProgressView!
    @IBOutlet weak var scoreLabel: UILabel!
    @IBOutlet weak var answer1: UIButton!
    @IBOutlet weak var answer2: UIButton!
    @IBOutlet weak var answer3: UIButton!
    
     
    // quizBrain Model을 init해서 가져옴
    var quizBrain = QuizBrain()

    override func viewDidLoad() {
        super.viewDidLoad()
        updateUI()
        answer1.layer.cornerRadius = 20
        answer2.layer.cornerRadius = 20
        answer3.layer.cornerRadius = 20
    }
    
    @IBAction func PressedAnswerBtn(_ sender: UIButton) {
        
        let userAnswer = sender.currentTitle!
        let trueOrFalse = quizBrain.checkAnswer(userAnswer)
//        let currentAnser = quiz[numberOfQuestion].answer
        
        if trueOrFalse {
            sender.backgroundColor = UIColor.green
        } else {
            sender.backgroundColor = UIColor.red
        }
            
        quizBrain.nextQuestion()
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
            UIView.animate(withDuration: 0.2) {
                self.updateUI()
            }
        }
        
    }
    
    func updateUI() {
        
        let choiceAnswer = quizBrain.getAnswerList()
        answer1.setTitle(choiceAnswer[0], for: .normal)
        answer2.setTitle(choiceAnswer[1], for: .normal)
        answer3.setTitle(choiceAnswer[2], for: .normal)
        
        answer1.backgroundColor = UIColor.clear
        answer2.backgroundColor = UIColor.clear
        answer3.backgroundColor = UIColor.clear
        
        questionLabel.text = quizBrain.UpdateQuestionText()
        progressBar.progress = quizBrain.updateProgressBar()
        scoreLabel.text = "현재 점수 : \(quizBrain.getScore())"
    }
}

Model

<hide/>

//Questions.swift

import Foundation
import UIKit

struct Questions {
    var questionText: String
    var answers: [String]
    var correctAnswer: String
    
    init(questionText: String, answers: [String], correctAnswer: String) {
        self.questionText = questionText
        self.answers = answers
        self.correctAnswer = correctAnswer
    }
}
<hide/>

//QuizBrain.swift

import Foundation
import UIKit

struct QuizBrain {
    let quiz = [
        Questions(questionText: "다음중에서 내장고에 얼음을 위쪽에 두는 이유는?", answers: ["전도", "대류", "복사"], correctAnswer: "대류"),
        Questions(questionText: "다음중 현재 우리나라에서 지정된 생태계 보전지역은?", answers: ["지리산, 대암산, 오봉산", "지리산, 낙동강, 대암산", "속리산, 낙동강, 지리산"], correctAnswer: "지리산, 낙동강, 대암산"),
        Questions(questionText: "다음중 세계 3대강풍이 아닌것은?", answers: ["블리자드", "허리케인", "태풍"], correctAnswer: "블리자드"),
        Questions(questionText: "다음중 연탄가스에 중독을 일으키는 성분은?", answers: ["메탄가스", "이산화탄소", "탄산가스"], correctAnswer: "이산화탄소"),
        Questions(questionText: "다음중 노이즈에 대한 설명중 맞는것은", answers: ["환경오염", "도시에서의 각종공해", "각종 공해 소리가 점차 해소되어가는 상태"], correctAnswer: "도시에서의 각종공해"),
        Questions(questionText: "다음중 우리나라 환율제도는 어느것인가?", answers: ["바스켓제도", "관리 통화제도", "변동환율제도"], correctAnswer: "변동환율제도"),
        Questions(questionText: "다음중 경기 변동에 가장 민감한 것은 어느것인가?", answers: ["물가지수", "환율", "주가지수"], correctAnswer: "주가지수"),
        Questions(questionText: "다음중 우리나라 화폐 제도는 어느것인가?", answers: ["관리통화제도", "고정환율제도", "금 본위제도"], correctAnswer: "관리통화제도"),
        Questions(questionText: "대한제국 이란 국호를 처음으로 사용하게 된 시기는 언제인가?", answers: ["아관파천 이후", "갑오경장 이후", "을미개혁 이후"], correctAnswer: "아관파천 이후")
    ]
    
    var numberOfQuestion = 0
    var score = 0
    
    mutating func checkAnswer(_ userAnswer: String) -> Bool {
        if userAnswer == quiz[numberOfQuestion].correctAnswer {
            score += 1
            return true
        } else {
            return false
        }
    }
    
    func getScore() -> Int {
        return score
    }

    func getAnswerList() -> [String] {
        return quiz[numberOfQuestion].answers
    }
    
    func UpdateQuestionText() -> String {
        return quiz[numberOfQuestion].questionText
    }
    
    func updateProgressBar() -> Float {
        let progress = Float(numberOfQuestion + 1) / Float(quiz.count)
        return progress
    }
    
    
    mutating func nextQuestion() {
        if numberOfQuestion + 1 < quiz.count {
            numberOfQuestion += 1
        } else {
            numberOfQuestion = 0
            score = 0
        }
    }
}

완성 파일

Quizzler-MultipleChoice.zip
0.99MB

----------  문법  ----------

Struct

struct town {
    let name: String = "서근"
    let myFamily = ["서근", "미진", "희진", "초코"]
    let description = ["서근": 27, "미진": 19, "희진": 29, "초코": 6]
}

// struct를 사용할 수 있게 이니셜라이즈 해줌.
var myTown = town()

print(myTown.name)
print("제 이름은 \(myTown.name)이고, 나이는 \(myTown.description["서근"]!)세입니다.")

append

myTown.myFamily.append("루키")
print(myTown.myFamily) //["서근", "미진", "희진", "초코", "루키"]

method

struct 또는 class의 중괄호 안에 func 함수 키워드가 있다면 그것은 메서드이다.

struct town {
    let name: String = "서근"
    var myFamily = ["서근", "미진", "희진", "초코"]
    let description = ["서근": 27, "미진": 19, "희진": 29, "초코": 6]
    
    func someMethod() {
        print("struct안에 func키워가 있다면 그것은 메서드 이다.")
    }
}

var myTown = town()

//town의 메서드 호출
myTown.someMethod()  //struct안에 func키워가 있다면 그것은 메서드 이다.

이것을 자동차로 비유해보자면 

Property - 자동차의 색, 자동차의 브랜드 등을 나타냄

Method  -  자동차를 전진, 후진, 브레이크, 비상등 등을 실행하도록 구현함

init()

위처럼 struct를 생성했지만 이것들을 바로 호출할 수 없다. 이 구조체를 호출하기 위해서는 초기화 즉, 이니셜라이저 해야 한다.

목적 : 블루프린트 같은 struct를 복사해서 사용하기 위함

예를 들어 이러한 도표 위해 자동차를 그렸다고 생각하면 이제 이 자동차를 실제 객체로 바꿔야 한다.

struct town {
    let name: String
    var family: [String]
    let description: [String: Int]
    
    init(name: String, myFamily: [String], description: [String: Int]) {
        self.name = name
        self.family = myFamily
        self.description = description
    }

    func someMethod() {
        print("struct안에 func키워가 있다면 그것은 메서드 이다.")
    }
}

var myTown = town(name: "서근", myFamily: ["미진", "초코", "희진"], description: ["미진": 19, "희진": 29, "초코": 6])
myTown.family.append("루키")
print(myTown.family)  //["미진", "초코", "희진", "루키"]


var ghostTown = town(name: "처녀귀신", myFamily: [], description: ["달걀귀신": 100, "처녀귀신": 120])
ghostTown.someMethod() // struct안에 func키워가 있다면 그것은 메서드 이다.

Challenge - 1

더보기

[Coding Exercise] Structures

You are about to create the next big social networking app, exclusive to business leaders called KingPin.

Define a Structure

As part of this app, you need to define a struct called User to represent a user.

This struct needs to hold onto the user's name, email (optional), number of followers, and whether they are active or not. The User struct needs to have the properties:

  • name
  • email?
  • followers
  • isActive

The Struct also needs to have a method called logStatus(). If the user is active, the method needs to print "XXX is working hard". Otherwise, it needs to print "XXX has left earth" (where XXX is the name of the user).

Initialise the Structure

After you have defined the struct, create a user with the name "Richard" with 0 followers who not active. Then print the status of this user to the console with logStatus().

<hide/>

  // Define the User struct here
    struct User {
        var name: String
        var email: String?
        var followers: Int
        var isActive: Bool
        
        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 == true {
                print("\(name) is working hard")
            } else {
                print("\(name) has left earth")
            }
        }
    }
    
    // Initialise a User struct here
    let user = User(name: "Richard", email: nil, followers: 0, isActive: false)
    user.logStatus()

Design Pattern

Design pattern : 문제를 해결하기 위한 수단

MVC Design Pattern

Model과 View를 분리시킨 이유

- viewController와 View(Main.storyboard)는 수정하지 않고 Model만 변경시키면 앱의 디자인을 변경할 수 있다.

- JAVAMVC 디자인 패턴을 만들었을 땐 viewController에서 기능을 구현하고 Model에서는 쿼리문을 작성하고 뷰에서는 값을 뿌렸었는데 Swift에서는 조금 다르다.

- MVC 디자인 패턴은 재사용에 아주 좋다.

  • 또 다른 디자인 패턴
    • VIPER
    • MVVM ( 가장 많이 사용함 )

TIP
 
 

IBOutlet 조작법
viewController에 있는 @IBOutlet을 조작하려면 functionreturn값을 활용하면 된다.

 

Functions with Outputs and Return Types

함수의 매개변수와 매개 변숫값이 Input이고 ->으로 반환하는 타입을 output이라고 한다. 반환하는 값 outputreturn 하는 타입과 반드시 일치해야 한다.

func greeting1() {
    print("안녕하세요")
}
greeting1() //안녕하세요

/* =================== */

func greeting2(_ name: String) {
    print("\(name)씨 안녕하세요")
}
greeting2("서근") //서근씨 안녕하세요

/* =================== */

func greeting3(name: String) -> String {
    if name == "서근" || name == "미진" {
        return "안녕하세요 \(name)씨"
    } else {
        return "...누구시죠?"
    }
}

print(greeting3(name: "서근")) //안녕하세요 서근씨
print(greeting3(name: "미진")) //안녕하세요 미진씨

let greetingTo = greeting3(name: "초코")
print(greetingTo) //...누구시죠?

/* =================== */

func greeting4(_ name: String) -> Bool {
    if name == "서근" {
        return true
    } else {
        return false
    }
}

print(greeting4("서근")) //true
print(greeting4("미진")) //false

Challenge - 2

더보기

Using what you have learnt about functions which can have outputs, create a function called isOdd(n: Int).

For any given whole number passed to the function, e.g.

  1. isOdd(n: 5)

The function will test to see if the number is odd. If it is, then it should output true otherwise it should output false. These are Booleans and not Strings.

NOTE: You should not write any print statements. The tests will only pass if the result is an output.

func isOdd(n: Int) -> Bool {
   if n % 2 != 0 {
       return true
   } else {
       return false
   }
}

혹은 삼항연산자를 사용해서

func isOdd(n: Int) -> Bool {
   return n % 2 != 0 ? true : false
}

immutable 변경 불가성 (Mutating)

 - let keyword

 - old copydestroy 하고 new copy를 만들어야 한다. (예전의 것을 깨부수고, 새로운 것을 만든다)

- struct 변경

1. outsideinside에서 구조체를 변경하는 것에는 차이가 있다.

2. inside 안에서 변경할 때, 'self' is immutable 오류가 발생하는데 inside에서의 selflet 키워드이다.

3. inside내에서 구조체의 속성을 변경하려 했기 때문에 selfimmutable(변경 불가성 let)이라는 오류 메시지를 띄움.

4. mutating을 붙여주면 self.descriptionvar description처럼 작동해 상태를 변경할 수 있다

 

 

읽어주셔서 감사합니다 🤟

 

 


잘못된 내용이 있으면 언제든 피드백 부탁드립니다.


서근


위처럼 이미지 와 함께 댓글을 작성할 수 있습니다.