앱이 재실행 되었을때 사용자가 마지막에 선택한 값이 픽커뷰에 표시되도록 변경해보기.
마지막에 선택한 행이 저장되도록 구현하였지만 앱이 실행될 때 자동으로 표시 되진 않는다.
뷰의 라이프사이클을 이용하여 앱이 실행될 때 마지막에 선택한 행이 표시되도록 구현해보자.
override func viewDidLoad() {
super.viewDidLoad()
let row = initialPickerRow()
distancePicker.selectRow(row, inComponent: 0, animated: false) // 중간값 표시
pickerView(distancePicker, didSelectRow: row, inComponent: 0) // 표시된 값 선택 액션
}
func initialPickerRow() -> Int {
return distancePicker.numberOfRowsInComponent(0) / 2 // 중간값 리턴
}
이제 저장했던 값이 있다면 표시되도록 아래와 같이 변경해 보자.
func initialPickerRow() -> Int {
// 키에 해당하는 정수를 리턴하고 값이 없다면 '0'을 리턴한다.
let savedRow = UserDefaults.standard.integer(forKey: userDefaultsLastRowKey) if savedRow != 0 { // 저장된 값이 0이 아니라면
return saveRow
} else { // 저장된 값이 없다면
return distancePicker.numberOfRows(inComponent: 0) / 2
}
}
앱을 실행해서 특정값을 선택한 후 재실행하여 그 값이 그대로 표시되는 것을 확인해보자.
하지만 여기에는 한가지 문제점이 있다. 첫번째 행을 선택후 앱을 재실행해 보면 내가 선택한 값이 아닌 중간값이 표시 될 것이다. 왜냐하면 첫번째 값을 선택 후 저장된 값을 불러오면 ‘0’이 반환되고 값이 저장되어 있지 않아도 ‘0’이 리턴되기 때문이다.
API 도큐먼트(⇧⌘0)를 실행하여 해당 메서드를 검색해보자.
func integer(forKey defaultName: String) -> Int
이 메서드는 리턴타입이 Int 형이므로 값이 존재하지 않아도 무조건 정수를 반환한다.
따라서 이 경우에 다른 메서드를 사용하는것이 적절해 보인다.
object(forKey:) 를 검색해 보자.
func object(forKey defaultName: String) -> Any?
리턴타입이 모든 타입을 담을수 있는 Any에 ?가 붙은 optional(Any) 타입이므로 값이 있다면 Any를, 없다면 nil 을 리턴할 것이다. 다음과 같이 리팩토링 해보자.
func initialPickerRow() -> Int {
let savedRow = UserDefaults.standard.object(forKey: userDefaultsLastRowKey) as? Int
// 저장된 값이 있다면 Any 타입을 리턴하므로 Int 형으로 타입 캐스팅한다.
if let row = savedRow { // savedRow 값이 존재 한다면 옵셔널 바인딩으로 row에 할당한다.
return row
} else { // savedRow 값이 nil 이라면
return distancePicker.numberOfRows(inComponent: 0) / 2
}
}
as? 는 캐스팅이 실패할 경우 nil을 반환하는 연산자로서 불러온 값이 Int 타입으로 캐스팅이 안될 수 있기 때문에 사용하였는데 만약 100% 캐스팅이 가능하다고 판단된다면 as! 를 사용하면 된다.
앱을 실행하여 첫번째 값을 선택후 재실행 해보면 첫번째 값도 잘 표시 될 것이다. 드디어 거리변환기앱을 완성하였다.