이번 포스트에서는 간단한 목록을 표시하는 앱을 만들어보겠습니다. 그 전에 중요한 개념 몇가지 짚고 넘어가겠습니다.
Access Control - 코드의 일부에 대한 접근 권한을 제한하는 기능 - 코드의 세부 구현 숨김 -> 인터페이스에만 접근 가능 -> 보안성 향상 - 모듈화, 캡슐화 촉진 -> Java의 Public, Private 등
- Access Modifier
Open: 클래스와 클래스 멤버에 대해서만 사용할 수 있으며, 가장 높은 수준의 접근성을 보유 다른 모듈에서 상속 및 Override가 가능
Public:Open과 유사하지만, 다른 모듈에서 상속이나 Override 불가능 함수, 변수 등 대부분의 타입에 사용할 수 있습니다.
Internal: 기본 액세스 수준 같은 모듈 내에서만 접근할 수 있으며, 모듈 외부에서는 접근 불가능 모듈 단위의 프로젝트를 개발할 때 유용합니다.
File-private: 정의된 소스 파일 내에서만 사용가능 소스 파일 내의 다른 타입이나 함수 등에서 접근 O, 파일의 외부에서는 접근 X
Private: 가장 제한적인 액세스 수준 선언이 포함된 블록(클래스, 구조체, 열거형, 확장 등) 내에서만 사용 가능. 가장 좁은 범위에서의 캡슐화를 제공
- Default Modifier Swift에서는 특정 액세스 수준을 명시적으로 표시하지 않으면, 기본적으로 internal 액세스 수준이 적용됩니다. 위에서 설명드린 것 처럼 해당 요소가 같은 모듈 내에서는 자유롭게 접근할 수 있지만, 모듈 외부에서는 접근할 수 없음을 의미합니다.
open class OpenClass { // 다른 모듈에서 상속 및 오버라이드 가능
public var publicVar = 0 // 모듈 내외부에서 접근 가능, 하지만 오버라이드 불가능
internal func internalMethod() {} // 같은 모듈 내에서만 사용 가능
fileprivate func filePrivateMethod() {} // 같은 파일 내에서만 사용 가능
private func privateMethod() {} // 정의된 블록 내에서만 사용 가능
}
class InternalClass {} // 기본적으로 internal 액세스 수준이 적용됨
상속(Override) 상속을 통해 기존 클래스의 코드를 재사용하고 확장이 가능 합니다. 모든 클래스는 자신이 초기화될 때 모든 저장 프로퍼티(Stored Property)에 대해 적절한 초기 값을 할당해야 하는데 이 과정에서 초기화 메서드(init)를 사용하게 됩니다.
super, init - super 1. 부모 클래스를 참조하는 데 사용 2. 클래스 상속 구조 내에서 자식 클래스는 super를 사용하여 부모 클래스의 메서드, 프로퍼티 등에 접근 가능 3. 부모 클래스의 원본 구현을 호출, 부모 클래스의 프로퍼티 값을 사용할 때 유용
class Parent {
func sayHello() {
print("안녕하세요")
}
}
class Child: Parent {
//override를 사용하여 부모 클래스를 가져옴
override func sayHello() {
super.sayHello() // 부모 클래스의 sayHello 메서드 호출
print("반갑습니다")
}
}
- init 1. 클래스, 구조체 또는 열거형의 인스턴스가 생성될 때 호출되는 메서드
2. 해당 타입의 새 인스턴스가 사용 준비가 되도록 초기 상태를 설정 3. 모든 저장 프로퍼티는 초기값을 할당해야 하며, 이 과정에서 init 메서드가 중요한 역할
4. init 메서드는 파라미터를 가질 수 있으며, 이를 통해 인스턴스 생성 시 초기값을 제공 가능
class Person {
var name: String
var age: Int
// 초기화 메서드, 인스턴스의 초기 상태 설정
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
let person = Person(name: "홍길동", age: 20) // Person 인스턴스 생성 시 초기값 제공
Delegate, Protocol, Adopt - Delegate 객체가 일부 작업을 다른 객체에게 위임(delegate)하는 디자인 패턴 -> 코드의 재사용성을 상승, 객체 간의 결합도 낮춤 -> 즉, 한 객체가 모든 일을 혼자 처리하는 대신, 특정 역할이나 행동을 다 른 객체에 맡김으로써 더 유연하고 관리하기 쉬운 코드 생성 가능
UIComponent
- Protocol 특정 클래스와 관련없는 Property, Method 선언 집합 -> 클래스, 구조체, 열거형은 이 프로토콜을 채택(Adopt)하여 요구사항을 구현 -> 프로토콜 자체는 기능을 구현하지 않고, 구현해야 할 요구사항만을 정의
- Adopt 클래스, 구조체, 열거형이 특정 프로토콜에 정의된 요구사항을 충족시키겠다고 선언하는 과정 -> Protocol에는 메서드, 프로퍼티, 그리고 다른 요구사항들이 정의될 수 있으며, 이를 채택한 타입은 이러한 요구사항들을 모두 구현해야 함. -> 장점 - 코드의 재사용성 향상 - 타입의 유연성 증가 - 확장성 개선
상속과 채택의 차이
섹션, 행, 열 - 섹션 : 테이블 그룹 - 행 : 섹션 내에서 각각의 한 칸 - 열 : 섹션 내에서 각각의 한 칸
목록 Application 만들기 - 프로젝트 생성 -> Main -> Shift + Cmd + L -> Table View Controller 추가 * TableViewController : TableView + Table View Cell
- Add New Constraints : 각 I-bar 클릭 -> 점선에서 실선으로 바뀜 -> 입력한 수치만큼 각 모서리로부터 멀어짐 * Safe Area : 상, 하단을 보면 0으로 지정했음에도 꽉 채워지지 않는 것을 확인할 수 있음 iOS 디바이스의 노치, 홈 버튼, 화면 모서리와 같은 특정 영역을 제외한, 콘텐츠를 안전하게 표시할 수 있는 영역 TableViewController, Constraints
아래는 ViewController에 TableView를 Outlet으로 잡아놓은 코드입니다.
import UIKit
class ViewController: UIViewController {
//table에 초기값이 없기 때문에 Optional(!)로 사용
@IBOutlet weak var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
}
아래 코드는 1개의 섹션에 5개의 행을 사용하여 이름을 5번 출력하는 코드입니다.
import UIKit
// UIViewController를 상속받는 ViewController 클래스 정의
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// UITableView의 IBOutlet 연결. Optional(!) 사용하여 초기값이 없는 상태로 선언.
@IBOutlet weak var table: UITableView!
// 테이블 뷰의 섹션당 행의 개수를 설정하는 메서드
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// 섹션당 행의 개수를 5로 설정
return 5
}
// 각 행에 대한 셀을 설정하는 메서드
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// 재사용 가능한 테이블 뷰 셀을 생성하거나 새로운 셀을 생성
let cell = UITableViewCell(style: .default, reuseIdentifier: "my cell")
// 셀의 텍스트 레이블에 "Taram" 문자열을 설정. 옵셔널 체이닝 사용.
cell.textLabel?.text = "Taram"
// 구성된 셀을 반환
return cell
}
// 뷰 컨트롤러의 뷰가 메모리에 로드된 후 호출되는 메서드
override func viewDidLoad() {
super.viewDidLoad()
// UITableView의 delegate와 dataSource를 현재 ViewController 인스턴스로 설정
table.delegate = self
table.dataSource = self
}
}
1. UITableView와 ViewController 연결하기
- Storyboard를 사용해 UITableView를 뷰 컨트롤러에 추가하고, @IBOutlet을 통해 코드와 연결 -> Table이란 이름으로 연결
2. UITableViewDelegate와 UITableViewDataSource 채택
- UITableView를 사용하기 위해서는 UITableViewDelegate와 UITableViewDataSource 프로토콜을 채택해야 함. -> 테이블 뷰의 동작과 데이터 관리 방법을 정의
numberOfRowsInSection: 테이블 뷰의 각 섹션에 몇 개의 행이 있는지 알려줌. -> 모든 섹션에 5개의 행으로 설정
cellForRowAt: 각 행에 대한 셀을 구성 -> 각 셀에 "Taram"이라는 텍스트를 표시하도록 설정
3. Delegate와 DataSource 설정
- viewDidLoad 메소드 안에서 UITableView의 delegate와 dataSource를 self로 설정 -> 현재 뷰 컨트롤러에서 위에서 언급한 프로토콜 메소드들을 처리 가능
결과 화면
위 코드를 수정하면서 점점 바꿔보겠습니다. 이번에는 인덱스 번호를 출력하는데 3개의 섹션에 5개의 행으로 구성해볼게요.
import UIKit
// UIViewController를 상속받고 UITableViewDelegate, UITableViewDataSource 프로토콜을 채택하는 ViewController 클래스 선언
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// Interface Builder에서 연결된 UITableView의 참조. 초기값이 없어 옵셔널(!)로 선언됨
@IBOutlet weak var table: UITableView!
// 테이블 뷰의 각 섹션에 표시될 행의 수를 반환하는 메소드. 여기서는 5를 반환
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
// 각 행에 대한 셀을 구성하는 메소드. 셀의 텍스트 라벨에 현재 행의 인덱스 번호를 표시
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: "my cell")
cell.textLabel?.text = "\(indexPath.row)" // indexPath.row는 행의 인덱스 번호, 0부터 시작
return cell
}
// 테이블 뷰의 섹션 수를 반환하는 메소드. 여기서는 3을 반환
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
// 뷰 컨트롤러의 뷰가 메모리에 로드된 후 호출되는 메소드. 테이블 뷰의 delegate와 dataSource를 self로 설정
override func viewDidLoad() {
super.viewDidLoad()
table.delegate = self
table.dataSource = self
}
}
1. UITableView 설정하기
Delegate와 DataSource 설정하기 - UITableView의 동작을 관리하기 위해, ViewController에서 UITableViewDelegate와 UITableViewDataSource 프로토콜을 채택 - viewDidLoad 메소드에서 이 테이블 뷰의 delegate와 dataSource를 self로 설정
2. UITableView 구현하기
섹션과 행 설정하기 - numberOfSections(in tableView:) 메소드를 통해 섹션의 수를 3으로 설정 - tableView(_:numberOfRowsInSection:) 메소드를 통해 각 섹션 내의 행의 수는 5로 설정
셀 구성하기 - tableView(_:cellForRowAt:) 메소드에서는 각 행의 셀을 구성 - 각 셀의 텍스트 라벨에 해당 행의 인덱스 번호를 표시하도록 설정 결과 화면
이번엔 각 섹션에는 5개의 행(row) 과 그 행의 인덱스 번호와 설명, 그리고 이미지가 표시되게 해보겠습니다.
import UIKit
// 이미지 파일명을 저장한 배열
var image = ["1.png", "2.png", "3.png", "4.png", "5.png"]
// UIViewController를 상속받고 UITableViewDelegate, UITableViewDataSource 프로토콜을 채택하는 ViewController 클래스 선언
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// Interface Builder에서 연결된 UITableView의 참조. 초기값이 없어 옵셔널(!)로 선언됨
@IBOutlet weak var table: UITableView!
// 테이블 뷰의 각 섹션에 표시될 행의 수를 반환하는 메소드. 여기서는 5를 반환
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
// 각 행에 대한 셀을 구성하는 메소드. 셀에는 텍스트 라벨, 세부 텍스트 라벨, 이미지 뷰가 포함됨
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "my cell")
cell.textLabel?.text = "\(indexPath.row)" // indexPath.row는 행의 인덱스 번호, 0부터 시작
cell.detailTextLabel?.text = indexPath.description // 세부 텍스트 라벨에 indexPath의 설명을 표시
cell.imageView?.image = UIImage(named: image[indexPath.row]) // 이미지 뷰에 배열에서 해당 인덱스의 이미지를 표시
return cell
}
// 테이블 뷰의 섹션 수를 반환하는 메소드. 여기서는 3을 반환
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
// 뷰 컨트롤러의 뷰가 메모리에 로드된 후 호출되는 메소드. 테이블 뷰의 delegate와 dataSource를 self로 설정
override func viewDidLoad() {
super.viewDidLoad()
table.delegate = self
table.dataSource = self
}
}
1. 기본 설정
- 이미지 파일의 이름을 String 배열로 선언 -> 5개의 이미지 파일 이름을 배열에 저장
2. UITableView 구현하기
- Delegate와 DataSource 설정 1) UITableView의 기능 제어 -> ViewController에서 UITableViewDelegate와 UITableViewDataSource 프로토콜을 채택 -> viewDidLoad에서 이를 self로 설정 - 섹션과 행 설정 1) numberOfSections(in tableView:) 메소드를 통해 섹션의 수를 3으로 설정 2) tableView(_:numberOfRowsInSection:) 메소드를 통해각 섹션 내의 행의 수는5로 설정
- 셀 구성하기 1) tableView(_:cellForRowAt:) 메소드 -> UITableViewCell의 style을 .subtitle로 설정, 주 제목과 부 제목을 모두 사용 2) 주 제목(textLabel)에는 각 행의 인덱스 번호를, 부 제목(detailTextLabel)에는 indexPath의 설명을 설정 -> imageView에는 미리 정의된 이미지 배열에서 해당 인덱스에 맞는 이미지를 표시
결과 화면
마지막으로 맛집의 이름, 전화번호, 이미지를 표시하도록 바꿔보겠습니다.
import UIKit
// 맛집의 이미지 파일 이름을 저장하는 String 배열
var image = ["1.png", "2.png", "3.png", "4.png", "5.png"]
// 맛집의 이름을 저장하는 String 배열
var name = ["맛집1", "맛집2", "맛집3", "맛집4", "맛집5"]
// 맛집의 전화번호를 저장하는 String 배열
var phone = ["010-1234-5678", "010-3234-7855", "010-8974-6212", "010-7512-6325", "010-4121-8542"]
// UIViewController를 상속받고 UITableViewDelegate, UITableViewDataSource 프로토콜을 채택하는 ViewController 클래스 선언
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// Interface Builder에서 연결된 UITableView의 참조. 초기값이 없어 옵셔널(!)로 선언됨
@IBOutlet weak var table: UITableView!
// 테이블 뷰의 각 섹션에 표시될 행의 수를 반환하는 메소드. 여기서는 5를 반환
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
// 각 행에 대한 셀을 구성하는 메소드. 셀에 맛집의 이름, 전화번호, 그리고 이미지를 설정
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "my cell")
cell.textLabel?.text = name[indexPath.row] // 셀의 주 제목으로 맛집의 이름 설정
cell.detailTextLabel?.text = phone[indexPath.row] // 셀의 부 제목으로 맛집의 전화번호 설정
cell.imageView?.image = UIImage(named: image[indexPath.row]) // 셀의 이미지 뷰에 맛집의 이미지 설정
return cell
}
// 테이블 뷰의 섹션 수를 반환하는 메소드. 여기서는 3을 반환
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
// 뷰 컨트롤러의 뷰가 메모리에 로드된 후 호출되는 메소드. 테이블 뷰의 delegate와 dataSource를 self로 설정
override func viewDidLoad() {
super.viewDidLoad()
table.delegate = self
table.dataSource = self
}
}
UITableView 구현하기
셀 구성하기: 1) tableView(_:cellForRowAt:) 메소드를 통해 각 행에 대한 셀을 구성 2) 표준 셀 스타일인 .subtitle을 사용 3) 셀의 주 제목에는 맛집의 이름, 부 제목에는 맛집의 전화번호, 그리고 셀의 이미지 뷰에는 맛집의 이미지를 설정 4) 각 값은 indexPath.row를 사용하여 배열에서 적절한 데이터를 찾아 할당 indexPath.row는 현재 셀의 행 번호를 나타내므로, 이를 통해 각 행에 맞는 데이터를 배열에서 가져오기 가능
섹션 수 설정하기 1) numberOfSections(in:) 메소드를 사용하여 테이블 뷰에 표시할 섹션의 수를 설정 -> 총 3개의 섹션을 사용 2) 실제 앱에서는 섹션을 통해 데이터를 그룹화하거나 다른 유형의 정보를 분리하여 표시 가능
행의 수 설정하기: 1) tableView(_:numberOfRowsInSection:) 메소드를 통해 각 섹션에 표시할 행의 수를 결정 -> 각 섹션에 5개의 행을 표시 결과 화면