
// UIViewController
class SomeUIViewController: UIViewController {
@IBOutlet var someTable: UITableView!
...
func configureTable() {
// 각각의 프로토콜에 테이블 핸들링을 위임 받는다.
someTable.delegate = self
someTable.dataSource = self
someTable.register(
UINib(nibName: CustomTableCell.identifier, bundle: nil),
forCellReuseIdentifier: CustomTableCell.identifier
)
}
}
extension SomeUIViewController: UITableViewDelegate, UITableViewDataSource {
// SomeUIViewController의 역할을 테이블 관련 프로토콜로 확장시킨다.
func tableView(
_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
// 하나의 테이블 섹션에서 몇 개의 행을 생성할 지 결정해주어야 한다.
return datas.count
}
func tableView(
_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// xib로 생성한 커스텀 Cell 인스턴스를 반환해줘야 한다.
let cell = someTable.dequeueReusableCell(
withIdentifier: CustomTableCell.identifier, for: indexPath
) as! CustomTableCell
return cell
}
func tableView(
_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath) {
// 특정 Cell이 선택(select)되었을 때, 어떤 동작을 처리할 지에 대한 로직이 온다.
// 해당 메서드에 대한 구현은 필수가 아니지만, 보통 구현하는 것 같다.
}
}// SomeViewController.swift
class SomeViewController: UIViewController {
var datas: SomeDataSet?
override func viewDidLoad() {
super.viewDidLoad()
..
}
func configureViewWithData(dataSet: SomeDataSet) {
// 멤버 변수로 선언한 datas에 값을 할당해준다.
datas = dataSet
// Optional 멤버 변수로 선언했기 때문에 unwrapping은 필수다.
guard let datas else { return }
someLabel.text = datas.somekey.text
..
}
}
// UITableView가 컨트롤 되는 VC에서 작성
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(
withIdentifier: SomeViewController.id
) as! SomeViewController
// pushViewController 메서드를 호출하기 전에 데이터를 VC 멤버들에 할당해준다.
vc.configureViewWithData(dataSet: someDatas[indexPath.row])
navigationController?.pushViewController(vc, animated: true)
someTable.reloadRows(at: [indexPath], with: .none)
}
func tableView(_ tableView: UITableView, didSelect)let vc = SomeViewController()
present(vc, animated: true)// City Model
struct City {
let city_name: String
let city_english_name: String
let city_explain: String
let city_image: String
let domestic_travel: Bool
var formattedCityName: String {
return "\(city_name) | \(city_english_name)"
}
}
struct Cities {
static let cities: [City] = [ ... ]
var isDomesticCities: [City] {
Cities.cities.filter {
return $0.domestic_travel
}
}
var isNotDomesticCities: [City] {
Cities.cities.filter {
return !$0.domestic_travel
}
}
}// View Controller
class SomeViewController {
..
var data: [City]?
@IBOutlet var segmentControl: UISegmentedControl!
func configSegment() {
["전체", "국내", "해외"].enumerated().forEach { (idx, v) in
// 이렇게 배열로 하지 않고 다른 방법을 사용해도 물론 괜찮다.
segmentControl.setTitle(v, forSegmentAt: idx)
}
// 버튼에 addTarget하는 것과는 다르게,
// 세그먼트의 value가 변경되면 액션이 발생되도록 설정해줬다.
segmentControl.addTarget(
self,
action: #selector(selectSegment),
for: .valueChanged
)
}
@objc func selectSegment(_ sender: UISegmentedControl) {
// sender 인스턴스 자체에 selectedSegmentIndex 값이 있다.
switch sender.selectedSegmentIndex {
case 1:
data = Cities.isDomesticCities
case 2:
data = Cities.isNotDomesticCities
default:
data = Cities.cities
}
// 물론 여기쯤에서 테이블 데이터를 reload하는 함수 호출이 필요할 수 있다.
}
}// View Controller
extension SomeViewController: UISearchBarDelegate { }extension SomeViewController: UISearchBarDelegate {
var data: [City]?
@IBOutlet var searchBar: UISearchBar!
// 1. 검색어를 입력하고, '검색' 버튼 or '입력키' 를 입력했을 때
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
view.isEndEditing(true) // 키보드를 내려주고
// 서치바에 입력한 값이 nil일 경우를 배제해주고
guard let text = searchBar.text?.lowercase() else { return }
if text.isEmpty {
// 서치바를 비어둔 상태에서 입력 -> 기존 데이터 유지
data = 기존 데이터
} else {
data = 검색어로 필터링한 데이터
}
// 검색이 끝나면, 업데이트 된 데이터를 기반으로 테이블 리로드
table.reloadSections(IndexSet(integer: 0), with: .none)
}
// 2. 검색어를 타이필 할 때마다 호출되는 메서드 (1번과 내부는 거의 동일함)
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
guard let text = searchBar.text?.lowercased() else { return }
if text.isEmpty {
searchedData = 기존 데이터
} else {
searchedData = 검색어로 필터링한 데이터
}
table.reloadSections(IndexSet(integer: 0), with: .none)
}
// 검색어에 따라 기존 데이터를 필터링하는 helper function
func _filteringData(_ dataSet: [City], keyword: String) -> [City] {
return dataSet.filter {
$0.city_name.contains(keyword)
|| $0.city_en_name.contains(keyword)
|| $0.city_explain.contains(keyword)
}
}
}import MapKit
class SomeViewController: UIViewController, MKMapViewDelegate {
var address: (String, Double, Double) = ("서울역", 37.554921, 126.970345)
@IBOutlet var mapview: MKMapView!
@IBOutlet var mapPickerTextField: UITextField!
let picker = UIPickerView()
func configureMapView() {
mapview.delegate = self
setAddressOnMap(address, map: mapview)
}
func setAddressOnMap(_ addressData: (String, Double, Double), map: MKMapView) {
let annotation = MKPointAnnotation()
map.setRegion(
MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: addressData.1, longitude: addressData.2),
latitudinalMeters: 100, longitudinalMeters: 100)
, animated: true)
annotation.coordinate = CLLocationCoordinate2D(latitude: addressData.1, longitude: addressData.2)
annotation.title = addressData.0
map.addAnnotation(annotation)
}
}class SomeViewController {
var mapDatas: [(String, Double, Double)] = [..]
@IBOutlet var mapPickerTextField: UITextField!
let picker = UIPickerView()
func viewDidLoad() {
..
// 관련 권한을 위임해준다.
picker.delegate = self
mapPickerTextField.inputView = picker
}
}
// TextField 위에 UIPicker를 올려서 필드가 터치되면 피커가 bottom-up 되게 하려고 한다.
extension SomeViewController: UIPickerViewDelegate, UIPickerViewDataSource {
// PickerView에 몇 개의 휠 컴포넌트가 있을지 결정한다.
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
// 휠 컴포넌트마다 몇 개의 데이터를 보여줄 지 결정한다.
func pickerView(
_ pickerView: UIPickerView,
numberOfRowsInComponent component: Int) -> Int {
return mapDatas.count
}
// 컴포넌트에서 어떤 데이터를 보여줄 지 결정한다.
func pickerView(
_ pickerView: UIPickerView,
titleForRow row: Int,
forComponent component: Int) -> String? {
return mapDatas[row].0
}
func pickerView(
_ pickerView: UIPickerView,
didSelectRow row: Int,
inComponent component: Int) {
// 마치 텍스트 필드에 선택 값이 반영된 것처럼 보여주고
mapPickerTextField.text = mapDatas[row].0
// 데이터에 해당하는 값을 맵핑하고
present = (mapDatas[row].0, mapDatas[row].1, mapDatas[row].2)
// 지도에서 보여주고
setAddressOnMap(present)
// 피커를 내려준다.
view.endEditing(true)
}
}