[TIL] 2022-04-20

선주·2022년 4월 20일
0

TIL

목록 보기
5/13

📌 OJT

Application Life Cycle

앱 아이콘을 클릭하거나 혹은 다른 경로로 앱을 실행시켜보자. Application이 Create되고, OS가 앱을 Running State로 올려준다. 여기까지가 OS의 일이다. Running State가 되면 뷰의 라이프사이클이 시작된다.

✏️ AppDelegate

개발자들은 OS가 하는 일(Application Create → Running State)에 권한은 없으나 이 과정에서 ‘이러한 이벤트가 있어!’ 하고 알림을 받을 수 있다.
'applicationDidEnterBackground됐어. 너 뭐 할래?' 같은 메소드가 정의되어 있고,
'ㅇㅇ 로그 찍을래.' 처럼 필요시 그에 따른 할 일을 처리하는 것이 Delegate에서 일어난다.

✏️ SceneDelegate

ios의 멀티윈도우를 위해 생긴 개념이다. ios13부터 추가됨!
SceneDelegate에서 윈도우가 두개 이상이 되었을 때의 이벤트를 받을 수 있다.


View Life Cycle

init
→ viewDidLoad
→ viewWillAppear
→ viewDidAppear
→ viewWillDisappear
→ viewDidDisappear
→ deinit

init부터 viewWillAppear, viewDidAppear까지는 inflate 과정이다.
inflate는 iOS의 스토리보드/SwiftUI (Android에서의 xml 레이아웃/ComposeUI)를 OS의 메모리에 올리는 작업(= 컴퓨터가 알아들을 수 있게 이진수로 바꾸는 작업)을 말한다.

각각의 단계에서 무슨 일을 주로 할까?

  • viewDidLoad: 화면 초기화
  • viewWillAppear: 데이터 갱신 (뷰가 나타나기 전에 갱신해야 함)
  • viewWillDisappear / viewDidDisappear: 설정값 저장! Switch ON/OFF처럼 뷰와 연결된 IBOutlet 변수로 설정값을 조작하는 경우 deinit될 때 쯤에는 이미 뷰가 사라져있으므로 저장이 불가하고, 뷰와 연결된 정보가 아니더라도 메모리에서 해제되기 직전에 그제서야 허겁지겁 설정값을 저장하는 것 자체가 안전하지 못하다. 따라서 deinit 전인 viewWillDisappear 또는 viewDidDisappear 단계에서 해주는 것이 좋다.

📌 TableView

TableView를 만들기 위해서는 DataSource와 Delegate가 필수 요소이다.

  • TableView DataSource

    • 사용자에게 보여줄 데이터 관리

    • 테이블뷰가 화면에 보여지기 전에 데이터소스에게 '내가 몇개의 데이터를 보여주어야 하는지' 물어보고, 각 섹션의 로우에 해당하는 cell을 보내달라고 부탁하면 데이터소스가 cell을 보내준다.

    • ✏️ 섹션 수 반환 메소드
      구현 필수인 메소드는 아니고, 구현하지 않을 경우 기본값인 1을 반환한다.

      func numberOfSections(in tableView: UITableView) -> Int {
      	return 2
      }
    • ✏️ 섹션별 로우 수 반환 메소드 (필수 구현!)

      func tableView(_ tableView: UITableView, numbersOfRowsInSection section: Int) -> Int {
      	return 3
      }
    • ✏️ 인덱스(n번째 섹션의 n번째 로우)에 해당하는 cell 반환 메소드 (필수 구현!)

      func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
      	let cell = tableView.dequeReusableCell(withIdentifier: "cell", for: indexPath)
          cell.textLabel?.text = "section: \(indexPath.section), row: \(indexPath.row)"
          return cell
      }
    • ✏️ 섹션별 헤더 반환 메소드

      func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
      	return "Section \(section) Header"
      }
  • TableView Delegate

    • 테이블뷰 관리, 사용자 이벤트 핸들링

    • 사용자가 cell을 선택하거나 편집하려는 시도를 하면 delegate에게 어떻게 해야하는지 물어보고 delegate가 알려주는대로 처리하거나 delegate에게 처리를 맡긴다.


간단한 예제를 만들어 보자.

프로젝트를 생성하고, Cocoa Touch Class 새 파일을 만들 건데 UITableViewController를 상속받는 TableViewController라는 이름의 파일로 만들어준다.


Main.storyboard
스토리보드에 기본으로 생성되어 있는 View Controller를 삭제하고 오브젝트 라이브러리로부터 Table View Controller를 하나 생성한다.

해당 View Controller를 잡고 [Editor] > [Embed In] > [Navigation Controller]를 눌러 뷰 컨트롤러를 내비게이션 컨트롤러에 임베드해준다.

  • Navigation Controller에서 할 일
    이 내비게이션 컨트롤러를 스토리보드의 첫번째 뷰 컨트롤러로 지정하기 위해 [Attribute Inspector]에서 [Is Initial View Controller]에 체크해주자.

  • View Controller에서 할 일
    뷰 컨트롤러의 [Identify Inspector]에서 Class를 TableViewController.swift로 선택한다.
    cell의 [Attribute Inspector]에서 Identifier를 MyCell이라는 임의의 이름으로 설정해준다.

이제 스토리보드에서 할 일은 끝났다. 아까 새로 만들었던 파일의 코드를 마저 작성해보자.


TableViewController.swift

class TableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    /// 섹션 수 반환
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 2
    }
    
    /// 섹션별 로우 수 반환 (필수)
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }

    /// 인덱스에 해당하는 셀 반환 (필수)
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath)
        cell.textLabel?.text = "section \(indexPath.section), row: \(indexPath.row)"
        return cell
    }
    
    // 섹션별 헤더 반환
    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return "Section \(section) Header"
    }
}

UITableViewController의 정의부를 보면
이렇게 테이블뷰를 만들기 위해 필요한 Delegate와 DataSource를 모두 상속받고 있다.

우리는 처음에 기본으로 만들어져 있던 ViewController를 지우고 UITableViewController를 상속받는 TableViewController를 새로 생성해주었기 때문에 코드상에서 상속부가 상당히 간단해졌다.

하지만 기본 ViewController를 그대로 사용했다면

class ViewController: UIViewController, UITableViewController, UITableViewDataSource {  }

이런 식으로 불필요하게 상속부가 길어졌을 것이다.

dequeueReusableCell

cellForRowAt 파라미터를 받는 tableView 메소드를 보자. dequeueReusableCell이라는 걸 사용하는데, 이게 뭘까?

테이블뷰는 테이블 높이와 셀 높이를 기반으로 셀 수를 생성한다. 만약 현재 화면에서 셀이 6개까지밖에 안 보인다면, 6개의 셀에 대한 메모리 할당만 이루어지는 것이다. 테이블뷰를 스크롤한다고 해도 동일한 셀이 사용되고 DataSource를 기반으로 셀 내용만 바뀌게 된다.

셀이 스크롤로 인해 화면 밖으로 밀려나면, 해당 셀은 reuse pool에 들어가게 되고, 우리가 dequeueReusableCell을 호출할 때 반환된다. 이런 식으로 메모리를 절약할 수 있는 것이다.

dequeueReusableCell의 파라미터인 withIdentifier에는 우리가 재사용할 객체를 나타내는 문자열(Identifier)이 들어간다. 우리는 셀을 재사용할 것이고, 아까 스토리보드에서 셀의 identifier를 MyCell로 지정해주었으므로 이 파라미터의 값에 "MyCell"을 넘겨준 것이다.

또 다른 파라미터인 for에는 셀의 위치를 지정하는 indexPath가 들어간다. indexPath는 [section, row] 로 이루어져 있어서 이 셀이 몇 번째 섹션의 몇 번째 로우에 위치하는지 알 수 있다.

시뮬레이터를 실행시켜보면 원하는 대로 잘 적용된 모습을 확인할 수 있다 😆


참고
Tistory | ZeddiOS
Git Blog | VincentGeranium Blog

profile
기록하는 개발자 👀

0개의 댓글