class LocationViewController: UIViewController {
// MARK: - Properties
let location = ["서울", "인천", "대전", "대구", "광주", "부산", "울산", "세종", "경기도", "강원도", "충청북도", "충청남도", "경상북도", "경상남도", "전라북도", "전라남도", "제주도"]
let pickerView: UIPickerView = UIPickerView()
var focusedRow: Int = 0
var selectedRow: Int = 0
// MARK: - IBActions
@IBAction func touchUpAreaButton(_ sender: UIButton) {
let alert = UIAlertController(title: "지역 선택", message: "\n\n\n\n\n\n\n\n", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "취소", style: .cancel) { _ in
self.pickerView.selectRow(self.selectedRow, inComponent: 0, animated: true)
})
alert.addAction(UIAlertAction(title: "확인", style: .default) { _ in
self.selectedRow = self.focusedRow
})
pickerView.frame = CGRect(x: 0, y: 50, width: 270, height: 130)
pickerView.delegate = self
pickerView.dataSource = self
alert.view.addSubview(pickerView)
self.present(alert, animated: true, completion: nil)
}
}
// MARK: - PickerView
extension LocationViewController: UIPickerViewDelegate, UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return location.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return location[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
focusedRow = row
}
func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat
{
return 30
}
}
전체 소스코드는 위와 같습니다.
다른 코드는 어렵지 않게 이해할 수 있을 텐데, selectedRow 변수와 focusedRow 변수를 따로 쓰는 이유에 대해서는 짚고 넘어가보겠습니다.
didSelectRow 메소드는 피커뷰를 굴릴 때마다 호출됩니다. 슬라이드를 휙 내려서 여러 지역이 후두두둑 지나가면, 그 많은 지역들에 포커스가 맞춰질 때마다 호출되는 것입니다. 따라서 이 메소드 내에서 selectedRow = row
로 최종선택된 지역을 저장하게 되면 확인
버튼을 누를 때는 문제가 없지만, 취소
버튼을 누를 때가 문제입니다.
만약 [전국]이 선택돼있는 상태에서 피커뷰를 굴리다가 [충청북도]에서 멈췄습니다. 그런데 갑자기 [전국]을 더 보고 싶어져서 취소
버튼을 눌렀습니다. 그렇게 [전국]을 더 구경하다가, 다른 지역을 보기 위해 얼럿을 띄웠는데 포커스가 [전국]이 아닌 [충청북도]에 가 있습니다. UX적으로 보기 좋은 상황은 아닙니다 😔
이는 UIPickerView가 마지막으로 선택된 row를 갖고 있다가, 다시 피커뷰가 띄워졌을 때 자동으로 그 row가 선택된 상태를 보여주기 때문입니다.
따라서 취소
버튼을 누르면 피커뷰를 다시 원래 상태로 돌려주어야 합니다. 코드로 명시적으로 작성해주어야 하는 것이죠. 그래서 focusedRow 변수가 필요합니다! 💡
didSelectRow 메소드가 불릴 때마다, 즉 피커뷰가 굴러갈 때마다 포커스를 얻었던 지역을 focusedRow = row
로 저장합니다.
확인
버튼이 눌리면 selectedRow = focusedRow
로 선택지역을 업데이트해줍니다.
취소
버튼이 눌리면 피커뷰의 포커스를 원래 지역으로 돌려줘야 하기 때문에 pickerView.selectRow(selectedRow, inComponent: 0, animated: true)
로 선택지역을 리셋해줍니다.