Do it! 스위프트로 아이폰 앱 만들기
12장 테이블 뷰 컨트롤러 이용해 할 일 목록 만들기
- Main의 뷰 컨트롤러와 swift 파일 삭제하기
- Library 에서 Table View Controller 를 찾아 스토리보드에 배치하기
- Prototype Cells 를 선택한 후 메뉴에서
Editor -> Embed in -> Navigation Controller 추가하기- 네비게이션 컨트롤러 선택한 후 [Attributes inspector] 에서 [is Initial View Controller] 체크하기
- Library 에서 View Controller 를 찾아 테이블 뷰 컨트롤러 오른쪽에 위 아래로 2개 배치하기
- 각 뷰마다 타이틀 변경하기
- 테이블 뷰 컨트롤러에 Bar Button Item 추가하기 (+)
- 추가한 바 버튼을 우클릭 드래그하여 Add View 컨트롤러에 놓고 Action Segue -> Show 선택하기 (세그웨이 연결)
- 테이블 뷰 컨트롤러에서 Prototype Cells 를 선택한 후 우클릭 드래그하여 Detail View 컨트롤러에 놓고 Action Segue -> Show 선택하기 (세그웨이 연결)
- 해당 세그웨이의 Identifier 를 'sgDetail' 로 설정하기
- Add View 컨트롤러에 텍스트 필드와 버튼 배치하기
- Detail View 컨트롤러에 레이블 배치하기
- 테이블 뷰 컨트롤러에서 Prototype Cells 의 Identifier 를 'myCell' 로 설정하기
- 3개의 swift 소스 파일 추가하기
- 테이블 뷰 컨트롤러: UITableViewController
- Add View, Detail View 컨트롤러: UIViewController
- Main 에서 각 컨트롤러의 클래스 연결하기
- 테이블 뷰 컨트롤러에서 Table View 에 대한 아웃렛 변수 추가하기
- Add View 컨트롤러에서 텍스트 필드에 대한 아웃렛 변수와 버튼에 대한 액션 함수 추가하기
- Detail View 컨트롤러에서 레이블에 대한 아웃렛 변수 추가하기
import UIKit
var items = ["책 구매", "철수와 약속", "스터디 준비하기"]
var itemsImageFile = ["cart.png", "clock.png", "pencil.png"]
class TableViewController: UITableViewController {
@IBOutlet var tvListView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
cell.textLabel?.text = items[(indexPath as NSIndexPath).row]
cell.imageView?.image = UIImage(named: itemsImageFile[(indexPath as NSIndexPath).row])
return cell
}
- TableViewController.swift 파일의 코드를 다음과 같이 수정하기
- func numberOfSections: 섹션 개수 반환 함수
- func tableView(numberOfRowsInSection): 섹션 내의 행의 개수 반환 함수
- func tableView(cellForRowAt): 각 행에 대한 셀 반환 함수
- 셀에 나타낼 레이블과 이미지 등을 설정할 수 있다.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
items.remove(at: (indexPath as NSIndexPath).row)
itemsImageFile.remove(at: (indexPath as NSIndexPath).row)
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
override func tableView(_ tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) -> String? {
return "삭제"
}
- TableViewController.swift 파일에 해당 코드 추가하기
- func tableView(commit editingStyle): 셀 내용 삭제 함수로 셀을 왼쪽으로 밀어서 삭제할 수 있게 한다.
- func tableView(titleForDeleteConfirmationButtonForRowAt): 셀을 왼쪽으로 밀었을 때 나타나는 삭제 버튼 이름을 변경할 수 있다.
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
self.navigationItem.leftBarButtonItem = self.editButtonItem
}
- TableViewController.swift 파일의 코드를 다음과 같이 수정하기
- 테이블뷰 컨트롤러의 왼쪽 바에 Edit 버튼이 생성되면서 셀 삭제 기능이 추가된다.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
let itemToMove = items[(fromIndexPath as NSIndexPath).row]
let itemImageToMove = itemsImageFile[(fromIndexPath as NSIndexPath).row]
items.remove(at: (fromIndexPath as NSIndexPath).row)
itemsImageFile.remove(at: (fromIndexPath as NSIndexPath).row)
items.insert(itemToMove, at: (to as NSIndexPath).row)
itemsImageFile.insert(itemImageToMove, at: (to as NSIndexPath).row)
}
- TableViewController.swift 파일에 해당 코드 추가하기
- 이동시킬 셀의 레이블과 이미지를 미리 저장한 후 삭제 및 삽입한다.
- 아이템 배열 내의 원소들이 재정렬되면서 목록에서 보여지는 순서가 변경된다.
@IBAction func btnAddItem(_ sender: UIButton) {
items.append(tfAddItem.text!)
itemsImageFile.append("clock.png")
tfAddItem.text = ""
_ = navigationController?.popViewController(animated: true)
}
- AddViewController.swift 파일에서 버튼 액션 함수 수정하기
override func viewWillAppear(_ animated: Bool) {
tvListView.reloadData()
}
- TableViewController.swift 파일의 viewDidLoad() 함수 아래쪽에 해당 코드 추가하기
- [Add] 버튼을 클릭하여 Main View 로 돌아왔을 때 추가된 내용이 나타나도록 한다.
💡 ViewDidLoad: 뷰가 생성될 때 한 번만 호출되는 함수이다.
💡 ViewWillAppear: 뷰가 노출될 때마다 호출되는 함수이다.
💡 ViewDidAppear: 뷰가 완전히 보인 후에 호출되는 함수이다.
import UIKit
class DetailViewController: UIViewController {
var receiveItem = ""
@IBOutlet var lblItem: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
lblItem.text = receiveItem
}
func receiveItem(_ item: String) {
receiveItem = item
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
- DetailViewController.swift 파일의 코드를 다음과 같이 수정하기
- receiveItem 함수를 통해 Main View 에서 데이터를 전달 받는다.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
if segue.identifier == "sgDetail" {
let cell = sender as! UITableViewCell
let indexPath = self.tvListView.indexPath(for: cell)
let detailView = segue.destination as! DetailViewController
detailView.receiveItem(items[((indexPath! as NSIndexPath).row)])
}
}
- TableViewController.swift 파일에 해당 코드 추가하기
- Main View 에서 셀을 선택했을 때 셀에 대한 상세 정보를 Detail View 로 전달한다.
import UIKit
var items = ["책 구매", "철수와 약속", "스터디 준비하기"]
var itemsImageFile = ["cart.png", "clock.png", "pencil.png"]
class TableViewController: UITableViewController {
@IBOutlet var tvListView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
self.navigationItem.leftBarButtonItem = self.editButtonItem
}
override func viewWillAppear(_ animated: Bool) {
tvListView.reloadData()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
cell.textLabel?.text = items[(indexPath as NSIndexPath).row]
cell.imageView?.image = UIImage(named: itemsImageFile[(indexPath as NSIndexPath).row])
return cell
}
/*
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
*/
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
items.remove(at: (indexPath as NSIndexPath).row)
itemsImageFile.remove(at: (indexPath as NSIndexPath).row)
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
override func tableView(_ tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) -> String? {
return "삭제"
}
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
let itemToMove = items[(fromIndexPath as NSIndexPath).row]
let itemImageToMove = itemsImageFile[(fromIndexPath as NSIndexPath).row]
items.remove(at: (fromIndexPath as NSIndexPath).row)
itemsImageFile.remove(at: (fromIndexPath as NSIndexPath).row)
items.insert(itemToMove, at: (to as NSIndexPath).row)
itemsImageFile.insert(itemImageToMove, at: (to as NSIndexPath).row)
}
/*
// Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
*/
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
if segue.identifier == "sgDetail" {
let cell = sender as! UITableViewCell
let indexPath = self.tvListView.indexPath(for: cell)
let detailView = segue.destination as! DetailViewController
detailView.receiveItem(items[((indexPath! as NSIndexPath).row)])
}
}
}
import UIKit
class AddViewController: UIViewController {
@IBOutlet var tfAddItem: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
@IBAction func btnAddItem(_ sender: UIButton) {
items.append(tfAddItem.text!)
itemsImageFile.append("clock.png")
tfAddItem.text = ""
_ = navigationController?.popViewController(animated: true)
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
import UIKit
class DetailViewController: UIViewController {
var receiveItem = ""
@IBOutlet var lblItem: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
lblItem.text = receiveItem
}
func receiveItem(_ item: String) {
receiveItem = item
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
- Add View 컨트롤러에 이미지 뷰와 피커 뷰 추가하기
- 피커 뷰에 대한 delegate 를 AddViewController 로 설정하기
- 이미지 뷰에 대한 아웃렛 변수 추가하기
import UIKit
let images = ["cart.png", "clock.png", "pencil.png"]
class AddViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
var selectedRow = 0
@IBOutlet var imgView: UIImageView!
@IBOutlet var tfAddItem: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
imgView.image = UIImage(named: images[0])
}
@IBAction func btnAddItem(_ sender: UIButton) {
items.append(tfAddItem.text!)
itemsImageFile.append(images[selectedRow])
tfAddItem.text = ""
_ = navigationController?.popViewController(animated: true)
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return images.count
}
func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
let img = UIImageView(image: UIImage(named: images[row]))
img.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
return img
}
func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
return CGFloat(50)
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
imgView.image = UIImage(named: images[row])
selectedRow = row
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}