Slider
- Value : 슬라이더의 기본 값
- Minimum : 슬라이더의 최솟값
- Maximum : 슬라이더의 최댓값
Slider의 값 출력 ( 소수점 )
소수점 출력 방법 ( 2 가지 )
첫 번째, String(format: "%.2f", sender.value)
두 번째, sender.value
는 Float
정수이기 때문에 Int(sender.value)
로도 가능
text 레이블로 연결
소수점 출력 방법을 let
상수로 지정한 후, Label.text
로 호출 함.
@IBAction func heightSliderBar(_ sender: UISlider) {
let height = String(format: "%.2f", sender.value)
heightLabel.text = "\(height)m"
print(height)
}
버튼 연결
UISlider
들은 개별적으로 분리되어있기 때문에 UIButton
에 슬라이더 value
을 가져오려면 Slider
를 IBOutlet
으로 가져와서 버튼에 호출할 수 있다.
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var heightLabel: UILabel!
@IBOutlet weak var weightLabel: UILabel!
@IBOutlet weak var heightSlider: UISlider!
@IBOutlet weak var weightSlider: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
heightSlider.value = 1.50
weightSlider.value = 100
}
@IBAction func heightSliderBar(_ sender: UISlider) {
let height = String(format: "%.2f", sender.value)
heightLabel.text = "\(height)m"
print(height)
}
@IBAction func weightSliderBar(_ sender: UISlider) {
let weight = String(format: "%.0f", sender.value)
weightLabel.text = "\(weight)kg"
print(weight)
}
@IBAction func pressedCalculate(_ sender: UIButton) {
let height = heightSlider.value
let weight = weightSlider.value
let bmi = weight / (height * height)
print(bmi)
}
}
체질량(BMI) 계산
UIButton
에 사용자의 몸무게와 키를 통해 체질량을 계산한 값을 나타내려면 다음과 같은 공식이 필요하다.
BMI : 자신의 무게를 키의 제곱으로 나눈 값
swift에서의 제곱
// 몸무게 / (신장 * 신장)
let bmi = weight / (height * height)
// pow(double, double) = height의 2제곱
let bmi = weight / pow(height, 2)
@IBAction func pressedCalculate(_ sender: UIButton) {
let height = heightSlider.value
let weight = weightSlider.value
let bmi = weight / pow(height, 2)
print(bmi)
}
다른 뷰로 값을 전달하는 방법
1. Main.storyboard
에서 object library
중 View Controller
를 추가
2. Controller폴더 우클릭 > New File > Swift File > SecondViewController
import UIKit
// SecondViewController의 superView는 UIViewController
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
UIKit
은 Foundation
을 포함하는 더 큰 프레임워크이다.
View를 연결하는 방법
첫 번째, 뷰 컨트롤러에서 다른 뷰 컨트롤러를 호출하여 화면 전환
// ViewController.swift
let secondVC = SecondViewController()
secondVC.bmiValue = String(format: "%.2f", bmi)
self.present(secondVC, animated: true, completion: nil)
// SecondVieWController.swift
import UIKit
class SecondViewController: UIViewController {
var bmiValue = "0.0"
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .yellow
let label = UILable()
label.text = bmiValue
label.frame = CGRect(x:0, y:0, width:100, heigth: 50)
view.addSubview(label)
}
}
SecondViewController
에서 UILabel()
이 UIView
를 상속받았기 때문에 view.addSubview(label)
이 가능하다.
두 번째, Cocoa Touch Class : Segue 세그웨이
UIKit
, UIViewController
, viewDidLoad
를 이미 포함하고 있는 프레임워크
일단 우리는 위에서 작성한 SecondeVC
를 사용하지 않을 것이다. 대충 저런 식으로 main Stroybard
없이 코드를 구현할 수 있다는 것 정도만 일단 알아두자!
ViewController 파일 생성
⌘ + N > Cocoa Touch Class
Class
: Some ViewController Name
Subclass of
: UIViewController
VC와 SecondVC 연결
viewController
를 ⌃ + 오른쪽 마우스 클릭 > SecondVC
로 끌어당김 > Present Modally
SecondVC Class 연결
SecondViewController
의 class
를 연결해주고 assistant
를 띄울 수 있다.
VC와 SecondVC Segue Indentifier 연결
viewController
와 SecondViewController
의 indetifier
를 정해주어 고유한 코드를 넣어준다.
performSegue & prepare
CalculateVC
에서 ResultVC
를 연결할 때 준비 과정
1. self.perporm(withIdentifier:String, sender: self)
2. 준비과정인 prepare()
사용
// ResultViewController.swift
import UIKit
class ResultViewController: UIViewController {
var bmiValue: String?
@IBOutlet weak var bimLabel: UILabel!
@IBOutlet weak var adviceLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
bimLabel.text = bmiValue
}
@IBAction func recalculatePressed(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
}
AS! 다운 캐스팅
CalculateVC
에서 ResultVC
를 호출할 때 준비 과정인 prepare
을 override
하여 메서드를 가져왔고, 조건문으로는 segue
의 indetifier
가 맞다면, destinationVC
를 다운 캐스팅을 통해 ResultVC
와 연결해준다.
// CalculateViewController.swift
var bmiValue = "0.0"
@IBAction func pressedCalculate(_ sender: UIButton) {
let height = heightSlider.value
let weight = weightSlider.value
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?) {
if segue.identifier == "goToResult" {
// 🔥 UIStoryboardSegue의 destination은 UIViewController인데 이걸을 as!(다운 캐스팅)을통해 ResultVC 으로 캐스팅해준다.
let destinationVC = segue.destination as! ResultViewController
destinationVC.bmiValue = bmiValue
}
}
Dismiss ( back button)
self.dismiss(animated: true, completion: nil)
를 사용하여 ResultVC
에서 back버튼을 누르면 이전 화면으로 돌아가게 할 수 있다.
// ResultViewController.swift
@IBAction func recalculatePressed(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
옵셔널 사용
1. Model 폴더 > CalculatorBrian
파일 생성
2. calculateBMI(height:, weight:)
함수 생성
3. 소수점 자리를 나타내는 getBmiValue()
함수 생성.
4. CalculateVC
에 model
에서 만든 함수 호출 ( CalculatorBrain
초기화 필수 )
// CalculateViewController.swift ( Controller )
class CalculateViewController: UIViewController {
// CalculatorBrain 초기화
var calculatorBrain = CalculatorBrain()
@IBAction func pressedCalculate(_ sender: UIButton) {
...
// calculateBMI 함수 호출
calculatorBrain.calculateBMI(height: height, weight: weight)
...
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
...
// getBmiValue 함수 호출
destinationVC.bmiValue = calculatorBrain.getBmiValue()
}
}
}
// CalculatorBrain.swift ( Model )
struct CalculatorBrain {
var bmiValue: Float = 0.0
mutating func calculateBMI(height: Float, weight: Float) {
bmiValue = weight / pow(height, 2)
}
func getBmiValue() -> String {
let bmiToOneDecimalPlace = String(format: "%.1f", bmiValue)
return bmiToOneDecimalPlace
}
}
문제점
위 코드의 brain
을 보면 bmiValue
의 기본값이 0.0
으로 되어있다. 이것을 Float
혹은 Float?
으로 기본값을 주지 않는다면 getBmiValue
메서드의 bmiValue
에 error
가 나타난다. 왜냐? bmiValue
는 nil
이기 때문이지!
또, 기본값을 주지 않으면 viewController
에 우리가 초기화했었는데 거기에도 값을 주라고 아래와 같이 오류가 난 것을 확인할 수 있다!
해결방법
옵셔널을 이해하면 위 문제를 해결할 수 있다. 옵셔널에 대한 문법
첫 번째 방법) if let
으로 safe unwrapping 해줌.
두 번째 방법) nil Coalescing Operator
을 사용하여 축약할 수 있음. 추천 ⭐️
//CalculatorBrain.swift (model)
struct CalculatorBrain {
var bmiValue: Float?
//🔥 struct의 bmiValue를 변화시키기 때문에 mutating 키워드를 반드시 사용함
mutating func calculateBMI(height: Float, weight: Float) {
bmiValue = weight / pow(height, 2)
}
func getBmiValue() -> String? {
// if let safeBMI = bmiValue {
// let bmiToOneDecimalPlace = String(format: "%.1f", safeBMI)
// return bmiToOneDecimalPlace
// } else {
// return "0.0"
// }
// 🔥 Nil Coalescing Operator를 사용하여 축약할 수 있다.
let bmiToOneDecimalPlace = String(format: "%.1f", bmiValue ?? 0.0)
return bmiToOneDecimalPlace
}
}
MVC 패턴 리팩터링 ( 옵셔널 체이닝 )
이제 여태까지 했던 코드를 MVC 패턴으로 리팩터링 하는 동시에 bmi
수치에 따라 advice
와 backgroundColor
가 변경될 수 있도록 해주겠음! 옵셔널체이닝에 대한 문법
1. Bmi
model 생성
2. Bmi
구조체에 value
, advice
, color
를 상수로 만듦
3. ResultVC
에 advice
및 color
를 옵셔널 상수로 만듦
3. CalculatorBrain
에 Bmi
model을 옵셔널로 호출과 동시에 코드 수정
4. calculateBMI
메서드에 bmi?.value
를 추가하는 대신 임시 상수를 생성함
5. calculateBMI
메서드에 bmi
지수별 advice
와 backgroundcolor
를 나타내는 if
조건문 생성
6. CalculateVC
의 ovrride prepare
메서드의 advice
와 color
에 함수를 호출함
<hide/>
// Bmi.swift ( Model )
struct Bmi {
var value: Float
var color: UIColor
var advice: String
}
<hide/>
//ResultViewController.swift ( Controller )
import UIKit
class ResultViewController: UIViewController {
var bmiValue: String?
var advice: String?
var color: UIColor?
@IBOutlet weak var bimLabel: UILabel!
@IBOutlet weak var adviceLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
bimLabel.text = bmiValue
adviceLabel.text = advice
view.backgroundColor = color
}
@IBAction func recalculatePressed(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
}
<hide/>
//CalculatorBrain.swift
import UIKit
struct CalculatorBrain {
var bmi: Bmi?
mutating func calculateBMI(height: Float, weight: Float) {
let bmiValue = weight / pow(height, 2)
switch bmiValue {
case 0...18.4:
bmi = Bmi(value: bmiValue, color: #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1), advice: "저체중 입니다.")
case 18.5...22.9:
bmi = Bmi(value: bmiValue, color: #colorLiteral(red: 0.3411764801, green: 0.6235294342, blue: 0.1686274558, alpha: 1), advice: "정상체중 입니다.")
case 23...24.9:
bmi = Bmi(value: bmiValue, color: #colorLiteral(red: 0.9529411793, green: 0.6862745285, blue: 0.1333333403, alpha: 1), advice: "과체중 입니다.")
case 25...:
bmi = Bmi(value: bmiValue, color: #colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1), advice: "비만 입니다.")
default:
bmi = Bmi(value: bmiValue, color: #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1), advice: "저체중 입니다.")
}
}
func getBmiValue() -> String? {
// 🔥 bmi?.value ?? 0.0 옵셔널 체이닝
let bmiToOneDecimalPlace = String(format: "%.1f", bmi?.value ?? 0.0)
return bmiToOneDecimalPlace
}
func getAdvice() -> String? {
let bmiAdvice = bmi?.advice ?? "empty"
return bmiAdvice
}
func getColor() -> UIColor {
return bmi?.color ?? #colorLiteral(red: 0, green: 0.46, blue: 0.89, alpha: 1)
}
}
<hide/>
//CalculateViewController.swift ( Controller )
import UIKit
class CalculateViewController: UIViewController {
var calculatorBrain = CalculatorBrain()
@IBOutlet weak var heightLabel: UILabel!
@IBOutlet weak var weightLabel: UILabel!
@IBOutlet weak var heightSlider: UISlider!
@IBOutlet weak var weightSlider: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
heightSlider.value = 1.50
weightSlider.value = 100
}
@IBAction func heightSliderBar(_ sender: UISlider) {
let height = String(format: "%.2f", sender.value)
heightLabel.text = "\(height)m"
print(height)
}
@IBAction func weightSliderBar(_ sender: UISlider) {
let weight = String(format: "%.0f", sender.value)
weightLabel.text = "\(weight)kg"
print(weight)
}
@IBAction func pressedCalculate(_ sender: UIButton) {
let height = heightSlider.value
let weight = weightSlider.value
calculatorBrain.calculateBMI(height: height, weight: weight)
self.performSegue(withIdentifier: "goToResult", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToResult" {
let destinationVC = segue.destination as! ResultViewController
destinationVC.bmiValue = calculatorBrain.getBmiValue()
destinationVC.advice = calculatorBrain.getAdvice()
destinationVC.color = calculatorBrain.getColor()
}
}
}
완성 파일
---------- 문법 ----------
Order of operations (Mathematical)
swift
에서 계산이 완료되는 순서는 아래와 같다.
B > O > D > M > A > S
Classes
- SuperClass
- SubClass
- Override
SuperClass & SubClass
Inheritance(상속) - override
subClass
는 superClass
를 상속받아서 superClass
에 있는 메서드를 활용할 뿐 아니라 override
(변경)하고 추가할 수 있다.
superClass
의 메서드를 override
를 사용하여 재정의 한다.
// superClass
class Seogun {
func name() {
print("저의 이름은 서근 입니다")
}
}
// subClass
class Mijin: Seogun {
override func name() {
print("저의 이름은 미진 입니다")
}
}
subClass
에서 superClass
의 메서드를 재정의한 메서드와 함께 실행하려면 subClass
에서 super
키워드를 사용한다.
// subClass
class Mijin: Seogun {
override func name() {
super.name()
print("저의 이름은 미진 입니다")
}
}
Class 프로젝트는 macOS의 Command tool로 만들어서 실습한다
File > New > Project > macOs > Command Line Tool > Product Name : Classes Something
Practice
main
<hide/>
// main
let seogung = Seogun()
seogung.move()
seogung.attack()
let dragon = Dragon()
dragon.power = 500
dragon.move()
dragon.talk(speech: "불꽃 회오리!")
dragon.attack()
superClass
<hide/>
// superClass
class Seogun {
var power = 50
var health = 100
func move() {
print("앞으로 이동한다!")
}
func attack() {
print("\(power)의 데미지로 적을 공격했다!")
}
}
subClass
<hide/>
// subClass
class Dragon: Seogun {
var dragonFire = 1000
func talk(speech: String) {
print("드래곤: \(speech)")
}
override func attack() {
print("드래곤이 불을 내뿜어 \(dragonFire) 만큼의 데미지를 입혔다!")
}
override func move() {
super.move()
print("드래곤이 하늘을 향해 날아갔다!")
}
}
Swift 상속 관계
NSObject > UIResponder > UIView > UIControl > UIButton/UILabel/UISlider...
Struct & Class
Class
는 참조 타입이다. 클래스는 init()
으로 초기화를 필수로 해줘야 한다.
위 이미지를 보면 내가 누군가와 어떠한 파일을 공유할 때, 사본으로 상대방과 공유하는 것이 아닌 정확한 로컬의 위치를 알려주어 모든 사람과 그 파일이 공유될 수 있도록 한다.
이것의 단점으로는 공유받은 어느 한 사람이라도 그 파일을 지우거나 수정하게 된다면 모든 사람들이 이에 영향을 받게 된다.
Struct
는 class
와 다르게 값으로 전달하는 타입이다. 즉, 위에 이미지처럼 내가 어떠한 사진을 가지고 다른 사람에게 공유를 할 때 사본을 만들어서 그 이미지를 준다.
그렇기 때문에 다른 사람이 그 사본을 가지고 삭제를 하거나, 무언가를 수정해도 또 다른 사본이나 원본은 아무런 영향이 없다.
이 점 때문에 Apple
에서는 Class
보다 Struct
를 사용하는 것을 권장하고 있다.
Struct와 Class 차이점
Structure
:init()
을 생성하지 않아도 됨.- immutable(불변) 하기 때문에 안에 생성되어 있는 변수의 값을 바꾸기 위해서는
mutating
키워드를 사용 - 상속이 불가능함.
- Apple은
struct
를 사용하기를 권장함.
Class
:init()
을 생성해야 함.- 참조값을 가짐.
Object-C
와 함께 사용할 때 주로 사용함.
class Seogun {
var health: Int
init(health: Int) {
self.health = health
}
func takeDamage(amount: Int) {
health = health - amount
}
}
let Character1 = Seogun(health: 100)
let Character2 = Character1
Character1.takeDamage(amount: 20)
Character2.takeDamage(amount: 20)
print("Character1 의 hp : \(Character1.health)")
print("Character2 의 hp : \(Character2.health)")
struct Seogun {
var health: Int
init(health: Int) {
self.health = health
}
// stuct의 속성을 변경했기 때문에 mutation 키워드 사용
mutating func takeDamage(amount: Int) {
health = health - amount
}
}
var Character1 = Seogun(health: 100)
var Character2 = Character1
Character1.takeDamage(amount: 20)
Character2.takeDamage(amount: 40)
print("Character1 의 hp : \(Character1.health)")
print("Character2 의 hp : \(Character2.health)")
Optionals
1. Force Unwrapping ( 강제 언래핑 )
optional!
강제로 언래핑 하는 방법이다. 하지만 이것은 사용하지 않는것이 좋다. 아래 코드에 myOptional
이 nil
이고 이것을 강제로 언래핑 시켰을 때 코드에서는 오류가 생기지 않지만, 앱을 실행하면 Optional 값의 래핑을 해제하는 동안 예기치 않게 nil
이 발견되었습니다.라는 오류와 함께 crush 된다.
let myOptional: String?
myOptional = nil
let text: String = myOptional!
print(text) // Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
2. Check for nil value ( nil 값 확인 )
if myOptional != nil {
myOptional!
}
myOptional
이 nil
이 아니라면 text
는 myOptional!
이라고 조건문으로 코드를 구현했는데 이곳에도 반드시 강제언래핑 ! 을 해줘야 한다.
let myOptional: String?
myOptional = "Check for nil value (nil 값 확인)"
if myOptional != nil {
let text: String = myOptional!
print(text)
} else {
print("이 값은 nil 입니다.")
}
//Check for nil value (nil 값 확인)
3. Optional Binding ( useful ⭐️⭐️⭐️⭐️⭐️ )
myOptional
이 nil
이 아니라면, unwrap(언래핑)된 safeOptional
을 { } 안에서 바인딩하여 사용한다.
- safeOptional
의 데이터 타입은 String
- myOptional
의 데이터 타입은 String?
if let safeOptional = myOptional {
print(safeOptional)
}
let myOptional: String?
myOptional = nil
if let safeOptional = myOptional {
let text: String = safeOptional
print(text)
} else {
print("이 값은 nil 입니다.")
}
//이 값은 nil 입니다.
4. Nil Coalescing Operator ( 추천 )
optional
이 nil
이 아니면 optional
값을 사용하고, nil
이면 기본값으로 정해준 defaultValue
를 사용하게 된다.
optional ?? defaultValue
아래는 myOptional
이 값이 있기 때문에 text
를 print
하면 "myOptional" 이 출력된다.
let myOptional: String?
myOptional = "myOptional"
let text:String = myOptional ?? "nil 병합 연산자"
print(text)
5. Optional Chaining ( useful ⭐️⭐️⭐️⭐️⭐️ )
optional
이 nil
이 아니라면 property
또는 method()
에 접근할 수 있다.
optional?.property
optional?.method()
// optional이 nil이 아니면 property에 접근한다
Struct에서 사용
struct MyOptional {
var property = "서근"
func method() {
print("MyOptional의 method 입니다.")
}
}
let myOptional: MyOptional?
myOptional = nil
print(myOptional?.property) // nil
struct MyOptional {
var property = "서근"
func method() {
print("MyOptional의 method 입니다.")
}
}
let myOptional: MyOptional?
myOptional = MyOptional()
print(myOptional?.property) // "서근"
'SWIFT > Udemy iOS' 카테고리의 다른 글
[Udemy] 섹션13 : Protocols, Networking, Delegate 패턴, JSON Parsing, UITextField 날씨 앱 (0) | 2021.09.12 |
---|---|
[Udemy] 섹션12: stepper, textField, segue 더치페이 계산기 (0) | 2021.08.12 |
[Udemy] 섹션 10: iOS App Design Pattern Challenge (0) | 2021.08.05 |
[Udemy] 섹션9: MVC 패턴, Struct, mutating ( 퀴즈 앱 ) (0) | 2021.08.02 |
[Udemy] 섹션8: Egg Timer / ProgressView ( Control Flow and Optionals ) (0) | 2021.07.31 |