프로젝트/메모앱

005 ~ 008. Main Layout, Memo Class, Memo List

걔랑계란 2021. 7. 23. 22:50

Main Layout

Main.storyboard에는 기본적으로 한 개의 Scene(씬)이 추가되어 있다.
그리고 이 씬은 ViewController 클래스와 연결되어 있다.
그냥 사용해도 괜찮지만 강의에선 해당 씬과 뷰 컨트롤러를 삭제한 상태로 개발을 시작한다.
따라서 해당 씬을 Delete로 삭제하고, 기존의 ViewController.swift 파일도 삭제하고 시작한다.

삭제 후엔 라이브러리를 통해 Navigation Controller를 선택한다.
라이브러리는 'Shift + Cmd + L'로 접근할 수 있다.

이후 엔 두개의 화면이 표시되는데,
좌측은 화면 이동을 담당하는 객체이고,
우측이 실제 화면이 출력되는 Scene이다.
따라서 목록을 표시할 수 있는 Table View Controller가 우측에 추가되어 있는 것을 확인 할 수 있다.

앞에서 부터 언급되는 View Controller는 이벤트를 처리하고, View(뷰)를 관리하는 객체이다.
이 때 View는 버튼이나 이미지 뷰 같은 것들을 지칭하고,
하나 이상의 뷰들이 모여 하나의 화면을 이룬다.

지금 상태로 앱을 실행하면 위와 같은 검은 화면을 만날 수 있다.
이는 시작화면이 지정되지 않아 생기는 흔한 실수로 좌측 씬을 선택한 후,
어트리뷰트 인스펙터에서 'is Initial View Controller'를 선택하면 씬의 좌측에 화살표가 추가된 것을 확인 할 수 있다.

이는 화면의 흐름이 해당 부분부터 시작됨을 나타낸다.
이후에 다시 실행하면 정상적으로 TableView가 나타나는 걸 확인 할 수 있다.

Navigation Controller에는 Navigation bar가 자동으로 생성된다.
해당 부분을 클릭해 제목을 수정하고, 좌측 씬의 인스펙터의 'Prefer Large Tiltes'를 활성화 해 큰 제목으로 설정할 수 있다,

또한 네비게이션 바에는 버튼을 추가할 수 있다.
라이브러리에서 'Bar Button Item'을 선택해 네비게이션 바에 추가해 보자.

버튼을 선택하고 인스펙터에서 Style을 'Add'로 변경한다.
여러가지 스타일이 있으니 각각 어떤 모습을 하고 있는지 확인해 보는 것도 좋다.

이후 다시 실행하면 위와 같은 화면을 확인할 수 있다.

Memo Class

작업을 시작하기 앞서
새 그룹과 새 파일을 생성한다.
파일의 이름은 Model.swift이다.

해당 파일에는 Memo 클래스를 생성한다.
이 클래스에는 String 형식의 content와 Date 형식의 date 두 개의 변수가 필요하고,
클래스이기 때문에 이 둘을 초기화하는 생성자가 반드시 필요하다.

class Memo {
    var content: String
    var date: Date
    
    init(content: String) {
        self.content
        date = Date()
    }
}

content의 경우 사용자가 입력하는 문자열이 저장되어야 하기 때문에 parameter로 String 형식의 content를 받는다.
date의 경우 현재의 시간을 직접 생성하면 되기 때문에 별도의 파라미터가 필요 없다.

이후 테이블에 임시로 띄울 더미 데이터를 생성한다.

class Memo {
    var content: String
    var date: Date
    
    init(content: String) {
        self.content
        date = Date()
    }
    
    static var dummyMemoList = [
        Memo(content: "What I daid to you remains like a will.")
}

더미데이터는 배열 형태로 전달한다.

Memo List

보통의 앱들은 데이터를 DB에 저장한다.
하지만 초반 단계인 지금은 배열에 담아 임시적으로 사용하도록 한다.

Table View는 하나의 데이터를 하나의 Cell(셀)로 표시한다.
씬에서 셀을 선택하고 인스펙터에서 셀을 디자인한다.

Style은 기본적으로 Custom이 지정되어 있지만 해당 스타일은 이름 그대로 직접 구성할 때 사용한다.
강의에선 Subtilte을 사용하니 Subtitle을 선택하자.

이후엔 Subtilte만 Label 색상을 Light Gray로 변경했다.
이후엔 Identifier를 작성한다.
해당 Identifier에 적절한 값이 있어야 셀을 표시할 수 있다.

이렇게 완성 된 셀의 모습이다.
Title과 회색의 Subtitle이 존재하고, Identifier가 입력되어 있다.
이제는 씬과 클래스를 연결해 셀에 데이터를 표시하도록 코드를 작성해야한다.

프로덕트 번들에 파일을 추가한다.

이 때 추가하는 파일의 템플릿은 'Cocoa Touch Class'이다.
생성하는 클래스 파일은 반드시 UIViewController를 상속 받거나, 상속 받은 클래스를 상속 받아야 한다.
이번에 상속 받을 클래스는 TableViewContoller로 UIViewController를 상속 받은 클래스이다.

그러면 생성된 파일에서 자동으로 작성된 코드들을 확인 할 수 있다.
해당 파일에 코드를 작성하여 셀이 정상적인 데이터를 표시할 수 있도록 해야한다.

이러한 작업은 Delegate Pattern에 따라 5단계로 진행된다.

  1. 테이블 뷰 배치.
    테이블 뷰를 Scene에 배치한다.
    해당 과정은 Storyboard에서 TableViewController를 선택하면서 자동으로 배치되었다.

  2. 프로토타입 셀 디자인, 셀 Identifier 지정
    해당 과정은 Main Layout을 작성하는 과정에서 Cell 스타일을 Subtitle로 지정하고, 인스펙터에서 Identifier를 작성하는 것으로이미 마쳤다.

  3. 데이터 소스, Delegate 연결
    TableViewController가 자동으로 진행한다.
    만약 다른 경우라면 직접 연결해야 할 필요가 있다.

    • Table View가 몇개의 셀을 표시해야 하는지.
    • 어떤 디자인으로 표시해야 하는지.
    • 어떤 데이터를 표시해야 하는지.

    를 직접 디자인하고 알려 줘야한다.

    이런 역할을 하는 객체를 Datasource라고 하고, 정확히 말 하면
    UISourceData protocol을 태용한 형식으로, TableView에 데이터를 표시하기 위한 메소드가 선언되어 있다.

  4. 인스펙터의 네번째 메뉴인 Identifier Inspector에서 CustomClass 부분을 생성한 클래스로 설정한다.
  5. 데이터 소스 구현
    방금 생성한 MemoListTableViewController 가 UITableViewController를 상속받고 있다.

    TableView와 데이터 소스는 호출과 반환으로 상호작용 한다.
    import UIKit
    
    class MemoListTableViewController: UITableViewController {
        override func viewDidLoad() {
        }
    
        override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return Memo.hummyMemoList.count
        }
    
        override func tableView(_ tableView: UITableView, cellForRowAt indexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
    
            let target = Memo.dummyMemoList[indexPath.row]
            cell.textLabel?.text = target.content
            cell.detailTextLabel?.text = target.date.description
    
            return cell
        }
    }​

    해당 클래스에서 실질적으로 작성해야할 메소드는 2개이다.
    이미 작성되어 있던 numberOfSection은 사용하지 않을 메소드이기 때문에 삭제하도록 하고,

    첫번째 tableView 메소드 부터 시작한다.
    해당 메소드는 몇개의 셀을 출력해야 하는지에 관여하고, 메소드는 배열에 저장 된 데이터의 수를 세서 전달한다.

    두번째 tableView 메소드는 어떤 디자인으로 어떤 데이터를 출력해야 하는가에 관여한다.
    해당 메소드는 셀 마다 출력 데이터가 다르기 때문에 셀 을 출력할 때 마다 반복해서 작동한다.
    따라서 몇번째 셀인지 판단하기 위한 지표인 IndexPath를 몱록 내에서 특정 위치를 표현할 때 사용한다.

    해당 메소드는 dequeueReusableCell을 호출해 사용할 셀 디자인과 indexPath를 전달 받는다.
    target으로 표시할 데이터를 받아 오고, indexPath를 사용해 이에 접근한다.
    이후 실제 데이터를 셀의 label에 추가한다.

지금까지의 과정을 완료한 뒤 앱을 실행하면 다음과 같이,
임시로 입력한 데이터가 TableView에 정상적으로 출력 되는 것을 확인할 수 있다.

하지만 해당 메모를 클릭해도, 추가한 버튼을 클릭해도 전혀 작동하지 않는다.
이는 마지막 단게인 Delegate를 진행하지 않았기 때문이다.


Log

2021.07.23.
블로그 이전으로 인한 글 옮김 및 수정