-
[์ผ๋ฐ ๊ณผ์ ]
โถ๏ธ 6์ฃผ์ฐจ ์ธ๋ฏธ๋ ๊ณผ์ ์์ ํ์๊ฐ์ ๊ณผ ๋ก๊ทธ์ธ์ด ์ ์งํ๋๋๋ก ํ๋ ๊ฒ์ด ๊ณผ์ ๐ฅ
๐ ๊ทธ ์ ์ ๋คํธ์ํฌ ํต์ ์ ๋ํด์ ์ ๋ฆฌํ๋ ์๊ฐ ๐
- POST : ๋ฐ์ดํฐ๋ฅผ
BODY
์ ์จ๊ฒจ ์๋ฒ์ ์ ๋ฌํ๋ ๋ฐฉ์, ๋ฐ์ดํฐ์ ์ ์ก๊ณผ ๋๋ถ์ด๊ฐฑ์
,์ฝ์
์๋ ์ฌ์ฉ๋๋ค. - GET :
URL
์ ๋ณ์๋ฅผ ํฌํจ์์ผ ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ์์ฒญํ๋ ๋ฐฉ์, ๋ฆฌ์์ค๋ฅผ ์ฝ์ ๋ ์ฃผ๋ก ์ฌ์ฉํ๋ค. - PUT : ํด๋น ๋ฆฌ์์ค์ ๋ํ ๋ฐ์ดํฐ์
์์
์ ์ฃผ๋ก ์ฌ์ฉ - DELETE : ํด๋น ๋ฆฌ์์ค์ ๋ํ ๋ฐ์ดํฐ์
์ญ์
์ ์ฃผ๋ก ์ฌ์ฉ
-
REST API
โฐ REST :
REST(Representational State Transfer)
๋ ๋คํธ์ํฌ ์์์ ์ ์ํ๊ณ ์์์ ๋ํ ์ฃผ์๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ,HTTP ๊ธฐ๋ฐ
์ผ๋ก ํ์ํ ์์์ ์ ๊ทผํ๋ ๋ฐฉ์์ ์ ํด๋์ ์ํคํ ์ณ๐ ํ์(Verb): HTTP Method
๐ ์์(Resource): URI
๐ ๋ฉ์์ง(Representation): { โageโ: 35 } ๊ณผ ๊ฐ์ JSON ํฌ๋งท์ ๋ฐ์ดํฐ~>
URI
๋ก ์ ๊ทผ~> ์ ๋ฌ ๋ฐฉ์์ผ๋ก
HTTP Method
๋ฅผ ์ฌ์ฉํ์ฌ์กฐํ/์ฝ์ /๊ฐฑ์ /์ญ์
์ํ -
JSON(JavaScript Object Notation)
โฐ ์ฃผ๊ณ ๋ฐ์ ์ ์๋ ์๋ฃํ์ Int, String, Boolean, Array, Object
โฐ
Key
์Value
๋ก ์ด๋ฃจ์ด์ ธ ์์ผ๋ฉฐ ๊ฐ์ฒด๋์ค๊ดํธ {}
, ๋ฐฐ์ด์๋๊ดํธ []
๋ก ๊ฐ์ผ๋ค.
๐ฅ ํต์ ์ ์ํด์
Alamofire
๋ฅผpod install
โ๏ธ ํต์ ์ค์ต์ ์ํ ์ค์
-
Info.plist
-
Information Property List โ '+' button โ App Transport Security Settings โ '+' button โ Allow Arbitrary Loads โ 'Yes'
-
API ๋ฌธ์ ํตํด์ URL ์ฃผ์ ํ์ธ
-
APIConstants.swift
โ API ์ฃผ์ ๋ชจ์๋๋ ๊ณณstruct APIConstants { static let baseURL = "http://15.164.83.210:3000" static let usersSignInURL = baseURL + "/users/signin" static let usersSignUpURL = baseURL + "/users/signup" }
-
GenericResponse.swift
โCodable
: ๋ฐ์ดํฐ๋ฅผ JSON ๋ฐ์ดํฐ ํฌ๋งท์ผ๋ก ์์ ๋กญ๊ฒDecoding
,Encoding
ํ ์ ์๋๋ก ํด์ฃผ๋ protocolstruct GenericResponse<T: Codable>: Codable { var status: Int var success: Bool var message: String var data: T? // JSON ๋ฐ์ดํฐ์ ํค ์ swift struct์ ๋ณ์๋ฅผ ๋งตํํ๋ ์ญํ enum CodingKeys: String, CodingKey { case status = "status" case success = "success" case message = "message" case data = "data" } // data์ ๋ํ ํค ๊ฐ์ด ์๋ ๊ฒฝ์ฐ์ ์๋ ๊ฒฝ์ฐ๋ฅผ ๋ชจ๋ ์ฒ๋ฆฌํ๋ ์ญํ init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) status = (try? values.decode(Int.self, forKey: .status)) ?? -1 success = (try? values.decode(Bool.self, forKey: .success)) ?? false message = (try? values.decode(String.self, forKey: .message)) ?? "" data = (try? values.decode(T.self, forKey: .data)) ?? nil } }
-
data๋ฅผ ํตํด ๋ค์ด์ฌ ํ์ ๋ ๋ง๋ค์ด ์ฃผ๊ธฐ โ
SignInData.swift
-
https://quicktype.io/ :
JSON ๋ฐ์ดํฐ ํ์
์ ๊ฐํธํ๊ฒswift์ struct
๋ก ๋ณํํด์ฃผ๋ ์ฌ์ดํธ -
๋ฐ์ดํฐ ๋ฐ๊ธฐ๋ฅผ ์ฑ๊ณตํ์ ๋ ๋ฐ์์ฌ ํ์ ์์ฑ(SignInData.swift)
struct SignInData: Codable { var email, password, userName: String }
-
NetworkResult.swift
โ ์๋ฒ ํต์ ์ ๋ฐ๋ฅธ ๊ฒฐ๊ณผ// ์๋ฒ ํต์ ์ ๋ํ ๊ฒฐ๊ณผ(์ฑ๊ณต, ์์ฒญ์๋ฌ, ๊ฒฝ๋ก์๋ฌ, ์๋ฒ๋ด๋ถ์๋ฌ, ๋คํธ์ํฌ ์ฐ๊ฒฐ ์คํจ) enum NetworkResult<T> { case success(T) case requestErr(T) case pathErr case serverErr case networkFail }
-
AuthService.swift
โ๋ก๊ทธ์ธ ์๋ฒ ํต์
๊ตฌํ์ ์ํ ๊ตฌ์กฐ์ฒดimport Foundation import Alamofire struct AuthService { // ์ฑ๊ธํค ๊ฐ์ฒด๋ก ์ฑ ์ด๋์๋ ์ ๊ทผ ๊ฐ๋ฅ static let shared = AuthService() func signIn(email: String, password: String, completion: @escaping (NetworkResult<Any>) -> (Void)) { // ํต์ url, ์์ฒญ ํค๋, ์์ฒญ ๋ฐ๋ let url = APIConstants.usersSignInURL let header: HTTPHeaders = [ "Content-Type":"application/json" ] let body: Parameters = [ "email": email, "password":password ] // ์ํ๋ ํ์์ HTTP Request ์์ฑ let dataRequest = AF.request(url, method: .post, parameters: body, encoding: JSONEncoding.default, headers: header) // ๋ฐ์ดํฐ ํต์ ์์ ๋ฐ์ดํฐ ํต์ ๊ฒฐ๊ณผ dataRequest.responseData { (response) in // ํต์ ์ ์ฑ๊ณต์คํจ์ ๋ฐ๋ฅธ ๋ถ๊ธฐ์ฒ๋ฆฌ switch response.result { case .success: // ํต์ ์ ๊ฒฐ๊ณผ์ ๋ฐ๋ผ statusCode์ value ๊ฐ์ ๊ฐ์ง๊ฒ ๋จ guard let statusCode = response.response?.statusCode else { return } guard let data = response.value else { return } // Completion์ด๋ ํด๋ก์ ธ์๊ฒ ์ ๋ฌํ ๋ฐ์ดํฐ๋ฅผ judgeSignInData๋ผ๋ ํจ์๋ฅผ ํตํด ๊ฒฐ์ completion(judgeSignInData(status: statusCode, data: data)) case .failure(let err): print(err) completion(.networkFail) } } } // statusCode์ decode ๊ฒฐ๊ณผ์ ๋ฐ๋ผ NetworkResult๋ฅผ ๋ฐํ์์ผ์ค๋ค. private func judgeSignInData(status: Int, data: Data) -> NetworkResult<Any> { // ํต์ ์ ํตํด ์ ๋ฌ๋ฐ์ ๋ฐ์ดํฐ๋ฅผ decode let decoder = JSONDecoder() guard let decodedData = try? decoder.decode(GenericResponse<SignInData>.self, from: data) else { return .pathErr } // statusCode๋ฅผ ํตํด ํต์ ๊ฒฐ๊ณผ๋ฅผ ์ ์ ์๋ค. switch status { // ์ฑ๊ณต์ ์ผ๋ก ํต์ ์ ์ฑ๊ณต, ์ฑ๊ณตํ๋ค๋ ๊ฒฐ๊ณผ์ ํจ๊ป decodeํ data๊ฐ๋ ์ ๋ฌํด ์ค๋ค. case 200: return .success(decodedData.data) // ํต์ ์๋ ์ฑ๊ณตํ์ง๋ง, ์์ฒญ๊ฐ์ ๋ํ ์ค๋ฅ ์ฒ๋ฆฌ ์ค๋ฅ ๊ฒฐ๊ณผ์ ํจ๊ป ์ค๋ฅ ๋ฉ์ธ์ง๋ฅผ ์ ๋ฌํ๋ค. case 400..<500: return .requestErr(decodedData.message) // Server ๊ฐ๋ฐ์๊ฐ ์ง์ ํ Server์ ์ ์๋ฌ ์ฝ๋ ์๋ฌ ๊ฒฐ๊ณผ๋ง์ ๋ณด๋ด์ค๋ค. case 500: return .serverErr default: return .networkFail } } }
1๏ธโฃ ํ์๊ฐ์ ํต์ ํ๊ธฐ
-
APIConstants.swift
โ signUp URL ๋ฃ์ด์ฃผ์ -
AuthService.swift
โ signUp ํจ์๋ฅผ ๋ฃ์ด์ฃผ์(userName๋ฅผ ๋ฐ๊ธฐ ๋๋ฌธ์ userNameํญ๋ชฉ ์ถ๊ฐ)func signUp(email: String, password: String, userName: String, completion: @escaping (NetworkResult<Any>) -> (Void)) { let url = APIConstants.usersSignUpURL let header: HTTPHeaders = [ "Content-Type":"application/json" ] let body: Parameters = [ "email": email, "password": password, "userName": userName ] let dataRequest = AF.request(url, method: .post, parameters: body, encoding: JSONEncoding.default, headers: header) dataRequest.responseData { (response) in switch response.result { case .success: guard let statusCode = response.response?.statusCode else { return } guard let data = response.value else { return } completion(judgeSigninData(status: statusCode, data: data)) case .failure(let err): print(err) completion(.networkFail) } } }
-
ํ์ ๊ฐ์ ํ๊ธฐ ๋ฒํผ์ ๋๋ ์ ๋ โ ํต์ ์ด ์ด๋ค์ง๋๋ก ํ๊ธฐ
guard let emailText = emailTextField.text, let password = passwordTextField.text, let name = userNameTextField.text else { return } AuthService.shared.signUp(email: emailText, password: password, userName: name) { (networkResult) in switch networkResult { case .success(let data): if let data = data as? SignInData { self.simpleAlert(title: "ํ์ ๊ฐ์ ์ฑ๊ณต", message: "\(data.userName)๋ ํ์ ๊ฐ์ ์ฑ๊ณต!") UserDefaults.standard.set(data.userName, forKey: "userName") self.presentingViewController?.dismiss(animated: true, completion: nil) } print("success") case .requestErr(let msg): if let message = msg as? String { self.simpleAlert(title: "ํ์ ๊ฐ์ ์คํจ", message: message) } print("requestErr") case .pathErr: print("pathErr") case .serverErr: print("serverErr") case .networkFail: print("networkFail") } }
โ
simpleAlert
์ฌ์ฉํด์ ๋ฉ์์ง ์ฐฝ์ด ๋์์ง ์ ์๋๋ก ํ์
- POST : ๋ฐ์ดํฐ๋ฅผ
-
โถ๏ธ TextField
๋ฅผ ๋๋ฌ์ ํค๋ณด๋๊ฐ ๋์ค๋ฉด ์ ๋๋ฉ์ด์ ์ ์ฌ๋ ธ๋ค๊ฐreturn
๋ฅผ ๋๋ฅด๋ฉด ์ ๋๋ฉ์ด์ ์ ๋ด๋ ค๋ผโ textfield๋ฅผ ๋๋ฅด๋ ๊ฒ๊ณผ return ํค๋ฅผ ๋๋ฅด๋ ๊ฒ ๋ชจ๋
UITextFieldDelegate
๋ฅผ ์ฌ์ฉํด์ ๊ตฌํํ์!extension LoginViewController: UITextFieldDelegate { func textFieldDidBeginEditing(_ textField: UITextField) { let move = CGAffineTransform(translationX: 0, y: -50) UIView.animate(withDuration: 0.2, animations: { self.profileImage.transform = move self.partLabel.transform = move self.partTextField.transform = move self.nameLabel.transform = move self.nameTextField.transform = move }) } func textFieldShouldReturn(_ textField: UITextField) -> Bool { UIView.animate(withDuration: 0.2, animations: { self.profileImage.transform = .identity self.partLabel.transform = .identity self.partTextField.transform = .identity self.nameLabel.transform = .identity self.nameTextField.transform = .identity }) textField.resignFirstResponder() return true } }
- UITextFieldDelegate์ ์๋
textFieldDidBeginEditing
,textFieldShouldReturn
๋ฅผ ์ฌ์ฉ- textFieldDidBeginEditing : textField ์์ ์ด ์์๋ ๋ ๋์ โ textField๋ฅผ ๋๋ฅด๋ฉด ๋์
- textFieldShouldReturn : textField์ returnํค๋ฅผ ๋๋ ์ ๋ ๋์
CGAffineTransform
์translaionX
๋ฅผ ์ฌ์ฉํด์ y์ขํ๋ฅผ 50 ์ฌ๋ฆฌ์ โ animateํจ์์ ๋ฃ์ด์ฃผ๋ฉด ์ด๋ฏธ์ง๊ฐ ์ฌ๋ผ๊ฐ๋ ๋์์ด ์์ฑ- ๋ค์ ๋ด๋ ค์ค๋ ๋์์
.identity
๋ฅผ ์ฌ์ฉํด์ ์๋ ์๋ฆฌ๋ก ๋์์ค๊ฒ ํด์ฃผ์ resignFirstResponder
๋ ํค๋ณด๋๊ฐ ์๋์ผ๋ก ๋ด๋ ค๊ฐ๋๋ก ๋์ํจ
- UITextFieldDelegate์ ์๋
-
๋์ ๊ณผ์
โถ๏ธ ์คํฌ๋กค์ ํ ๊ฒฝ์ฐ ๊ณ ์ ํค๋๊ฐ ์ฌ๋ผ์ก๋ค๊ฐ ๋ฉ์ถ๋ฉด ๊ณ ์ ํค๋๊ฐ ๋ํ๋๊ฒ ํ์ โ๏ธโ
UIScrollViewDelegate
๋ฅผ ์ฌ์ฉํด์ ํค๋์ ์์ง์์ ๊ตฌํํ์!extension ViewController: UIScrollViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { UIView.animate(withDuration: 0.3, animations: { if self.isTop == false { self.headerView.transform = CGAffineTransform(translationX: 0, y: -88) } if scrollView.contentOffset.y > 0 { self.isTop = false self.scrollView.frame.size.height = UIScreen.main.bounds.size.height self.scrollView.transform = CGAffineTransform(translationX: 0, y: -88) } else { self.isTop = true } }) } func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { UIView.animate(withDuration: 0.3, animations: { if scrollView.contentOffset.y >= 0 { self.headerView.transform = .identity self.scrollView.transform = .identity } }) } }
-
UIScrollViewDelegate์ ์๋
scrollViewDidScroll
๊ณผscrollViewDidEndDecelerating
๋ฅผ ์ฌ์ฉ-
scrollViewDidScroll : scrollView๊ฐ scroll๋๋ฉด ๋์
-
scrollViewDidEndDecelerating : scrollView๊ฐ scroll๋๋ค๊ฐ scroll๋๋ ์๋๊ฐ ๊ฐ์๋๋ฉด ๋์
โ ์ด๊ฑด ๊ทธ๋ฅ ๋ฉ์ถ๋ฉด ํค๋ ๋ํ๋๋ ๋์์ด ์ ๋ํ๋ ์๋ ์๋ค
-
-
ํค๋์ ์์ง์์ ์ผ๋ฐ ๊ณผ์ ์ ๋์ผํ๊ฒ
CGAffineTransform
์.identity
๋ฅผ ์ฌ์ฉ -
์์ง์ด๋ ์กฐ๊ฑด์ scrollView
y์ขํ์ contentOffset
์ด 0๋ณด๋ค ํฌ๋ฉด ์ ๋๋ฉ์ด์ ์ด ์๋ํ๋๋ก ํ๋คโ ์๋จ์์ ์คํฌ๋กคํ๋ฉด ์์ง์ด์ง ์๋๋ก, ๊ทธ ์ธ์ ๋ถ๋ถ์์๋ ์ ๋๋ฉ์ด์ ์๋
-
isTop
์ด๋ผ๋ boolean๊ฐ์ ์ฌ์ฉํด์headerView
๊ฐ ์๋จ์์ ์คํฌ๋กคํ ๋ ์์ง์ด์ง ์๋๋ก ํ๋ค
๐ฅ
headerView
์scrollView
๋ฅผ ๋ค๋ฅธ ์กฐ๊ฑด์ ๋ฃ์ ์ด์โ ๊ฐ์ ์กฐ๊ฑด(scrollView.contentOffset.y > 0)์ ๋ฃ์๋๋ ์๋จ์์ ์คํฌ๋กค ํ๊ณ ๋์ ๋ View ์ฌ์ด์ โญ๏ธ๋น ํโญ๏ธ์ด ์๊น
๐ฅ
self.scrollView.frame.size.height = UIScreen.main.bounds.size.height
์ ๋งค๋ฒ ํด์ฃผ๋ ์ด์โ scrollView์
frame
์ ์ ๋๋ฉ์ด์ ์ ์ํด์translationX์ y๊ฐ
์ ๋ ์๋ก ์ฌ๋ผ๊ฐ ์๋ค. ๋ฐ๋ผ์ ์ฐ๋ฆฌ๋ tabBar์ scrollView์ฌ์ด์ โญ๏ธํโญ๏ธ์ ๋ฐ๊ฒฌํ ์ ์๋ค. ์ด ํ์ ๋ฉ์ฐ๊ธฐ ์ํด์scrollView์ frame height
๋ฅผUIScreen์ height
์ฌ์ด์ฆ๋ก ์ง์ ํด์ค์ผ ํ๋ค. -
-
๊ณ ์ View
๋ 2์ฃผ์ฐจ ๊ณผ์ ์ฒ๋ผScrollView
์ ๋ถ๋ฆฌํด์ ๋ฐ๋กView
๋ฅผ ๋ง๋ค์๋ค. ๊ทธ๋ผ ์คํฌ๋กคํ ๋ ํจ๊ป ์ฌ๋ผ๊ฐ์ง ์๋๋ค. -
์๋ฌด๊ฒ๋ ๋ชจ๋ฅด๊ณ
CollectionView
๋ฅผ ์ฌ์ฉํด์ banner์ profile ํ๋ฉด์ ๋ชจ๋ ๋ง๋ค๋ ค๊ณ ํ์ง๋ง ์คํจํ๋ค๐ -
๋จผ์
ScrollView
๋ฅผ ๋ง๋ค๊ณ ๊ทธ ์์ImageView
์CollectionView
๋ฅผ ์น๊ธฐ๋ก ๋ง์ ๋จน์ ๋ค์ ์๋โ๏ธ -
๊ฒฐ๊ณผ์ ์ผ๋ก ์์ ํ๋ฉด์ฒ๋ผ ๋์๋ค ์ง ๐
โ ์ ์ํ๋ก ์คํ์ ํ๋ฉด
CollectionView
๋ ์คํฌ๋กค๋๊ณScrollView
๋ ์คํฌ๋กค๋๋ ์ ๋ช ๋๋ ์ํฉ์ด ๋ฐ์ํ๊ธฐ์ ๐๊ผญCollectionView
์์View>Interaction>Multiple Touch
๋๋ฌ์ฃผ๊ธฐ ๐
โ ProfileViewCell
func setCell(info: Profile) {
profileImage.image = UIImage(named: info.imageName)
profileName.text = info.imageName
profileLabel.text = info.statusMessage
}
cell
์setCell
์ด๋ผ๋ ํจ์๋ฅผ ๋ง๋ค์ด๋๊ณ Profile Data๋ฅผ ๋ฐ์์ ํ๋กํ ์ด๋ฏธ์ง, ํ๋กํ ์ด๋ฆ, ํ๋กํ ์ํ ๋ผ๋ฒจ์ ์ค์ ํ ์ ์๊ฒ ํ๋ค.
โ ViewController(UICollectionViewDataSource)
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return profile.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = profileCollectionView.dequeueReusableCell(withReuseIdentifier: ProfileViewCell.identifier, for: indexPath) as? ProfileViewCell else {
return UICollectionViewCell()
}
cell.setCell(info: profile[indexPath.item])
// ProfileViewCell ์์ ์ค์ ํ๊ธฐ
return cell
}
}
cell
์ด๋ผ๋ ๋ณ์์ProfileViewCell
๋ฅผ ๋ฐ์์์ setCellํจ์๋ฅผ ๋ถ๋ฅธ๋ค.- profile์ด๋ผ๋ ๋ฐ์ดํฐ์ ํด๋น indexPath์ item๋ฅผ ๋ฐ์์จ๋ค โ
TableView
๋ indexPath์ row โ๏ธโ๏ธ
โ ViewController(UICollectionViewDelegateFlowLayout)
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 150, height: 225)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 27
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 41, left: 20, bottom: 0, right: 20)
}
}
TableView
์๋ ๋ค๋ฅด๊ฒCollectionView
๋ ํด๋น ์ ๋ค์ ์์น๋ฅผFlowLayout
๋ฅผ ํตํด์ ์ก์์ค ์ ์๋ค.
- ์ฒซ ๋ฒ์งธ ํจ์(sizeForItemAt) : ํด๋น cell์ ํฌ๊ธฐ ์ก๊ธฐ
- ๋ ๋ฒ์งธ ํจ์(minimumLineSpacingForSectionAt) : ๊ฐ๊ฐ ์ ๋ค ์,์๋์ Space
- ์ธ ๋ฒ์งธ ํจ์(minimumInteritemSpacingForSectionAt) : ๊ฐ๊ฐ ์ ๋ค ์ข,์ฐ์ Space
- ๋ค ๋ฒ์งธ ํจ์(insetForSectionAt) : CollectionView ContentInset ์ง์
-
โ ViewController
class ViewController: UIViewController, UIScrollViewDelegate { @IBOutlet weak var scrollView: UIScrollView! @IBOutlet weak var topButton: UIButton! override func viewDidLoad() { super.viewDidLoad() scrollView.delegate = self } func scrollViewDidScroll(_ scrollView: UIScrollView) { if(scrollView.contentOffset.y > (scrollView.contentSize.height - scrollView.frame.size.height) / 2) { topButton.isHidden = false } else { topButton.isHidden = true } } }
scrollView
๋ฅผscrollViewDidScroll
์์ ์ฌ์ฉํ๊ธฐ ์ํด์UIScrollViewDelegate
๋ฅผ Delegateํ๋ค.- ์ฌ์ฉํ
scrollView
์ ์ฒ์ view๊ฐ ์์ฑ๋ ๋ delegateํ๋ค. scrollView
๊ฐ ์์ง์ผ ๋ content offset์ด ๋ณ๊ฒฝ๋๋ ๋ฐ ๊ทธ offset์ y๋ถ๋ถ์scrollView.bounds.origin.y
์ ๊ฐ๋ค.- ํด๋น content offset์ด ์ ์ฒด ์คํฌ๋กค๋ทฐ์ ๋ฐ ์ด์์ผ๋ก ๋ด๋ ค์ค๋ฉด topButton์ด ๋ณด์ฌ์ง๊ณ ์๋๋ฉด ๋ณด์ด์ง ์๊ฒ ํ๋ค.
@IBAction func touchUpTop(_ sender: Any) { topButton.isHidden = true scrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: true) }
topButton
๋ฅผ ๋๋ฅด๋ฉด ๋ณด์ด์ง ์๊ฒ ํ๋ค. top ๋ถ๋ถ์ ๋๋ฌํ์ ๋๋ ๋ฒํผ์ด ๋ณด์ด์ง ์๊ธฐ ๋๋ฌธ์ด๋ค.setContentOffset
์UIScrollView
์์ ํน์ ์์น๋ก scrollํ ๋ ์ฌ์ฉ๋๋ค.x์ถ
์ผ๋ก๋ ์คํฌ๋กคํ ํ์๊ฐ ์๊ธฐ ๋๋ฌธ์ 0์ผ๋ก ํด์ฃผ๊ณ ๋งจ ์๋ก ์ด๋ํ ๊ฒ์ด๊ธฐ์y์ถ
๋ 0์ผ๋ก ๋ง์ถฐ์ค๋ค.
-
โ SecondViewController
@IBAction func touchUpDismiss(_ sender: Any) { guard let dvc = self.presentingViewController as? ViewController else { return } dvc.editLabels(part: partTextField.text ?? " ", name: nameTextField.text ?? " ") dismiss(animated: true, completion: nil) }
โ ViewController
func editLabels(part: String, name: String) { partLabel.text = part statusLabel.text = "\(name) ๋ ์๋ ํ์ธ์ ~~ ๐ฅฐ" }
SecondViewController
์์๋ก๊ทธ์ธ
๋ฒํผ์ ๋๋ฅด๋ ๊ฒฝ์ฐ,ViewController
๊ฐ presenting ๋๋ฉด์ViewController
์editLabels
๋ผ๋ ํจ์๋ฅผ call ํ๋ค.- TextField.text์ ๊ฐ์ด
editLabels
์part
์name
์ผ๋ก ์ ๋ฌ๋๋ค. ViewController
์partLabel.text
๊ฐ๊ณผstatusLabel.text
๋ก ๋ฃ์ด์ ธ์ViewController
๋กdismiss
๋์ ๋ label์ ๊ฐ์ด ๋ชจ๋ ๋ณ๊ฒฝ๋๋ค.
-
โ ViewController
@objc func touchUpPresent() { let loginVc = LoginViewController() let vc = UINavigationController(rootViewController: loginVc) vc.modalPresentationStyle = .fullScreen loginVc.editLabelText = { part, name in self.partNameLabel.text = part self.introduceLabel.text = "\(name) ๋ ์๋ ํ์ธ์!!!๐" } present(vc, animated: true, completion: nil) }
โ LoginViewController
var editLabelText: ((String, String) -> ())? @objc func touchUpLogin() { editLabelText?(partTextField.text ?? " ", nameTextField.text ?? " ") dismiss(animated: true, completion: nil) }
ViewController
์์ ๋ฐ๋กLoginViewController
๋ฅผ ์ฌ์ฉํ๋ฉดNavigationController
๊ฐ present๋์ง ์๊ณNavigationController
๋ง presentํ๋ฉด ๋นNavigationController
๊ฐ present๋๊ธฐ ๋๋ฌธ์UINavigationController
์rootViewController
๋ฅผLoginViewController
๋ก ์ ํด์ฃผ์ดLoginViewController
๊ฐ present๋ ์ ์๊ฒ ํ์๋ค.LoginViewController
์์editLabelText
๋ผ๋ closure๋ฅผ ์ ์ธํด์ViewController
๋ก TextField.Text๊ฐ์ ์ ๋ฌํด์คฌ๋ค.