API를 활용해 JSON형식으로 된 날씨 정보 가져옴
protocol을 extension 해서 delegate패턴 사용
다크 모드
레이블 색상
Main.Stroyboard
에서 inspector
의 tint
의 색상을 Label Color
로 정해주게 되면 라이트 모드일 때는 검은색으로, 다크 모드일 때는 하얀색으로 색상의 컬러가 변하게 된다.
사용자가 지정한 Custom Color
일 때는 Assets
에서 Light
모드일 때와 Dark
모드 일 때의 색을 정해줄 수 있다.
배경 이미지
단순히 텍스트 컬러만 바꾸는 것이 아닌 background
의 image
도 변경할 수 있다. 백터 이미지나 pdf
이미지도 사용이 가능하다.
- Inspector > Scale > Single
- Appearance > Any, Light, Dark
다크 모드로 변경해도 설정한 배경화면이 보이지 않으면 preserve vector data
체크를 풀면 확인 가능. ⇧ + ⌘ + A
백터 이미지를 사용하면 좋은 점jpeg
나 png
같은 파일을 Assets
에 추가하여 확대해보면 이미지가 픽셀화 된 것을 볼 수 있는데, 화면이 커질수록 픽셀이 깨진다. 하지만 pdf
파일 형식으로 이미지를 가져오게 되면 이미지를 픽셀화 하지 않고 각각의 위치를 계산하기 때문에 픽셀이 깨지지 않는다. MainStoryboard
에서는 크기가 고정되어있어서 픽셀이 깨져 보이지만, 실제로 시뮬레이터를 돌려보면 깨지지 않는 것을 확인할 수 있다.
VC연결
1. textField
를 VC
에 연결하고 시뮬레이터를 실행한다.
2. textField
분을 터치해서 키보드를 확인할 수 있다. 만약 소프트웨어 키보드가 올라오지 않는다면 아래 키를 누르면 된다.
I/O > Keyboard > Toggle Software Keyboard (⌘ + K)
import UIKit
class WeatherViewController: UIViewController {
@IBOutlet weak var conditionImageView: UIImageView!
@IBOutlet weak var temperatureLabel: UILabel!
@IBOutlet weak var cityLabel: UILabel!
@IBOutlet weak var searchTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
@IBAction func searchPressed(_ sender: UIButton) {
print(searchTextField.text!)
}
}
UITextFieldDelegate
Apple Developer Documentation - UITextFieldDelegate
UITExtFieldDelegate에 대해 자세히 알아보기
텍스트 필드에 Korea를 입력하고 키보드 이동 버튼을 누르면 아무런 반응이 없는데, 이동 버튼을 눌렀을 때 반응을 나타나게 하기 위해선 UITextFieldDelegate
를 사용해야 한다.
이 Delegate
는 쉽게 말해서 ViewController
에게 전달하는 것이다. 무엇을? 사용자가 어떤 행동을 하고 있는지를.
"사용자가 지금 텍스트를 입력하고 있어!", "텍스트 입력을 멈췄어!", "사용자가 다른 곳을 탭 하려고 해!" 하는 것처럼 말이다.
1. UIViewController
에 쉼표를 하고 UITextFieldDelegate
를 추가한다.
class WeatherViewController: UIViewController, UITextFieldDelegate {
}
2. viewDidLoad
내부에 searchTextField.delegate = self
를 넣어준다. 여기서 self
는 viewController
를 뜻한다.
textFieldShouldReturn
3. 이 둘을 수행할 수 있게 해 주는 것이 textFieldShouldReturn
메서드이다. "사용자가 키보드에서 return
키를 눌렀다!"라고 VC
에 전달한다는 의미!
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
print(searchTextField.text!)
return true // shouldReturn 타입이 bool 이기 때문에 bool 타입을 반환해준다.
}
키보드의 리턴을 사용해야 합니까?(_ textField: UITextField)
이곳을 주의 깊게 살펴보면, 화면에 텍스트 필드가 여러 개여도 텍스트 필드마다 작업을 따로 설정해줄 필요가 없어진다.
키보드 닫기
키보드가 아닌 다른 화면 어딘가를 탭 했을 때 키보드가 닫히게 할 수 있다.
SearchPressed
내부에 endEditing
추가
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
searchTextField.endEditing(true)
print(searchTextField.text!)
return true
}
@IBAction func searchPressed(_ sender: UIButton) {
searchTextField.endEditing(true)
print(searchTextField.text!)
}
textFieldDidBeginEditing
First Responder가 된 직후 호출하는 인스턴스
textFieldDidEndEditing
4. textField
에 텍스트를 입력 후 이동 또는 서치 아이콘을 클릭했을 때 키보드가 닫히는 것까지는 했는데, "사용자가 텍스트 작성을 중지했다!"라고 뷰 컨트롤러에 전달했을 때 실행하는 부분을 textFieldDidEndEding
메서드를 사용하여 지정해줄 수 있다.
// 키보드의 return키 또는 search icon을 탭했을때 textField가 clear됨
func textFieldDidEndEditing(_ textField: UITextField) {
searchTextField.text = ""
}
textFieldShouldEndEditing
5. 유효성 검사에 유용한 메서드이다. 예를 들어 텍스트 필드가 empty가 아니라면 search 될 수 있도록 하고, empty라면 placeholder가 그대로 남아 있도록 해줄 수 있다. textFieldShouldEndEditing
메서드를 사용한다.
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
if textField.text != "" {
return true
} else {
textField.placeholder = "찾으려는 도시를 입력해주세요."
return false //입력되지 않았기 때문에 키보드를 숨기지 않는다.
}
}
API
API? Application Programming Interface (API)
쉽게 말해서 API
는 개발자와 API Provider(공급자) 간의 Contract(계약)이라고 보면 된다.
날씨 앱을 기준으로 보자면, 앱에 도시명을 입력 ➜ API Provider에게 전달 ➜ 해당 도시의 날씨를 개발자에게 전달이라고 보면 된다.
Open Weather Map API Documentation
OpenWeather 이 사이트로 가서 우선 회원가입을 하고 API
를 직접 사용해야 한다.
JSON data를 tree structure로 보려면 JSON Viewer Awesome를 크롬 extension을 다운로드하면 편하지만, https://jsonlint.com 이 웹사이트를 더 추천함.
API 호출 방법
api.openweathermap.org/data/2.5/weather?q={city name}&appid={API key}
//API key가 활성화되는데 까지는 10분에서 하루의 시간이 걸릴 수 있다.
서울의 날씨를 가져와 본다면 아래와 같은데, JSON
의 temp를 보면 290으로 표시되어있다. 이것은 켈빈이라는 단위이므로 이것은 섭씨로 바꿔줄 수 있다.
{
"coord": {
"lon": 126.9778,
"lat": 37.5683
},
"weather": [{
"id": 741,
"main": "Fog",
"description": "fog",
"icon": "50n"
}],
"base": "stations",
"main": {
"temp": 290.47,
"feels_like": 290.71,
"temp_min": 288.38,
"temp_max": 291.81,
"pressure": 1009,
"humidity": 94
},
"visibility": 2500,
"wind": {
"speed": 2.06,
"deg": 320
},
"clouds": {
"all": 4
},
"dt": 1632940392,
"sys": {
"type": 1,
"id": 8105,
"country": "KR",
"sunrise": 1632950780,
"sunset": 1632993472
},
"timezone": 32400,
"id": 1835848,
"name": "Seoul",
"cod": 200
}
측정단위를 변경하려면 Units of measurement 탭에서 확인 가능하다.
나의 경우엔 섭씨를 사용할 것이기 때문에 metric units를 선택하여 아래 주소를 tree structure로 변경하여 확인했다.
api.openweathermap.org/data/2.5/weather?q=Seoul&appid=내API입력&units=metric
Search 또는 Return키 터치
이제 Search
아이콘 또는 Return
키를 눌렀을 때 TextField
에 입력한 도시가 결과로 보이도록 해줘야 한다. 이것을 수행하는 곳은 textFieldDidEndEding
메서드이다.
위에서 TextField
의 text
를 ""
로 초기화했었는데 이것을 수행하기 전에 다음과 같이 진행하면 된다.
Xcode > New File > Swift > WeatherManager
weatherURL
에서 도시를 검색할 때 쿼리인 q=
를 입력했었는데 그것을 TextField에서
검색한 텍스트로 넣어줄 것이기 때문에 아래와 같이 코드를 변경해 줄 수 있다.
//WeatherManag.swift
import Foundation
struct WeatherManager {
let weatherURL = "https://api.openweathermap.org/data/2.5/weather?appid=내API입력&units=metric"
func fetchWeather(cityName: String) {
let urlString = "\(weatherURL)&q=\(cityName)"
print(urlString) //확인을 위한 출력
}
}
WeatherViewController
에서 weatherManager
를 초기화하여 호출해주고, textFieldDidEndEditing
메서드에 텍스트 필드에 입력된 텍스트를 도시명으로 가져와서 API
를 호출하려고 한다.
만약 let city = searchTextField.text
를 바로 가져오게 된다면 searchTextField
는 옵셔널 이기 때문에 래핑을 해줘야 한다.
//WeatherViewController.swift
class WeatherViewController: UIViewController, UITextFieldDelegate {
...
var weatherManager = WeatherManager()
func textFieldDidEndEditing(_ textField: UITextField) {
if let city = searchTextField.text {
weatherManager.fetchWeather(cityName: city)
}
searchTextField.text = ""
}
}
Networking
Swift에서 네트워킹 작업을 수행하려면 조건이 있다.
첫 번째 Create a URL
두 번째 Create a URLSession
세 번째 Give URL Session a task
네 번째 Start the task (hit the enter on the chrome)
//WeatherManager.swift
struct WeatherManager {
let weatherURL = "https://api.openweathermap.org/data/2.5/weather?appid=내API&units=metric"
func fetchWeather(cityName: String) {
let urlString = "\(weatherURL)&q=\(cityName)"
performRequest(urlString: urlString) //performRequest 메서드 호출
}
func performRequest(urlString: String) {
//1. Create a URL
if let url = URL(string: urlString) {
//2. Create a URLSession
let session = URLSession(configuration: .default) //브라우저
//3. Give URL Session a task (세션에 작업을 부여)
let task = session.dataTask(with: url, completionHandler: (Data?, URLResponse?, Error?) -> Void)
//4. Start the task (hit the enter on the chrome)
task.resume()
}
3번은 잠깐 보류하고 4번부터 보면, 왜 Start가 아닌 resume
을 썼을까?
그 이유는 "새로 초기화된 작업은 일시 중단된 상태에서 시작되므로 이 메서드를 호출하여 작업을 시작해야 하기 때문" 이다.
그럼 보류해뒀던 3번의 completionHandler
부분도 처리해야 한다.
Handler?
프로그래밍에서는 시간이 많이 걸리는 작업들이 있다.
인터넷에서 데이터를 다운로드해서 가져올 때 URL로 이동하여 데이터를 수집하고 다시 돌아와야 하는데, 개인의 인터넷 속도에 따라 1초 또는 몇 분의 시간이 소요될 수 있다. 개발자들은 항상 위와 같은 문제에 부딪힌다.
여기서 completionHandler
에 대해 알아두는 것이 좋은데 간단히 말해서 "어떠한 일이 끝났을 때 진행할 업무를 담당" 한다고 생각하면 된다.
3번을 보면 completionHandler
가 매개변수처럼 보이지만, 함수의 출력에 대해서 꺾쇠 출력이 Void
즉, 실제로 출력이 없다. 확실히 함수임을 알 수 있다. (함수를 만들어줘야 함)
let task = session.dataTask(with: url, completionHandler: handle(data:response:error:))
//4. Start the task (hit the enter on the chrome)
task.resume()
}
}
func handle(data: Data?, response: URLResponse?, error: Error?) -> Void {
}
}
handle
이라는 함수를 만들고 completionHandler
에 이 함수를 삽입해줬다.
차근차근 handle
함수를 풀어보자.
1. error
만약 error
가 nil
과 같지 않다면, 무슨 error
인지 출력하고 모든 것을 중지해라 (return
)
만약 return
뒤에 아무것도 오지 않는다면 "모든 기능을 중지 해라" 라는 의미이다.
func handle(data: Data?, response: URLResponse?, error: Error?){
if error != nil {
print(error!) //error는 옵셔널 상태이기때문에 강제 언래핑
return
}
}
2. data
error
에서 오류가 없다면 실행된다.
옵셔널바인딩을 사용하여 safeData
를 생성한다. 실제로 Data
는 문자열로 쉽게 출력이 가능하지만 safeData
를 출력하기 위해서는 문자열로 변환해야한다.
func handle(data: Data?, response: URLResponse?, error: Error?) -> Void {
if error != nil {
print(error!)
return
}
if let safeData = data {
let dataString = String(data: safeData, encoding: .utf8)
print(dataString)
}
}
이렇게 까지 작성하고 시뮬레이터를 실행해보면 JSON Data
가 출력되는 것을 확인할 수 있다.
---------- 문법 ----------
UITextField, UITextFieldDelegate
Protocols
클로저
'SWIFT > Udemy iOS' 카테고리의 다른 글
[Udemy] 섹션12: stepper, textField, segue 더치페이 계산기 (0) | 2021.08.12 |
---|---|
[Udemy] 섹션11: Segue, Cocoa Touch Class, Optional Binding, BMI계산기 (0) | 2021.08.09 |
[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 |