오늘은 지인짜 첫 발 떼기 단계!
기초 UI 구현하고 AutoLayout만 잡아줬습니다.
스토리보드 컴포넌트 추가
AutoLayout 설정하기
일단 내가 아는 방법..냅다 사용하기!…!
Interface Builder에서 각 요소들을 선택하고 설정해줬어요.
(코드로 하는 방법도 공부를 해야 하는데 말이죠..~)
위 사진의 왼쪽 항목들 중 Constraints 부분이 각 컴포넌트에 설정해준 AutoLayout 입니다!
Lv1 구현 완료! 저에게는 굉장히 쉽지 않았는데 말이죠...
새로 알게 되거나, 배우긴 했는데 처음 사용해보는 것들 투성이였습니다ㅎㅎ......!!!!!!
내용 정리하며 복습하는 시간을 가져보도록 할게요!
TableViewCell에 Label과 Switch 추가하기
우선 첫 날 만들어둔 TableViewCell 위에
할 일이 작성될 Label과 완료 여부를 나타낼 Switch를 추가해줬습니다.
그리고 이 때 중요한 포인트!
TableViewCell의 Identifier를 설정해줘야 해요.
저는 listCell이라고 해줬어요!
1. ViewController class에 tableView 연결
IBOutlet을 사용하면 인터페이스 요소(UIButton, UILabel, UITableView 등)를 코드와 연결해줄 수 있다는 거!!
우리는 tableView을 나타내기 위한 코드를 작성해야 하니까 IBOutlet으로 tableView를 ViewController class에 연결합니다!
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
}
2. 변수 tasks 정의
해야 할 일들을 담아줄 변수가 있어야겠죠?!
tasks라고 이름을 붙여줬습니다. 변수의 타입은 String으로 지정해줍니다.
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
var tasks = [String]()
}
여기까지는 별 거 없죠?!
자 그럼 이 친구들을 동작하게 하려면 어떻게 해야 할까~?
여기서부터는 거의 새롭게 배워가며 코드를 작성하게 되었는데요.
그래서 말이 좀 많아질 예정!! :>
차근차근 정리해볼게요!
3. ViewController를 TableView의 DataSource와 Delegate로 설정하기
우선 DataSource
와 Delegate
에 대해서 간단히 정리해볼게요!
Delegate와 DataSource는 둘 다 프로토콜로 사용되는데요!
DataSource
는 데이터를 제공해 UI 요소가 표시되는 방법을 제어하는 패턴
입니다! (그리기)
Delegate
는 객체 간의 상호 작용을 위임하는 패턴이에요. (동작)
둘 다 프로토콜을 통해 구현되기 때문에 일반적으로
"Delegate 프로토콜"과 "DataSource 프로토콜"이라고 부른다고 해요!
둘 다 ViewController의 일을 나눠서 처리해주는데요.
이게 무슨 말이냐~~?
Swift는 객체 지향 프로그래밍 언어잖아요?!
Delegate와 Datasource는 객체 지향 프로그래밍에서
하나의 객체가 모든 데이터 처리를 담당하는 것이 아니라 여러 객체로 일을 분산시켜 더 효율적으로 데이터를 관리하고 표시하도록 해주는 거예요!
객체 1이 해야 할 일을 객체 2에게 넘겨서 처리하도록 하는 거죠!
쉽게 말하자면 일 나누기 !!! 입니다.
아 뭐 내가 해야 할 일을 떠넘기겠다!! 는 당연히 아니고요.
일을 나눠서 하면 더 유연하게할 수 있고, 정리가 잘 되잖아요? 그런 느낌이에요.
“하나의 큰 프로젝트를 위해 부서를 나눠서 각자의 일을 할 수 있게 한다.”
정도로 정리하면 좋겠네요 :>
자 이걸 우리의 ToDoList 어플 만들기로 돌아와서 적용시키면
큰 프로젝트 = ViewController
일 할 부서 = tableView
라고 생각할 수 있겠네요!
이렇게 Delegate와 Datasource를 사용하면 코드가 잘 나누어서 정리되어 있으니까 보기에도 쉽고, 유지 보수하기에도 용이하겠죠?!
다시 한 번 간단히 정리하고 ToDoList 만들기에 적용시켜 볼게요.
DataSource
DataSource는 데이터를 받아서 “뷰를 그려주는 역할”을 합니다.
extension ViewController: UITableViewDataSource {
//실행할 코드
}
numberOfRowsInSection = 각 세션에 표시할 행의 개수를 묻는 메서드 필수
cellForRowAt = 특정 인덱스 Row의 Cell에 대한 정보를 넣어 Cell을 반환하는 메서드 필수
numberOfSections = 총 섹션 개수를 묻는 메서드
titleForHeaderInSection = 특정 섹션의 헤더 타이틀을 묻는 메서드
titleForFooterInSection = 특정 섹션의 풋터 타이틀을 묻는 메서드
canEditRowAt = 특정 위치의 행이 편집 가능한지 묻는 메서드
canMoveRowAt = 특정 위치의 행을 재정렬할 수 있는지 묻는 메서드
sectionIndexTitles = Table view 섹션 인덱스 타이틀을 묻는 메서드
sectionForSectionIndexTitle = 인덱스에 해당하는 섹션을 알려주는 메서드
commit forRowAt = 스와이프 모드, 편집 모드에서 버튼을 선택하면 호출되는 메서드
moveRowAt = 행이 다른 위치로 이동되면 어디에서 어디로 이동했는지 알려주는 메서드
(출처: https://jiwift.tistory.com/entry/iOSSwift-TableView-Delegate-DataSource-메서드-정리)
이렇게 다양한 메서드들을 사용해서 뷰를 그려줄 수 있는데요.
이 중에서 numberOfRowsInSection 및 cellForRowAt 메서드를 사용해
테이블뷰의 행 수 및 셀을 구성했습니다!
extension ViewController: UITableViewDataSource {
// numberOfRowInSection: 테이블뷰의 행 구성
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tasks.count
}
// cellForRowAt: 테이블뷰의 셀 구성
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
cell.textLabel?.text = tasks[indexPath.row]
return cell
}
}
/*
return task.count
해야 할 일의 수를 count해서 그 수에 맞게 행 구성합니다.
*/
/*
style 속성은 UITableViewCell의 스타일을 나타내는데요!
.default 스타일은 텍스트만 포함된 기본 스타일의 셀을 생성해줍니다.
*/
/*
reuseIdentifier는 재사용을 위한 셀 식별자를 나타내는데요,
"Cell"이라고 식별자를 붙여줘서 나중에 다른 곳에서 사용해줄 거에요!
*/
/*
cell.textLabel?.text = tasks[indexPath.row]
tasks[indexPath.row]에서 가져온 데이터를 셀의 텍스트로 설정해준 것!
tasks[indexPath.row]에서는 TableView에 표시할 데이터를 저장해요.
indexPath.row는 현재 행의 인덱스를 나타내고,
이를 사용해 tasks 배열에서 해당 행에 대한 데이터를 가져옵니다!
*/
Delegate
Delegate는 행동에 대한 “동작”을 수행하도록 합니다!
extension ViewController: UITableViewDelegate {
//실행할 코드
}
didSelectRowAt = 행이 선택되었을 때 호출되는 메서드
didDeselectRowAt = 행이 선택 해제되었을 때 호출되는 메서드
heightForRowAt = 특정 위치 행의 높이를 묻는 메서드
viewForHeaderInSection = 지정된 섹션의 헤더 뷰에 표시할 View가 어떤 건지 묻는 메서드
viewForFooterInSection = 푸터뷰에 표시할 View가 어떤 건지 묻는 메서드
heightForHeaderInSection = 지정된 섹션의 헤더 뷰의 높이를 묻는 메서드
heightForFooterInSection = 푸터뷰의 높이를 묻는 메서드
willBeginEditingRowAt = 테이블 뷰가 편집 모드에 들어갔을 때 호출되는 메서드
didEndEditingRowAt = 테이블 뷰가 편집 모드에 들어갔을 때 호출되는 메서드
willDisplay = 테이블 뷰가 셀을 사용하여 행을 그리기 직전에 호출되는 메서드
didEndDisplaying = 테이블 뷰로부터 셀이 화면에 사라지면 호출되는 메서드
(출처: https://jiwift.tistory.com/entry/iOSSwift-TableView-Delegate-DataSource-메서드-정리)
Delegate도 다양한 메서드를 사용해 동작을 수행하도록 하는데요.
그 중에서 deselectRow를 사용해 행의 선택 상태를 해제하는 메서드를 사용했습니다!
extension ViewController: UITableViewDelegate {
// 테이블뷰에서 특정 행이 선택되었을 때, 메서드 호출해 선택 상태 해제
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
}
/*
indexPath 매개변수
TableView의 섹션과 행을 나타내는 IndexPath 객체.
*/
/*
tableView.deselectRow(at:animated:)
메서드를 사용해 사용자가 선택한 행의 '선택 상태를 해제'하고,
tableView.deselectRow(at:animated: true)
animated 매개변수를 true로 설정해서
선택 해제 '애니메이션이 적용'됩니다!
쉽게 풀어서 설명하자면
사용자가 TableView의 특정 행을 탭할 때는
해당 행이 일시적으로 강조되어 선택된 상태로 표시되고,
사용자가 손을 떼는 순간에는 다시 원래의 색으로 돌아오게 되는 것!
을 실행하도록 하는 코드!라고 생각하면 된다~!!
(이 때 선택과 해제는 deselectRow 메서드로,
색이 바뀌는 애니메이션은 animated: true로 구현되는 것!)
*/
하나 더! extension
이 친구는 뭘까~?
extension
확장
기존의 클래스, 구조체, 열거형 또는 프로토콜에 새로운 기능을 추가할 때 사용한다.
즉, 특정 형식을 확장해 외부에서 추가적인 기능을 제공하는 것인데요.
여기서는 이렇게 생각하면 됩니다!
특정 형식 = UIViewController
외부 = extension
추가적인 기능 = UITableViewDataSource와 UITableViewDelegate 프로토콜에 속하는 메서드들
프로토콜마다 extension을 나누지 않고
하나로 합쳐서 표현하고 그 안에 기능을 구현하는 것도 가능해요.
그래서 최최최종은 이렇게 작성할 수 있답니다!
extension ViewController: UITableViewDataSource, UITableViewDelegate {
// UITableViewDataSource 프로토콜의 메서드
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tasks.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
cell.textLabel?.text = tasks[indexPath.row]
return cell
}
// UITableViewDelegate 프로토콜의 메서드
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
}
후하 꽤나 길었죠?! 여기까지 하면 UITableView에 기능 추가해주기는 끝!!
이제 TableView가 실행 화면에 나타나도록 하기 위한 작업을 해줘야 해요 :>
4. viewDidLoad()에서 DataSource 및 Delegate 설정하기
우선 viewDidLoad()에 대한 간단한 설명!
viewDidLoad()
뷰 컨트롤러의 뷰가 메모리에 로드된 직후에 호출되는 초기화 메서드
즉, 뷰가 사용자에게 보여지기 전에 필요한 설정이나 데이터를 미리 준비할 수 있다!
따라서 TableView가 화면에 나타나기 위해서는
viewDidLoad()에서 TableView 속성들을 설정하고,
DataSource와 Delegate를 지정해줘야 해요.
한 번 해볼까요?
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
.resister
을 사용해 tableView에 사용할 cell을 등록해줍니다.
UITableViewCell.self
는 기본적인 테이블뷰 셀을 사용하겠다는 의미입니다.
그리고 이 셀의Identifier
를 “cell”로 지정해 재사용 시 부를 수 있도록 해줍니다.
tableView.dataSource = self
tableView의 데이터 소스를 현재 ViewController로 지정합니다.
tableView.delegate = self
tableView의 델리게이트를 현재 ViewController로 지정합니다.
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.dataSource = self
tableView.delegate = self
}
이렇게 하면 아까 TableView에 담았던 기능들이 viewDidLoad()에서 설정되고, 실행 화면에 뜨게 돼요.
Lv1 구현 이제 거의 끝나가는 중
할 일에 대한 데이터만 구성해주면 됩니다!!!!!!!!
5. 할 일에 대한 데이터 구성
- 데이터를 담을 Todo 라는 구조체를 하나 만들어줄게요.
- 데이터 고유값 id, 제목 title, 그리고 완료 여부 확인 isCompleted 로 데이터를 구성해줍니다.
- 구조체 속성들에 값을 설정해서 객체를 생성할 수 있도록 init으로 초기화 해주기!
//할일에 대한 데이터 구성
struct Todo {
let id: Int
var title: String
var isCompleted: Bool
//Todo 객체를 생성할 때 필요한 데이터(id, title, isCompleted) 받아와 객체의 속성들 초기화
init(id: Int, title: String, isCompleted: Bool) {
self.id = id
self.title = title
self.isCompleted = isCompleted
}
}
6. 아까 만든 기능이 잘 적용 되는지 task.append()를 추가해 확인해주기!
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.dataSource = self
tableView.delegate = self
tasks.append("Task 1")
tasks.append("Task 2")
tasks.append("Task 3")
}
}
위에서 쭉 한 것들을 하나로 작성해주면! Lv1 완성 :> !!!
실행화면
그리고 코드!
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
var tasks = [String]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.dataSource = self
tableView.delegate = self
tasks.append("Task 1")
tasks.append("Task 2")
tasks.append("Task 3")
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
// UITableViewDataSource 프로토콜의 메서드
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tasks.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
cell.textLabel?.text = tasks[indexPath.row]
return cell
}
// UITableViewDelegate 프로토콜의 메서드
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
}
//할일에 대한 데이터 구성
struct Todo {
let id: Int
var title: String
var isCompleted: Bool
//Todo 객체를 생성할 때 필요한 데이터(id, title, isCompleted) 받아와 객체의 속성들 초기화
init(id: Int, title: String, isCompleted: Bool) {
self.id = id
self.title = title
self.isCompleted = isCompleted
}
}
별 거 아닌 것 같은데 굉장히 긴 과정을 거쳤죠…….?
아직 가야할 길이 멀다 멀어. 이제 Lv2로 넘어가 봅시다!!!
귀찮아서 정리 안했는뎈ㅋ 내일 함 봐야겠네여 ㅋㅋ