-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[FEAT] #1 - iOS 1차 과제 #2
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
진짜 왤케 잘하시냐고요 ~~~ 이러시면 곤란 ~~~~
|
||
class Signup1ViewController: UIViewController { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Class 네이밍할때 보통 숫자를 안쓰는 것 같아요! 지금은 과제에 회원가입 1 , 회원가입 2 이런식으로 되어있어서 고러케 하신거 같네욜 ~~
저도 잘은 모르지만 조금 더 직관적이 네이밍이 필요해보입니다 ! 예를 들어, 첫번째 뷰컨에서는 사용자이름을 받고 있으니 MakeUserNameViewController나 EnterUserName.. AddUserNameViewController.. 이런식으로요 ! 🏄🏻♀️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
클래스 네이밍... 쉽지 않네요... 기깔나는 네이밍으로 수정 후 돌아오겠습니다... 피드백 감사합니다 ~~ 🤸🏻♀️
super.viewDidLoad() | ||
|
||
setTextFieldOption() | ||
// 다음 버튼 비활성화 | ||
nextButton.isEnabled = false | ||
addActionToTextField() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
버튼 비활성화 하는 부분도 함수 안에 넣어주시면 좋을 것 같습니다 ~ 보통 viewDIdLoad() 안에는 메서드들만 넣어주는 걸로 알고 있어요 !!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
수연님 조언에 조금 덧붙이자면 생명주기 함수 안에 코드를 넣으면 안된다!는 아니지만 로직에 대한 코드를 함수화해서 viewDidLoad()
와 같은 생명주기 함수 내에는 메서드 호출 코드만 순서대로 적어주면 코드 가독성이 훨씬 높아지기 때문에 협업 시에 컨벤션으로 보통 기본적으로 가져가는 것 같습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
계속 고민하던 부분이었는데 함수 안에 넣는게 가독성이 더 좋을 것 같네요! 수정하겠습니다 ~~
|
||
// 현재 id 또는 password 값이 있는지 확인 | ||
if id?.isEmpty == true || password?.isEmpty == true{ | ||
self.signinButton.isEnabled = false | ||
} else { | ||
self.signinButton.isEnabled = true | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.isEmpty 같은 경우, text가 비어있다면 기본으로 true를 리턴합니다 ! 그래서 == true는 안적어도 똑같답니다!
if id?.isEmpty || password?.isEmpty {
self.signinButton.isEnabled = false
} else {
self.signinButton.isEnabled = true
}
요러케만 해줘도 됩니다 ! 그리고, false를 판별하고 싶을땐 앞에 !를 붙여주면됩니다
!id?.isEmpty
=> 이런식으로요 ! 그러면 false 값을 return 합니다 !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오 !!! 감사합니다 !!!!!!!!! 🫶🏻
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
은주님 피드백 : 위의 경우에 id랑 password 값이 옵셔널이라 default 값을 명시해주는 게 필요합니다 ( 요렇게 : id?.isEmpty ?? true
) 이 경우에는 비교 연산자를 쓰는것과 안쓰는 것의 효율성 차이가 크지 않은 것 같다고 말해주셨숩니다 ! 혹시 다른분들이 보시다가 궁금해 하실 거 같아서 남깁니다 !
갠적으로 이 로직에서는 저는 text에 접근하지 않고 텍스트 필드에 바로 접근할 수 있는 .hasText 속성을 이용해 보는것도 좋을 것 같네요 ! ( 이렇게 하라는 건 아니고 제가 한 방식을 말씀드린 거에여 )
.hasText는 텍스트 필드가 text를 갖고 있는 경우 true, 없는경우 false를 리턴합니다
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@objc
private func textFieldDidChange(_ sender: UITextField) {
/// 도전과제 (2)
loginButton.isEnabled = (emailTextField.hasText && passwordTextField.hasText) ? true : false
}
저는 이런식으로 했습니당 !! + 삼항연산자도 취향껏 사용 ..!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.hasText 속성을 사용하면 훨씬 깔끔해질 것 같네요 👍🏻 찾아보겠습니다!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
옵셔널 타입에 대해서 공부하다가 TextField의 text 속성은 옵셔널이지만 nil을 반환하지 않는다는 점을 알게되었는데요,
그럼 강제 언래핑을 사용해도 괜찮은가요?!
if idTextField.text!.isEmpty || passwordTextField.text!.isEmpty {
self.signinButton.isEnabled = false
} else {
self.signinButton.isEnabled = true
}
아니면 강제 언래핑이기 때문에 최대한 사용하지 않는 것이 좋을까요?!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저도 놓치고 있었던 부분인데요 ! 찾아보니까 textfield의 default 가 빈 문자열이고 nil을 넣어도 빈 문자열을 출력하더라구요..??? 어매매.. 저도 몰랐어요 사실.. 허허 근데 nil도 아닌데 왜 옵셔널인가 봤더니 Objective-C와의 호환성 때문이라고 해요 !
Objective-C에서는 textField의 text에 nil을 할당할 수 있었기 때문이라고 하는데, 그래서 강제 언래핑에 대해서도 좀 더 찾아봤습니다 !
강제 언래핑은 옵셔널 변수가 nil이든 말든 일단 언래핑을 시도하기 때문에 혹시라도 nil 값이 생기게 되면 에러가 나기 때문에 지양하는데요 ! nil 값에 접근하려고 하면 당연 오류가 나겠죠 ? 컴파일러는 이를 잡지 못하기 때문에 겉으로 보기에는 오류가 없어보이지만 이런경우에 100프로 런타임에러가 발생합니다!
스위프트 언어 가이드에 보면
느낌표(!)를 사용하여 값이 존재하지 않는 옵셔널 값에 접근하려 시도하면
런타임 에러가 발생합니다.
느낌표를 사용하여 강제 언랩핑을 하기 전에는
항상 옵셔널 값이 nil이 아니라는 것을 확실히 해야 합니다.
이렇게 되어있네요.. 저의 갠적인 생각으로 textField의 text 속성은 항상 옵셔널 값이 nil 이 아니라는 것이 확실하기 때문에 강제 언래핑을 해도 된다고는 생각합니다 !!
Optional인 textField.text와 달리 textView.text는 강제 언래핑한 형태로 되어있더라구요 ! 내부적으로 처리해서 nil이 아닌 값을 return 해주는거 같은데, 그렇다면 textfield의 text 도 강제 언래핑을 사용해도 될거 같다는 생각입니다.. 혹시 지나가다 이 내용에 대해.. 잘 알고 계신 분은 코멘트 남겨주심 감사하겠습니다.. !!! ~~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- 혹시나 해서 @objc 함수 내부에서 textfield 의 text 속성에 nil 값을 넣어봤는데 , 역시나 빈 문자열을 출력하더라구요 ! swift에서는 무조건 빈문자열로 출력되는것 같습니당 ~~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Suyeon9911 @wngus4296
지나가다가 조금 적어보자면..
일단 결론적으로 이 경우 저는 강제 언래핑을 사용해도 문제는 없다! 고 생각합니다. (저도 최대한 강제 언래핑을 지양하는 것이 좋다고 생각은 하지만요ㅎ)
왜냐면 수연님이 말씀해주신 것 처럼 textField의 text 속성은 항상 옵셔널 값이 nil 이 아니라는 것이 확실하기 때문에 (nil일 일이 절대 없기 때문에) 써도 상관없다고 생각해요!
그런데 강제 언래핑..아무래도 찝찝하다...두발 뻗고 잘 수가 없다..하신다면
extension UITextField {
/// UITextField의 상태를 리턴함
var isEmpty: Bool {
if text?.isEmpty ?? true {
return true
}
return false
}
}
이렇게 UITextField
의 extension에다 UITextField의 상태를 Bool값으로 리턴하는 코드를 만들어두고
if idTextField.isEmpty || passwordTextField.isEmpty {
self.signinButton.isEnabled = false
} else {
self.signinButton.isEnabled = true
}
이렇게 써주는 방법도 있을 것 같습니다! 이러면 옵셔널 처리를 매번 해줘도 되지 않기 때문에 코드가 더 깔끔해진다는 장점도 있을 것 같습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
와 감사합니다 역시 오비는 남다르네요..?... 🫶🏻
vscode 타자 치면 불 나오는 extension만 사용해봤던 저... extension을 이렇게 사용할 수 있다니 유용한 정보 얻어갑니다.. 🔥
import UIKit | ||
|
||
class ViewController: UIViewController { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요거는 별건 아닌데 구동영상보니까 로그인 완료 화면이 dismiss된 후에 이전에 로그인 화면에서 textField에 입력해준 값들이 남아있더라구! 완료화면에서 다시 로그인 화면으로 돌아왔을 때 값들이 남아있지 않도록 초기화시켜준다면 조금 더 좋은 뷰가 될 것 같아서 도움될만한 코드 남기고 갈게 참고만 해줘!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
resetTextField()
}
/// textField 초기화하는 메서드
private func resetTextField() {
idTextField.text?.removeAll()
pwTextField.text?.removeAll()
}
위와 같이 텍스트필드를 초기화시키는 함수를 만들어주고, 요 함수를 viewWillAppear()
라는 생명주기 함수에서 호출해주면 돼!
viewWillAppear()
는 언제 호출되는지 생명주기 함수에 대해 더 찾아보면 도움이 될 것 같구, 이해가 안가는 부분이 있다면 다시 물어봐줘도 돼~!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이거 진짜 필요했던 건데.. 생명주기까지 .. 제인초이님 감사합니다 바로 적용해볼게요 💙
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
심화과제까지 완전 잘했다~!~!
코드의 가독성을 위해 마크구문도 써보고, 파일명도 더 직관적인 네이밍에 대해 고민해보면 더더 좋을 것 같아!!
수고했어🔥👍🏻
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
우왕 주현님 넘나 잘하세요..
코-베로 짜는법 공부하고 싶었는데 감사합니다 !!
많이 배우고 갑니다 👍
self.userIdTextField.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged) | ||
} | ||
|
||
@objc func textFieldDidChange(sender: UITextField) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
질문 있습니다 !! @objc 는 키보드 입력으로 실시간으로 바뀐걸 반응하려고 넣는건가요..? 꼭 넣어야하는건가요...? 코베 어려워요 . . . 🥲
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저도 궁금합니다 !!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
부족하지만,, 아는 선에서 최대한 자세히 설명해보겠습니다.. 하하
먼저 @objc는 Objective-C 약칭인데요!
저는 TextField에 입력이 되면 버튼을 활성화 시키는 기능을 구현하기 위해서 이벤트 감지 메서드 addTarget()
을 사용해주었습니다.
만약 userIdTextField에 입력이 된다면 action에 보이는 #selector()에 적어준 함수가 실행됩니다.
제 코드로 보면 textFieldDidChange함수가 실행되겠죠?!
#selector가 Objective-C의 개념이기 때문에 실행시킬 함수(textFieldDidChange)에 @objc를 적어주는 겁니다!
저도 기능 구현할 때 찾아봤던 내용이라 도움 되었던 자료 공유하겠습니다 🔥
TextField 감지하는 버튼 만들기: https://kirkim.github.io/swift/2021/12/11/backspace.html
selector란?: https://woozzang.tistory.com/120
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
전체적으로 징짜 너무 깔끔하고 잘보고갑니다~!~! 고생하셧습니다
|
||
setWelcomeLabel() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
함수있는거 너무 좋아요~!
private func setButtonOption() { | ||
// 다음 버튼 비활성화 | ||
nextButton.isEnabled = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | ||
|
||
@objc func textFieldDidChange(sender: UITextField) { | ||
self.nextButton.isEnabled = sender.hasText ? true : false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sender.hasText자체가 true/false값이기 때문에
self.nextButton.isEnabled = sender.hasText
로만 써도 될 것 같아요!!
|
||
@IBAction func okButtonDidTap(_ sender: Any) { | ||
let presentingVC = self.presentingViewController! | ||
let navigationController = presentingVC is UINavigationController ? presentingVC as? UINavigationController : presentingVC.navigationController |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오왕 is는 한번도 안써봤는데 신기하네여!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
우오 저 is 가 들어간 구문은 어떻게 동작하는 건지 궁금해요~!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저두 알고싶습니다 🧐
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
시험기간에 집중안돼서 구경하다가 코멘트 달아용... 도움이 되면 좋겠습니다ㅎㅎ
@daminoworld @513sojin @wngus4296
let presentingVC = self.presentingViewController!
일단 위 코드에서는 이 뷰 컨트롤러를 띄워주고있는 VC를 가져오고 있네요! 강제언래핑을 쓰셨는데, 이 회원가입 완료 뷰컨트롤러를 띄워주는 VC가 있다는 것을 확신해서 !를 쓰실 수도 있지만, guard let 구문으로 안전하게 가져오는게 좋을 것 같아요! 강제언래핑을 남용하면 언젠가 nil 오류를 마주할 수도 있습니다...!
let navigationController = presentingVC is UINavigationController ? presentingVC as? UINavigationController : presentingVC.navigationController
그리고 여기에서는 가져온 presentingVC가 UINavigationController 타입인지 Is 연산자를 통해 확인합니다(링크 참조)
https://jinshine.github.io/2018/05/25/Swift/12.타입체크(TypeCheck)와%20타입캐스팅(Type%20Casing)/
회원가입 완료 VC로 오는 경로는 로그인 뷰컨에서 모달로 띄워지는 경우, 회원가입 뷰컨에서 모달로 띄워지는 경우 두가지를 삼항연산자를 통해 분기처리해주고 있네요.
로그인 뷰컨에서 모달로 띄워진 경우에는 presentingVC가 로그인 뷰컨트롤러이면서, 동시에 UINavigationController 타입이기도 하기 때문에 presentingVC 자체를 navigationController로 가져옵니다.
회원가입 뷰컨에서 모달로 띄워진 경우에는 회원가입 뷰컨이 UINavigationController가 아니기 때문에, 회원가입 뷰컨의 property로 가지고 있는 navigationController를 가져옵니다.
self.dismiss(animated: true){
navigationController?.popToRootViewController(animated: true)
return
}
그리고 이 부분에서 회원가입 완료 VC를 dismiss시키고 completion에서 navigationController도 RootVC로 pop시켜주고 있네요! 결과적으로는 두 케이스 모두 로그인VC로 돌아가게 되는 코드입니다!
일단 두 가지 경우를 모두를 고려하여 코드를 짜주신 점이 인상적이에요! is 연산자도 정말 못 보는 친구인데 저도 배워갑니다..ㅎㅎ
그런데 결론적으로 말하면 삼항연산자의 false 부분은 실행될 일이 없고, presentingVC.navigationController
가 실행되었다고 하더라도 popToRootViewController 메서드가 동작하지 않습니다! 이유는 아래와 같습니다.
로그인 뷰컨은 UINavigationController가 직접 연결되어서 UINavigationController 타입이고, 회원가입 뷰컨은 거기에 연결된 다른 뷰컨트롤러니까 UINavigationController가 아닐거라고 생각하고 분기처리를 하신 것 같은데, 두 경우 모두 presentingVC의 타입은 UINavigationController입니다.
네비게이션 컨트롤러에 연결되어 push형식으로 띄워진 뷰컨트롤러(회원가입 뷰컨 즉, 패스워드 입력해주는 뷰컨)를 self.presentingViewController로 접근하면 항상 UINavigationController 타입으로 가져와지기 때문이죠~!
실제로 presentingVC의 타입을 출력해 보면 항상 UINavigationController로 출력이 되고 있습니다.
그리고
presentingVC.navigationController?.popToRootViewController
위와 같이 프로퍼티에 접근하여 popToRootViewController 메서드를 호출하면 동작하지 않습니다! presentingVC가 UINavigationController 타입이기 때문에 navgationController 속성을 가지고 있기는 하지만 저장된 값은 없기 때문입니다(자기 자신이 UINavigationController이기 때문에 저장할 필요가 없으니까요)... 사실 이부분은 아리까리할 수 있지만 아래 링크 참조해주세요! UIViewController 타입일 때 navigationController 속성이 유의미해집니다!
https://gyuios.tistory.com/m/123
따라서 아래와 같이 고쳐볼 수 있겠네요!
guard let naviVC = self.presentingViewController as? UINavigationController else { return }
self.dismiss(animated: true) {
naviVC.popToRootViewController(animated: false)
}
guard let 구문으로 안전하게 nil 언래핑 해주고, UINavigationController로 다운캐스팅 해서 pop시켜줍니다!
이렇게 해도 분기처리하신 두 경우 모두 고려해줄 수 있어요!
if let userId = userId { | ||
welcomeLabel.text = "\(userId)님 Instagram에 오신 것을 환영합니다" | ||
welcomeLabel.sizeToFit() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
바인딩 넘좋아요~!
private func setButtonOption() { | ||
// 다음 버튼 비활성화 | ||
nextButton.isEnabled = false | ||
} | ||
|
||
private func setTextFieldOption() { | ||
// TextField에 입력하면 Clear 버튼이 나오도록 표시 | ||
passwordTextField.clearButtonMode = .whileEditing | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요부분도 스토리보드의 장점을 살린다면 스토리보드 인스펙터 창에서 직접 설정해줄 수도 있을 것같아요!!! 하지만 그저 취향차이니까 코드도 좋습니다~!~!!
|
||
import UIKit | ||
|
||
class AddNameToSignupViewController: UIViewController { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(담인님 리뷰에 썼던거 긁어왔어여..!! 더 이상 상속되지 않는다면 앞에 final 키워드를 붙이면 좋을 것 같습니다~!)
swift에서 더 이상 상속되지 않는 클래스에는 final 키워드를 붙여주면 훨씬 성능이 좋은 코드가 됩니다!!
클래스를 호출할때 스위프트에서는 Dynamic한 방법을 사용하는데 이게 성능을 떨어뜨리는 요소중 하나에요!
그래서 final을 붙여주면 Dynaimc -> Static 한 방식으로 바꿔준답니다,,
요건 링크를 첨부하면 좋을 것 같아서..몇가지 남기고 갑니다..
https://docs.swift.org/swift-book/LanguageGuide/Inheritance.html#ID202
https://velog.io/@ryan-son/Swift-final-%ED%82%A4%EC%9B%8C%EB%93%9C-%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B1%B8%EA%B9%8C
https://jellysong.tistory.com/122
그래서 지금 이 SignInViewController가 어디선가 상속될 클래스가 아니라면 final class SignInViewController: UIViewControleller { ~~ 로 적어주심 좋을 것 같아요!!
🌱 작업한 내용
기본, 도전, 심화 과제 완료
🌱 PR Point
📸 스크린샷
-
피드백 내용 수정 후
(로그인 화면으로 돌아올 때
텍스트필드 초기화)
📮 관련 이슈