프로그래밍/iOS

Udemy iOS 강의 - 섹션 13

장장꾸 2023. 8. 2. 21:15

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 추가

 

UITextFieldDelegate는 Textfield의 텍스트를 편집하고, 유효성을 관리하는 메소드의 집합

 

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는 요청이 완료되면 호출되는 함수를 넣으면 됨

 

출처: developer.apple.com

공식 문서를 꼼꼼히 읽는 습관을 들이자^^!

 

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