Color Set 생성
다크 모드에서도 내 커스텀 컬러가 적용될 수 있도록!

우측 Inspector - Appearances - Any, Light, Dark 선택 - 라이트 모드, 다크 모드에 맞게 원하는 색상 추가
Vector assets 사용
PNG, JPEG와 같은 스칼라 이미지는 확대했을 때 깨지는 현상 발생

물론 이 경우를 대비해서 크기에 맞춰 여러 사진을 준비하는 방법이 있음
그러나 여기서는 벡터 이미지를 사용해보자

벡터 이미지를 드래그하여 갖다놓기
1. 'Preserve Vector Data'를 체크해야 함 ← 확대 했을 때도 벡터 데이터를 유지하기 위해
2. Scales를 Single Scale로 선택 ← 1x, 2x, 3x가 필요 없기 때문
UITextField로 사용자에게 입력값 받기

라이트 모드, 다크 모드에 따라서 글자색, 배경색이 바뀌도록 초기에 설정되어 있음
대문자로 바꿀 것인지, 비밀번호처럼 *** 처리할 것인지, 키보드 타입은 어떻게 할지 원하는대로 설정 가능!
시뮬레이터에서 키보드가 보이지 않을 때
TextField를 눌렀는데 키보드가 보이지 않으면 Cmd + K 버튼을 누르기!
키보드의 Go(Return, Done) 버튼에 기능 추가
TextField 옆의 돋보기 모양 버튼에는 @IBAction으로 기능을 추가할 수 있는데, 같은 기능을 키보드의 Go 버튼에도 추가하고 싶음
1. ViewController에 UITextFieldDelegate 추가

2. viewDidLoad() 안에서 delegate 설정

text field에 delegate를 현재 view controller(self = 현재 view controller)로 설정
→ text field로부터 받는 메시지("유저가 타이핑을 시작했다!", "타이핑을 멈췄다!", "유저가 다른 곳을 탭했다!" 등)에 반응할 수 있게 됨
3. textFieldShoudReturn 메소드 생성
유저가 Return 버튼을 눌렀을 때 처리할 기능을 담을 메소드

+ 키보드 닫기
searchTextField.endEditing(true)
+ 키보드가 닫혔을 때, 취하고 싶은 액션이 있다면
textFieldDidEndEditing 메소드 생성 ← .endEditing()으로 인해 text field 편집이 종료된 경우 트리거됨
ex) 키보드가 닫히면 textField 안에 있는 내용을 지우고 싶음

+ 특정 상태에 따라 text field 편집을 계속할지 종료할지 결정하고자 한다면
textFieldShoudEndEditing 메소드 사용 ← 해당 텍스트 필드에서 편집을 종료할지 말지 결정하는 메소드

Swift - Protocol
- 클래스, 구조체, 열거형과 같은 타입에 특정 속성이나 메서드, 그리고 연관된 기능을 강제함
- 일종의 인터페이스 역할(다중 상속, 추상화 역할 수행)
- 위 타입들은 하나 이상의 프로토콜을 채택하여 해당 프로토콜에서 정의한 요구사항을 구현하도록 할 수 있음
- ex) '새' 타입을 상속하여 독수리, 참새, 펭귄을 만들었는데, '새'의 기본 속성 중 '날기(fly)'를 못하는 새가 있을 수 있음 ← 펭귄
- 그러면 펭귄은 실제로 날지 못하지만 fly라는 속성을 갖게 되는 문제 발생!
- Protocol을 객체의 타입으로 가질 수 있음
- Superclass와 Protocol을 동시에 상속한다면 Superclass명을 먼저 적기
protocol CanFly(){
func fly()
}
class Bird {
var isFemale = True
func layEgg() {
if isFemale {
print("새가 알을 낳았다")
}
}
}
class Eagle: Bird, CanFly {
func fly(){
print("독수리가 날고 있다")
}
func soar(){
print("독수리가 활공하고 있다")
}
}
class Penguin: Bird {
func swim() {
print("펭귄이 물 속에서 헤엄치고 있다")
}
}
struct FlyingMuseum: CanFly {
func flyingDemo(flyingObject: CanFly){
flyingObject.fly()
}
}
struct Airplane: CanFly {
func fly(){
print("비행기가 날고 있다")
}
}
let myEagle: Eagle()
let myPenguin: Penguin()
let myPlane: Airplane()
let museum = FlyingMuseum()
museum.flyingDemo(flyingObject: myPlane) // myEagle 가능, myPenguin 불가능
Delegate Design Pattern
객체가 다른 객체에 대해 특정 동작을 요청하고, 해당 동작을 처리하는 객체가 위임자로 선택됨
따라서, 객체들 사이의 결합도 ↓ / 재사용성 ↑ / 확장성 ↑
protocol AdvancedLifeSupport { // 이 타입이라면 CPR 자격증을 보유한 사람이라는 것을 보증
func performCPR()
}
class EmergencyCallHandler { // 119 상담원
var delegate: AdvancedLifeSupport? // delegate은 CPR 자격증이 있는 사람이어야 함
// 무슨 superclass를 상속했는지, 무슨 클래스인지는 관심 없음. 오로지 CPR을 할 줄 아는 사람이기만 하면 됨
func describeSituation() {
print("상황에 대한 설명")
}
func medicalEmergency() {
delegate?.performCPR()
}
}
struct Paramedic: AdvancedLifeSupport {
init(handler: EmergencyCallHandler) { // 구급대원은 119 상담원을 알아야 지시 받을 수 있음
handler.delegate = self
}
func performCPR() {
print("구급대원이 가슴 압박 실시")
}
}
class Doctor: AdvancedLifeSupport {
init(handler: EmergencyCallHandler){
handler.delegate = self
}
func performCPR() {
print("의사가 가슴 압박 실시")
}
func useFlashLight() {
print("동공 반응 확인")
}
}
class Surgeon: Doctor {
override func performCPR() {
super.performCPR()
print("일정한 속도로 실시")
}
func useScalpel() {
print("메스로 절개")
}
}
let emily = EmergencyCallHandler()
let tim = Paramedic(handler: emily)
// let matthew = Surgeon(handler: emily)
// 응급 콜을 받는 사람(타입)이 바뀌어도 아래 코드 2줄은 수정하지 않아도
// 자동으로 Surgeon인 matthew가 콜에 적절하게 대응함
emily.describeSituation()
emily.medicalEmergency()
// assessSituation -> medicalEmergency -> performCPR 순으로 실행됨
API 사용
1. URL 생성
2. URLSession 생성
3. 세션에게 task 주기
.dataTask 메소드 사용
completionHandler는 요청이 완료되면 호출되는 함수를 넣으면 됨


공식 문서를 꼼꼼히 읽는 습관을 들이자^^!
4. task 시작
.resume() 메소드 사용
Swift - Closures
익명 함수
장점: 코드의 간결성
그러나 가독성이 떨어질 수 있으므로 중요도에 따라 간결성과 가독성 사이에서 잘 판단하여 선택할 것!
기본적인 문법:
{ ( 매개변수들 ) -> 리턴 타입 in
statements
}
func calc (no1: Int, no2: Int, operation: (Int, Int) -> Int) -> Int {
return operation(no1, no2)
}
// 1) 원래 형태
func add (n1: Int, n2: Int) -> Int {
return n1 + n2
}
let res1 = calc(no1: 10, no2: 11, operation: add)
// 2) closure로 변환
let res2 = calc(no1: 10, no2: 11, operation: {(n1: Int, n2: Int) -> Int in
return n1 + n2
})
// 3) 간결해진 closure - 타입 명시 필요 X(타입 추론). body가 한 줄이라 return 키워드도 X
let res3 = calc(no1: 10, no2: 11, operation: {(n1, n2) in n1 + n2})
// 4) 익명 변수 사용($0 = 1번째 변수)
let res4 = calc(no1: 10, no2: 11, operation: {$0 + $1})
// 5) trailing closure - closure가 마지막 매개변수라면 괄호 밖으로 빼기
let res5 = calc(no1: 10, no2: 11) {$0 + $1}
// res1, res2, res3, res4, res5는 모두 21을 저장
클로저에서는 현재 클래스에서 메소드를 호출할 때, self 키워드를 추가해줘야 함
JSON 디코딩
1. 디코더 생성: JSONDecoder()
let decoder = JSONDecoder()
2. 디코딩 결과를 담을 Swift 객체 생성


JSON 객체를 디코딩해서 Swift 객체로 만들기 위해서는 Decodable 프로토콜이 필요함
+ 변수명을 JSON 파일에서 확인할 수 있는 이름과 동일하게 만들어야 함
그러나 인코딩 역시 필요하다면 Encodable 프로토콜도 달아줘야 함!
typealias는 별명을 붙이는 것으로, 여기서는 Decodable, Encodable을 묶어서 Codable이라는 별명을 지었음
Codable이라고만 적으면 인코딩, 디코딩 모두 가능

3. 디코딩
do {
let decodedData = try decoder.decode(WeatherData.self, from: weatherData)
// 디코딩 결과 출력
// print(decodedData.weather[0].description)
} catch {
print(error)
}
4. 결과

Computed Property
연산 결과에 따라 변수의 값을 지정하는 방법
별도의 함수를 선언하고, 이 함수를 호출하여 변수에 값을 할당하는 번거로움을 줄여준다는 장점이 있음
따라서, var로 선언해야 함! 연산 결과에 따라 값이 결정되기 때문

매개변수 레이블
외부, 내부 매개변수 레이블이 있음
func myFunc(name t: Type){ // 외부: name, 내부: t
print(t)
}
// 호출
myFunc(name: value)
외부는 말 그대로 함수 외부에서 함수를 호출할 때 사용하는 것
외부 매개변수 레이블을 _로 지정하면 함수를 호출할 때 해당 레이블을 생략하는 것으로, 따로 레이블을 적을 필요가 없음
내부 매개변수는 함수 내부에서 처리할 때 사용하는 레이블
func gift(_ thing: String, to name: String){
print("I wanna give \(thing) to \(name).")
}
// 호출할 때
gift("Mango", to: "Brian")
DispatchQueue
날씨 API에서 받아온 값으로 UI를 업데이트하려는데
UILabel.text must be used from main thread only 에러 발생
발생 이유:
Networking과 같이 오래 걸릴 수 있는 작업으로 UI를 업데이트하려고 해서!
네트워크 상태가 안 좋다거나 여러 상황 때문에 데이터를 받아오는 시간이 오래 걸리면 그 시간 동안 UI를 업데이트할 수 X
해결법:
DispatchQueue.main.async { ... } 안에 UI 업데이트 코드 넣기
Swift - Extensions
기존 클래스, 구조체 등에 추가 기능을 넣을 수 있게 함
extension SomeType {
// 추가 기능
}
Double 자료형의 round() 메소드는 정수로 반올림한 값을 리턴함
14.278이라는 실수를 반올림하여 소수점 이하 2자리만 남기고 싶다면?
100을 곱해서 1427.8을 만들기 → round()하면 1428 → 다시 100으로 나누면 14.28
이런 과정을 거쳐야 함...
따라서 메소드 오버로딩으로 round() 메소드의 기능을 추가하면 편리함
extension Double {
func round(to places: Int) -> Double {
let precision = pow(10, Double(places))
var n = self // 현재 Double 값
n *= precision
n.round()
n /= precision
return n
}
}
var pi = 3.141592
pi.round(to: 1) // 반올림하여 소수점 이하 1자리만 남긴 값
휴대폰의 GPS 기능 사용
1. import CoreLocation
2. CLLocationManager 생성
3. requestWhenInUseAuthorization() ← 사용자에게 위치 권한 요청
4. Info.plist에서 "Privacy - Location When In Use Usage Description" 추가하고 유저에게 보여줄 메시지 추가
5. requestLocation() ← 한번만 위치 정보 가져옴
와웅 이번 섹션은 굉장히 유익했다..!
출처: https://www.udemy.com/course/ios-13-app-development-bootcamp/
'프로그래밍 > iOS' 카테고리의 다른 글
| Udemy iOS 강의 - 섹션 15 (0) | 2023.08.22 |
|---|---|
| Udemy iOS 강의 - 섹션 11~12 (1) | 2023.07.19 |
| Udemy iOS 강의 - 섹션 9~10 (0) | 2023.07.18 |
| Udemy iOS 강의 - 섹션 7~8 (0) | 2023.07.17 |
| Udemy iOS 강의 - 섹션 6 (0) | 2023.07.08 |