-
201101 - TILTIL/2021 2020. 11. 2. 02:22
Swift Grammar
오늘 새롭게 공부한 부분은
- Selector
- Keypath String Expression
- Keypath Type
- Keypath Expression
- #available(OS version, *)
- MetaType
처음 공부한 개념이었지만 그럭저럭 이해가 갔지만 MetaType은 이해가 안 갔다..
나머지는 공식문서를 읽는데 많은 시간을 보냈다.
- UIDatePicker
- Date
- NumberFormatter
- String
기념일 계산기
UITabBarController를 이용하여 세 가지 화면으로 나눴다.
첫 번째 화면은 저번에 올렸고 두 번째 화면은 D-Day계산기 그리고 세 번째 화면은 살아온 날을 구하는 화면이다.
세 번째 화면은 Navigation Controller로 연결을 했는데 연결만 했고 활용하지는 않았다.
UIViewController+Alert.swift
import UIKit extension UIViewController { func presentAlert(message: String) { let alert = UIAlertController(title: "경고", message: message, preferredStyle: .alert) let alertAction = UIAlertAction(title: "확인", style: .default, handler: nil) alert.addAction(alertAction) present(alert, animated: true, completion: nil) } }
Alert은 매번 사용하는 것 그대로!
UIViewController+Date.swift
import UIKit extension UIViewController { static var currentCalendar: Calendar { return Calendar.current } var customDateFormatter: DateFormatter { let dateFormatter = DateFormatter() dateFormatter.dateStyle = .full dateFormatter.dateFormat = "yyyy-MM-dd" return dateFormatter } var koreanDateFormatter: DateFormatter { let dateFormatter = DateFormatter() dateFormatter.locale = Locale(identifier: "ko_kr") dateFormatter.dateStyle = .long return dateFormatter } } extension TimeInterval { func convertToDay() -> TimeInterval { return self / 60 / 60 / 24 } }
계속 사용해야 했던 Calendat.current는 Type Property로 선언하여 메모리 낭비를 줄여보려 했다.
DateFormmatter는 두 가지를 만들었는데 customDateFormatter에서 dateFormatter.dateStyle = .full 필요가 없는 코드이다.
아래에서 DateFormat을 다시 설정하므로 이 코드가 무시가 되는 듯하다.
무의식적으로 넣은 거 같은데 ㅠㅠ 반성하자..
TimeInterval이 second이므로 하루로 변환하기 위해 / 60 / 60 / 24로 했는데
지금 생각해보니 / (60 * 60 * 24)가 더 직관적이고 좋은 코드로 보인다!
UIViewController+NumberFormat.swift
import UIKit extension UIViewController { var customNumberFormatter: NumberFormatter { let numberFormatter = NumberFormatter() numberFormatter.locale = Locale(identifier: "ko_kr") numberFormatter.numberStyle = .decimal numberFormatter.roundingMode = .floor numberFormatter.minimumFractionDigits = 0 numberFormatter.maximumFractionDigits = 0 return numberFormatter } }
NumberFormatter인 이 코드에선 Locale부분이 없어도 결과에 영향이 없어서 빼도 좋을 거 같다는 생각이 든다.
1,000과 같이 세 자리마다 ,으로 구분해 주기 위해 numberStyle을 .decial으로 해주었다.
그리고 반올림을 하지 않고 버림을 하기 위해 roundingMode를 .floor로 했다.
소수점을 표현하지 않기 위해 minmum, maximumFractionDigits를 0으로 했다.
어느 순간인지는 모르지만 1100년 이후로 d - 10000000과 같이 아주 큰 수를 전달하면 오버플로우가 발생하는 거 같다.
이를 방지하기 위해서 제한을 뒀다. 하지만 코드를 보면 아주 허술한 점이 있다.
SecondViewController
import UIKit class SecondViewController: UIViewController { @IBOutlet weak var standardDatePicker: UIDatePicker! @IBOutlet weak var dDayInputField: UITextField! @IBOutlet weak var calculatedDateLabel: UILabel! @IBAction func calculateDDay(_ sender: Any) { guard let inputedDDayText = dDayInputField.text, inputedDDayText.count > 0 else { presentAlert(message: "D-Day를 입력해 주세요.") return } guard let inputedDDay = Int(inputedDDayText), inputedDDay > 0 else { presentAlert(message: "0이상의 수를 입력해 주세요.") return } if inputedDDay > 160271 { presentAlert(message: "1582년까지 입력 가능 합니다.") return } calculatedDateLabel.text = koreanDateFormatter.string(for: UIViewController.currentCalendar.date(byAdding: .day, value: inputedDDay * -1, to: standardDatePicker.date)) } override func viewDidLoad() { super.viewDidLoad() calculatedDateLabel.text = koreanDateFormatter.string(from: Date()) } }
TextField로 입력받는 텍스트를 무조건 입력을 하게 만들었고 정수만 가능하게 만들었다.
guard let inputedDDay = Int(inputedDDayText), inputedDDay > 0 else { presentAlert(message: "0이상의 수를 입력해 주세요.") return }
이 부분에서 이렇게 할 거면 Int 말고 UInt를 쓰면 되지 않나? 생각했지만 UInt를 사용하게 되면 아래 value 파라미터에서 타입 캐스팅을 다시 Int로 해줘야 하기 때문에 그냥 Int를 쓰는 게 제일 베스트이다.
if inputedDDay > 160271 { presentAlert(message: "1582년까지 입력 가능 합니다.") return }
문제의 코드 이 날은 20201101을 기준으로 15820101이 나오는 수를 집어넣었다.
분명 TimeInterval로 구한 뒤에 동적으로 구현할 수 있었는데 아무 생각 없이 써넣은 거 같다...
다시 고치자 반성 반성
ThirdViewController.swift
import UIKit class ThirdViewController: UIViewController { @IBOutlet weak var resultDateLabel: UILabel! @IBOutlet weak var datePicker: UIDatePicker! @IBAction func calculateDateToLive(_ sender: UIDatePicker) { if sender.date > Date() { presentAlert(message: "과거의 날짜로만 가주세요.") datePicker.maximumDate = Date() datePicker.maximumDate = nil } resultDateLabel.text = customNumberFormatter.string(for: sender.date.distance(to: Date()).convertToDay()) } override func viewDidLoad() { super.viewDidLoad() resultDateLabel.text = "0" } }
button을 놓지 않고 DatePicker에 날짜를 고르면 바로 계산이 되도록 구현했다.
그리고 미래의 날짜로 가지 못하게 if문을 사용했다.
처음에 datePicker.maximumDate = Date()를 viewDidLoad에 넣었는데 한 가지 문제가 있었다.
화면이 구성되자마자 maximumDate가 설정이 돼서 미래의 날짜를 골라도 경고창이 뜨지 않았다. 그냥 DatePicker가 오늘 날짜로 돌아올 뿐이었다.
그래서 calculateDateToLive 메서드에 넣었다.
그랬더니 발생한 문제는 처음에 미래로 갈 때만 경고창이 나오고 그 이후에는 maximumDate가 설정되어 경고창이 뜨지 않고 아까와 똑같았다.
그래서 그 이후 바로 nil로 초기화를 시켰다.
그랬더니 몇번을 미래로 가도 경고창이 계속 출력됐다.
마지막으로 resultDateLabel.text는 NumberFormatter를 사용했고 Date에 distance(to:) 메서드를 사용했다.
TimeInterval?을 리턴하기 때문에 extension에서 구현한 초를 날로 바꿔주는 convertToDay 메서드를 사용해서 날로 바꿔주었다.
728x90'TIL > 2021' 카테고리의 다른 글
201104 - TIL (0) 2020.11.04 201102 - TIL (0) 2020.11.03 201031 - TIL (1) 2020.11.01 201030 - TIL (2) 2020.10.31 201028 - TIL (0) 2020.10.29