[udemy] Section 11

서희찬·2022년 6월 27일
0

swift

목록 보기
15/17
post-thumbnail

이번섹션에는 아래와 같은것을 배워볼 것이다.

  • Learn to use UISliders
  • Create Swift Classes
  • Understand the difference between Classes and Structs
  • Learn to create UI programmatically without Storyboards.
  • Create multi-screen apps by learning about segues
  • Learn about advanced features of Optionals, including Optional Binding, Optional Chaining and Nil Coalescing Operator
  • Learn to use Color Literals to select colours from a palette.

UI슬라이더를 움직이면 레이블도 값이 변경되게

해줄려면 우선 액션을 위와 같이 만들자
이벤트는 값변경으로 되어있어야한다
이렇게 연결했다면 이제 이 액션이 발생했을때 값을 어케 출력하게할까?

당연히.. 안에 프린터문 넣어서 출력해보면 값이 변동되도록 설정가능하다.
그렇다면 위에 라벨이랑 이 값을 연결시켜주면 될것같다는 생각이 들지 않는가!? 그전에 소수점을 둘째자리까지 반올림하는 챌린지를 해보자.

    @IBAction func heightSliderChanged(_ sender: UISlider) {
        print(String(format: "%.2f", sender.value))
    }

https://formestory.tistory.com/21 를 참고하여 이런 방식으로 작성했다.따로 스위프트에는 round(aa,2)이런게 없나 보다..

오... 답도 위와 같은 방식이다 굿굿!
그리고, 소수점을 버리고 출력하고 싶으면

    @IBAction func weightSliderChanged(_ sender: UISlider) {
        print(Int(sender.value))
    }

이와 같이 Int로 형 변환해주면 된다.

이제 화면에 즉각 반응되게 고쳐볼건데

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var heightLabel: UILabel!
    @IBOutlet weak var weightLabel: UILabel!
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

    @IBAction func heightSliderChanged(_ sender: UISlider) {
        heightLabel.text = String(format: "%.2f", sender.value)
    }
    
    @IBAction func weightSliderChanged(_ sender: UISlider) {
        weightLabel.text = String(Int(sender.value))
    }
}

뭐,, 그냥 이런식으로 작성해주며 된다.
Weigt같은 경우 그냥 int로 넣어버리면 데이터형이 달라 오류가 뜬다
그래서 String으로 감싸주니깐~ 해결~

근데 안젤라 쌤은

weightLabel.text = String(format: "%.0f", sender.value)

일케 작성해따..
그런데 사용하면 m,kg은 날라가니깐 이게 안날라가게 해주자

    @IBAction func heightSliderChanged(_ sender: UISlider) {
        heightLabel.text = String(format: "%.2f", sender.value) + "m"
    }
    
    @IBAction func weightSliderChanged(_ sender: UISlider) {
        weightLabel.text = String(format: "%.0f", sender.value) + "kg"
        
    }

머,, 이런식으로 작성했는데 안젤라쌤은..

    @IBAction func heightSliderChanged(_ sender: UISlider) {
        let height = String(format: "%.2f", sender.value)
        heightLabel.text = "\(height)m"
    }
    
    @IBAction func weightSliderChanged(_ sender: UISlider) {
        let weight =  String(format: "%.0f", sender.value)
        weightLabel.text = "\(weight)kg"
    }

오...

이제 calculate해보자!

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var weightSlider: UISlider!
    @IBOutlet weak var heightLabel: UILabel!
    @IBOutlet weak var heightSlider: UISlider!
    @IBOutlet weak var weightLabel: UILabel!
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

    @IBAction func heightSliderChanged(_ sender: UISlider) {
        let height = String(format: "%.2f", sender.value)
        heightLabel.text = "\(height)m"
    }
    
    @IBAction func weightSliderChanged(_ sender: UISlider) {
        let weight =  String(format: "%.0f", sender.value)
        weightLabel.text = "\(weight)kg"
    }
    @IBAction func calculatePressed(_ sender: UIButton) {
        print(heightSlider.value)
        print(weightSlider.value)
    }
}

이렇게 코드를 작성해보면 몸무게와 키 값을 카큘레이트 버튼을 누르면 출력되는게 확인 가능하다.

BODMAS

이런게 있다니 신기하다..

    @IBAction func calculatePressed(_ sender: UIButton) {
        let height = heightSlider.value
        let weight = weightSlider.value
        // bmi = weight/(height)^2
        let bmi = weight / pow(height,2)
        print(bmi)
    }

이렇게 작성하면 bmi를 출력할 수 있따.
연산자의 순서는 위에 보드마스..!
제곱은 그냥 두번 곱해도 되고 pow method를 사용해도 좋다.

이제 class를 배우러가보자.
이에 대한 정리는
https://velog.io/@seochan99/udemy-Class-vs-Struct
이곳에 따로 정리해두었다.

이제 bmi계산한값을
새로운 스크린으로 넘기는 것을 해보자.

controller 폴더안에 SecondViewController 파일을 하나 생성하자.

import Foundation

class SecondViewController: UIViewController {
    <#code#>
}

그 후 class명을 파일명과 동일하게 하고
부모 클래스를 UIView를 들고오는데 선언할 수 없는 것이라고 뜬다..
근데 UIViewcontroller는 ViewController파일에서 슈퍼클래스로 가지고 왔는데 왜... 이 파일에서는 부모클래스로 못가지고 올까?

바로 UIkit를 import안해서 그렇다..
그러니 추가해주자..

그런데 UIKit 안에 foundation이 있으므로 키트만 임포트 해주면된다.
foundation 모듈에 관한 설명은
https://tong94.tistory.com/21
여기를 참고하자

import UIKit

class SecondViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

그래서 위와같이 작성해주자.
스토리보드 없이 디자인 해보자

import UIKit

class SecondViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .red
        
        // UIKiy에 있는 라벨 가져오기
        let label = UILabel() //레이블 생성
        label.text = "Hello"
        label.frame = CGRect(x: 0, y: 0, width: 100, height: 50)
        view.addSubview(label) //label이 UIView에 상속되고 있으므로 가능
        
    }
}

이렇게 디자인 가능하다.
그런데, 이 화면을 못불러온다 아직 그렇기에 viewcontroller에서 다음화면으로 이 화면이 떠지도록 코드를 추가해주자.

let secondVC = SecondViewController()
        
        self.present(secondVC, animated: true, completion: nil)

를 작성하고 실행하면

이와 같이 코드로만 스토리보드를 작성한 화면이 뜬다.

그럼 이제 bmi를 넘겨주도록하자
2번째 VC에

import UIKit

class SecondViewController: UIViewController {
    
    var bmiValue = "0.0"
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .red
        
        // UIKiy에 있는 라벨 가져오기
        let label = UILabel() //레이블 생성
        label.text = bmiValue
        label.frame = CGRect(x: 0, y: 0, width: 100, height: 50) // 그리기 
       
        view.addSubview(label) //label이 UIView에 상속되고 있으므로 가능
        
    }
}

이와 같이 bmiValue를 만들어주고 이 값을 lable에 준다.

...
        
        let secondVC = SecondViewController()
        secondVC.bmiValue = String(format: "%.1f", bmi) // 두번째뷰컨트롤에 있는 bmiValue 에 bmi할당
        //bmiValue는 String이므로 formt으로 변경해주기
        
   ... 
    }
}

그 후 이와 같은 방식으로 secondVC bmiValue에 bmi를 준다.
근데 String이므로 형변환을 진행해준다.

근데.. 결과화면 만들때 그냥 다시 파일을 삭제하고 아래와 같이
cocoa touch class
파일을 만들어준다.


그러면 이미 셋팅되어 있는 코드가 확인가능하다.

이제 이런 방식으로 스토리보드와 파일을 연결할 수 있다.

이렇게 assistant 수정이 가능했다..! 파일 고르는거! 대박

그러고 resultVC를 작성해주고, 그냥 vc는 헷갈리니깐~
파일명을 수정할려고 하면... storyboard랑 연결되어 있는 클래스명도 변경해줘야하고, 파일안에 클래스명도 변경해줘야하고
일이 많아진다.

그런데..
이런방식으로 리팩터를 해주면 한번에 변경이 가능하다!

그러면 이렇게 모두 찾아와준다!
신기..
그러고 변경하고 rename해주면 모두~ 바뀌게 된다!

그리고 이제 두 화면을 연결해주어야 하는데
이전 코드로 하는 방식과 다르게 storyboard의 segues를 사용하면된다.

이렇게 연결해줘서 segue를 생성해주면 된다.
그 후 이와같이 segue의 identifier를 작성해주자.

그 후 아까 화면을 연결한 곳을 다 지우고

self.performSegue(withIdentifier: "goToResult", sender: self)

이 한줄을 입력하면 화면이 넘어간다!

이제 bmiValue를 전달해줄려면 아래와 같이 코드를 작성해서 전달해주면된다..

CVC.swift

import UIKit

class CalculateViewController: UIViewController {

    var bmiValue = "0.0"
    
    @IBOutlet weak var weightSlider: UISlider!
    @IBOutlet weak var heightLabel: UILabel!
    @IBOutlet weak var heightSlider: UISlider!
    @IBOutlet weak var weightLabel: UILabel!
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

    @IBAction func heightSliderChanged(_ sender: UISlider) {
        let height = String(format: "%.2f", sender.value)
        heightLabel.text = "\(height)m"
    }
    
    @IBAction func weightSliderChanged(_ sender: UISlider) {
        let weight =  String(format: "%.0f", sender.value)
        weightLabel.text = "\(weight)kg"
    }
    @IBAction func calculatePressed(_ sender: UIButton) {
        let height = heightSlider.value
        let weight = weightSlider.value
        // bmi = weight/(height)^2
        let bmi = weight / pow(height,2)
        bmiValue = String(format: "%.1f", bmi)
        
        self.performSegue(withIdentifier: "goToResult", sender: self)
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        //UIView컨트롤러로 만들걸 우리 맘대로..
        //  Downcasting가능하게 해준다.
        if segue.identifier == "goToResult"{
            
            let destinationVC = segue.destination as! ResultViewController
            destinationVC.bmiValue = bmiValue
        }
    }
}

RVC.swift

import UIKit

class ResultViewController: UIViewController {
    
    var bmiValue : String?

    @IBOutlet weak var adviceLabel: UILabel!
    @IBOutlet weak var bmiLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        bmiLabel.text = bmiValue

    }
    

    @IBAction func recalculatePressed(_ sender: UIButton) {
    }
}

참.. 연결성이 중요한거같다..
그리고 다시 원래화면으로 돌아오는
재계산 버튼을 클릭하면 돌아오게할려면

@IBAction func recalculatePressed(_ sender: UIButton) {
        self.dismiss(animated: true, completion: nil)
    }

액션에 위와 같은 코드를 추가해주면된다.

그리고 이 겹겹이 를 통해 층계를 3디로 볼 수 있다

이제 bmi결과에 따라 다른 격려의메세지와 배경이 뜨도록해보자...
이건 MVC 중 M을 건들이면 된다.
CalculatorBrain을 만들어보자

import Foundation

struct CalculatorBrain{
    
    var bmi: Float?
    
    func getBMIValue() -> String {
        let bmiTo1DecimalPlace = String(format: "%.1f", bmi!)
        return bmiTo1DecimalPlace
        
    }
    
    mutating func calculateBMI(height : Float, weight : Float){
        bmi = weight / (height * height)
    }
    
    
}

이와 같이 작성가능하다..
근데 지금 bmi관련해서 어떻게 다뤄야할지 의문이다..
어찌 하든 오류가 뜬다
그래서 옵셔널을 어케 써야할지 한번 배워보자.

Using Optionals

이번장에서 아래 5가지를 확인해보자.

// 1. Force Unwrapping
optional!

// 2. Check for nil value
if optional != nil {
    optional!
}

// 3. Optional Binding
if let safeOptional = optional{
    safeOptional
}

// 4. nil Coalescing Operator
optional ?? defaulValue

// 5. Optional Chaining
optional?.property
optional?.method()

https://jwonylee.tistory.com/entry/Swift-Optional%EA%B3%BC-Optional-%EC%95%88%EC%A0%84%ED%95%98%EA%B2%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0
이도 참고하자
이와 같이 옵셔널을 ... 사용할 수 이따..
안전하게...어렵구만...

그래서
이를 본프로젝트에 적용하면
3가지 방법이 있는데

// 1
func getBMIValue() -> String {
        if let safeBMI = bmi {
            let bmiTo1DecimalPlace = String(format: "%.1f", safeBMI)
            return bmiTo1DecimalPlace
        }
        else {
            return "0.0"
        }
    }

// 2 

    func getBMIValue() -> String {
        if bmi != nil{
            let bmiTo1DecimalPlace = String(format: "%.1f", bmi!)
            return bmiTo1DecimalPlace
        }
        else {
            return "0.0"
        }
    }
    
// 3
    func getBMIValue() -> String {
        let bmiTo1DecimalPlace = String(format: "%.1f", bmi ?? 0.0)
        return bmiTo1DecimalPlace

    } 

안젤라는 3번째를 선호하다.

그 후 BMI는 Modele에 따로 만들어서
수치별로 다른 결과값을 전달하는 법을 생각해보자.

import UIKit

struct CalculatorBrain{
    
    var bmi: BMI?
    
    func getBMIValue() -> String {
        let bmiTo1DecimalPlace = String(format: "%.1f", bmi?.value ?? 0.0)
        return bmiTo1DecimalPlace

    }
    func getAdvice() -> String{
        return bmi?.advice ?? "No Adivce"
        
        
    }
    func getColor() -> UIColor{
        return bmi?.color ?? UIColor.white
    }
    
    
    mutating func calculateBMI(height : Float, weight : Float){
        let bmiValue = weight / (height * height)
        
        if bmiValue < 18.5{
            bmi = BMI(value: bmiValue, advice: "EAT MORE", color: #colorLiteral(red: 0.292, green: 0.081, blue: 0.6, alpha: 255))
        } else if bmiValue<24.9{
            bmi = BMI(value: bmiValue, advice: "NORMAL~", color: UIColor.green)
        }else{
            bmi = BMI(value: bmiValue, advice: "STOP EAT", color: UIColor.red)
        }
        
        
        
    }
    
    
}

이와 같이 계산해주고,각각 BMI,Adivce,Color Method를 만든다.

import UIKit

class ResultViewController: UIViewController {
    
    var bmiValue : String?
    var advice: String?
    var color: UIColor?

    @IBOutlet weak var adviceLabel: UILabel!
    @IBOutlet weak var bmiLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        bmiLabel.text = bmiValue
        adviceLabel.text = advice
        view.backgroundColor = color
    }
    
    @IBAction func recalculatePressed(_ sender: UIButton) {
        self.dismiss(animated: true, completion: nil) // 원래대로!
    }
    


}

그 후, viewDidLoad에 이렇게 바뀌도록 해준다.
그러면 우리가 원하는대로 색이 바뀌는것이 확인 가능하다..

옵셔널과 Model화 하는것을 더 느낄 수 있는 섹션이였다.

안젤라의 팁

점심을 배부르게 먹지말자
그러면 안졸릴거임

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

0개의 댓글