[Do it!] 테이블 뷰 컨트롤러 앱

CoCoral·2023년 10월 21일
1

Do it!

목록 보기
17/25
post-thumbnail

Do it! 스위프트로 아이폰 앱 만들기
12장 테이블 뷰 컨트롤러 이용해 할 일 목록 만들기

📱 실행 화면 📱


💻 Step 1. 기존의 뷰 컨트롤러 삭제하기

  • Main의 뷰 컨트롤러와 swift 파일 삭제하기

💻 Step 2. 스토리보드 꾸미기

  • 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 에서 각 컨트롤러의 클래스 연결하기

💻 Step 3. Outlet 변수와 Action 함수 추가하기

  • 테이블 뷰 컨트롤러에서 Table View 에 대한 아웃렛 변수 추가하기
  • Add View 컨트롤러에서 텍스트 필드에 대한 아웃렛 변수와 버튼에 대한 액션 함수 추가하기
  • Detail View 컨트롤러에서 레이블에 대한 아웃렛 변수 추가하기

💻 Step 4. 테이블 목록 보여 주기

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): 각 행에 대한 셀 반환 함수
    • 셀에 나타낼 레이블과 이미지 등을 설정할 수 있다.

💻 Step 5. 목록 삭제하기

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): 셀을 왼쪽으로 밀었을 때 나타나는 삭제 버튼 이름을 변경할 수 있다.

💻 Step 6. 바 버튼으로 목록 삭제하기

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 버튼이 생성되면서 셀 삭제 기능이 추가된다.

💻 Step 7. 목록 순서 바꾸기

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 파일에 해당 코드 추가하기
  • 이동시킬 셀의 레이블과 이미지를 미리 저장한 후 삭제 및 삽입한다.
  • 아이템 배열 내의 원소들이 재정렬되면서 목록에서 보여지는 순서가 변경된다.

💻 Step 8. 새 목록 추가하기

@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: 뷰가 완전히 보인 후에 호출되는 함수이다.

💻 Step 9. 목록의 세부 내용 보기

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 로 전달한다.

💻 Final Step. 전체 소스 코드

  • TableViewController.swift
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)])
        }
    }
    

}

  • AddViewController.swift
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.
    }
    */

}

  • DetailViewController.swift
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.
    }
    */

}

💻 도전! Misson 아이콘 선택 기능 추가하기

  • Add View 컨트롤러에 이미지 뷰와 피커 뷰 추가하기
  • 피커 뷰에 대한 delegate 를 AddViewController 로 설정하기
  • 이미지 뷰에 대한 아웃렛 변수 추가하기

  • AddViewController.swift 소스 코드
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.
    }
    */

}
profile
언젠간 iOS 개발자가 되겠지

0개의 댓글