[iOS] To-do 클론 앱 따라하기 (1)

TIL 7일차 - To-do 클론 앱 따라하기 (1회 차)

(1회 차) 구현 목표

- 스토리보드로 리스트 UI 구성하기

- 배열에 담긴 더미(가짜) 데이터를 UITableView에 표시하기

- UITableView를 활용하여 화면 구현하기

 

(1회 차) 완성될 화면

- 상단에 제목 "할 일 목록" 표시

- 중단에 UITableView를 사용해할 일 목록 표시

- 하단에 기능이 아직 연결되지 않은 "새 할 일 추가" 버튼 배치

 

 

1. to Xcode 'TodoApp' 프로젝트 만들기

storyboard 선택하기

 

 

2. Main.storyboard에서 UI 구성하기

 

1) 상단에 Label 추가

스토리보드 키고 command+shift+L 눌러서 레이블창 띄우고 Label을 ViewController의 상단에 배치함

그리고 텍스트를 "할 일 목록"으로 입력하고 System, Bold, 24pt로 설정하고 중앙정렬한다.

우측 Label 설정 창

 

 

2) AutoLayout설정하기

(좌): CenterX 수평 중앙 정렬, (우): 위쪽을 safearea로부터 20만큼 떨어지기

 

 

3) UITableView 추가하기

 

1. 아까와 마찬가지 방식으로 Table View를 Label 아래에 불러온다.

 

2. 제약조건을 설정한다.

Background color를 시스템 그린 컬러로 설정하여 눈에 잘 들어오게 설정함

Top: Label로 부터 20pt

Leading/Trailing: superview로부터 0pt

Bottom은 75pt 떨어지게 설정함

 

 

4) TableView Cell을 불러와 넣고 Identifier를 "TodoCell"로 설정하기

 

 

5) 버튼 추가

 

1. 화면 하단에 UIButton "새 할 일 추가" 버튼 추가하기

2. Bottom은 safe area로부터 45pt만큼 떨어지고, CenterX: 수평중앙정렬 시킴

 

 

 

3.ViewController.swift에 연결하기

 

1) @IBoutlet 연결

ctrl누르고 Table View를 꾹 눌러서 ViewController 바로 밑에 집어넣기 (Name은 tableView로)

(코드옆에 동그란버튼 가리켰을 때 storyboard위의 Table View에 표시가 되면 정상적으로 연결된 것임.)

 

2) 이 ViewController가 테이블뷰의 대리인(대표자) 역할을 하겠다”라는 선언 해주기

UITableViewDelegate와 UITableViewDataSource 추가하기

UITableView는 그릇일 뿐으로 테이블 뷰 스스로는 몇 칸이 있는지, 각 칸에 무엇을 넣을지, 셀을 눌렀을 때 어떤 동작을 할 것인지 결정할 수 없다. 그래서 대신 결정해 주는 애들이 위에서 추가한 delegate와 dataSource이다.

 

 

먼저 UITableViewDataSource(데이터 담당)은 '무엇을 보여줄까?' 하는 역할을 하고

얘가 반드시 구현해야 하는 질문에는 두 개가 있는데

tableView(_:numberOfRowsInSection:) // 몇줄임?
tableView(:_cellForRowAt:) // 이 줄에는 뭘 보여줄까?

 

 

다음으로 UITableViewDelegate (행동 담당)으로 '사용자가 뭘 했을 때 어떤 반응을 할까?' 하는 역할을 한다.

예를 들어,

tableView(:_didSelectRowAt:) //셀을 눌렀을 때 뭘 할까?

 

 

당연히 프로토콜 에러가 뜨므로 Apply 눌러서 함수 삽입하기

 

 

viewDidLoad 함수 밑으로 위에서 삽입된 두 개의 함수 붙여 넣기 하기

 

짜잔~ 완성! 하고 빌드를 했다.!! 끝!인 줄 알았는데

 

휑~

하지만 내 예상과는 다르게 내가 만들어놓은 셀들의 속눈썹도 안 보인다.. 왜 안보일까? 심지어 에러도 안 나고 빌드는 또 잘만 됨.;;

 

별표 백만억개

ViewDidLoad() 함수 안에 이 두 문장을 추가해 보자 

왜 해야 하나? 첫 문장부터 해석해 보자면 tableView가 묻는다. 내 dataSource는 누가 넣어주게? 바로 self가 넣어줘! 그 self는 무엇이냐?

위에서 말했듯 대표자인 ViewController를 말하는 거다.

delegate도 마찬가지이다.

얘네를 넣고 나면 정상적으로 빌드된다. 얘네를 빼먹으면 진짜 큰일 나니까 무조건 해야 함!

 

마참내! 정상적으로 빌드 된 모습

 

근데 뭔가 심심하다 그래서 추가한 함수

    func tableView(_ tableView: UITableView,
                   didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        // animated가 false면 즉시 사라짐
    }
    
    // didSelectRowAt: 사용자가 이 줄을 눌렀음을 알려주는 알림 함수임

버튼을 누르면 선택표시(회색)가 부드럽게 사라진다.

 

 

이제 셀에 todoApp의 취지에 맞게 '해야 할 일'들을 집어넣을 건데 일단 배열을 만들어 더미데이터를 만들어 주었다.

    let todos = ["Swift 문법 공부하기", "운동 하기", "듀오링고 하기"]

 

 

그리고 numberOfRowsInsection의 return값도 배열의 원소의 수만큼 정해지도록 동적으로 바꿔준다.

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return todos.count // 배열의 원소의 수만큼 return함
    }

 

그리고 문자열 "JH" 대신 아까 만들었던 배열을 집어넣어줄 것인데

	func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TodoCell", for: indexPath)
        cell.textLabel?.text = todos[indexPath.row] //todos 배열의 원소 불러오기
        return cell
    }

 

근데 여기에서 궁금했던 게 왜 indexPath.row인지 모르겠는 거다..

 

일단! indexPath가 뭔지부터 정확히 잡자

IndexPath는 위치 정보 묶음이다.

IndexPath(section: Int, row: Int)

즉:

  • section = 몇 번째 묶음
  • row = 그 묶음 안에서 몇 번째 줄로

Int값 index와 헷갈리면 절대 안 된다..

 

참고로 테이블뷰는 구조가 이렇게 생김

 
Section 0
├─ Row 0
├─ Row 1
└─ Row 2

지금 내 코드는:

  • section = 1개
  • todos 배열 = 한 섹션의 데이터

그래서 항상:

indexPath.section == 0인것이다.
 
 
(1회차) 빌드 완