본문 바로가기

iOS

iOS_3grade - 6주차

protocol을 채택하면 필요한 부분을 conform 해야함. - 시험 출제

 

didSelectRowAt : 실행 후 셀들을 선택할 때 어떤 셀을 선택했는지를 배열로 보여줌

 

cellForRowAt : 실행 후 여러 셀들을 볼때 보여지는 셀들을 계속 만드는 역할을 함

주변 cell을 자동으로 생성한다 그러나 다 생성하지는 않는다 내가 보고있는거 주변만 출력이 됨

 

1. 전역 변수

var name = ["학식","스벅","초밥","분식","중식"]
  • 테이블뷰에 표시할 데이터 배열
  • 각 셀에 들어갈 텍스트라고 보면 됨
  • 총 5개 → 행(row) 개수와 연결됨

2. ViewController 선언

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource

의미

  • UIViewController: 화면 하나를 관리
  • UITableViewDataSource: 테이블에 데이터를 공급
  • UITableViewDelegate: 테이블의 동작(클릭 등)을 처리

👉 즉, 이 클래스가 테이블의 데이터 + 이벤트를 모두 담당


3. IBOutlet

@IBOutlet weak var table: UITableView!
  • 스토리보드에 있는 TableView와 코드 연결
  • 이걸 통해 코드에서 테이블을 제어 가능

4. 행(row) 개수 설정

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 5
}
  • 각 섹션(section)에 들어갈 행 개수
  • 지금은 무조건 5개
  • 사실 name.count로 하는 게 더 좋음

👉 추천:

return name.count

5. 셀 생성 및 데이터 넣기 ⭐ (핵심)

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell
    cell.myLabel.text = name[indexPath.row]
    print("c=\(indexPath.description)")
    return cell
}

하나씩 설명

1) 셀 재사용

dequeueReusableCell(withIdentifier: "myCell")
  • 셀을 계속 새로 만들지 않고 재사용 → 성능 좋음

2) 타입 캐스팅

as! MyTableViewCell
  • 기본 셀 → 커스텀 셀로 변환
  • MyTableViewCell 안에 myLabel이 있기 때문

3) 데이터 넣기

cell.myLabel.text = name[indexPath.row]
  • 배열의 값을 셀에 넣음
  • indexPath.row = 몇 번째 행인지

👉 결과:

0 → 학식
1 → 스벅
2 → 초밥
...

4) 로그 출력

print("c=\(indexPath.description)")
  • 셀이 생성될 때마다 위치 출력

6. 셀 클릭 이벤트

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    print(indexPath.description)
}
  • 셀 클릭하면 실행됨
  • 어떤 셀이 눌렸는지 출력

👉 예:

[0, 2] → 0번째 섹션, 2번째 행

7. 섹션 개수

func numberOfSections(in tableView: UITableView) -> Int {
    return 6
}
  • 테이블을 6개의 섹션으로 나눔

👉 구조:

섹션 0 → 행 5개
섹션 1 → 행 5개
...
섹션 5 → 행 5개

⚠️ 중요한 점:

  • 모든 섹션이 같은 name 배열을 사용함 → 내용이 반복됨

8. viewDidLoad

override func viewDidLoad() {
    super.viewDidLoad()
    table.dataSource = self
    table.delegate = self
}

역할

  • 테이블에게 "데이터랑 이벤트는 내가 처리할게!"라고 설정

전체 구조 정리

이 코드의 테이블 모습:

  • 섹션: 6개
  • 각 섹션:
    • 행: 5개
    • 내용: 학식, 스벅, 초밥, 분식, 중식 (반복)

👍 개선하면 좋은 점

1. 하드코딩 제거

return name.count

2. 섹션별 다른 데이터 쓰기

지금은 모든 섹션이 똑같음 → 보통은 다르게 구성


한 줄 요약

👉 "테이블뷰에 6개의 섹션을 만들고, 각 섹션마다 5개의 음식 데이터를 표시하고, 클릭하면 위치를 출력하는 코드"


//
//  ViewController.swift
//  table1
//
//  Created by 소프트웨어컴퓨터 on 2026/04/07.
//

import UIKit

var name = ["학식","스벅","초밥","분식","중식"]

class ViewController: UIViewController, UITableViewDelegate,UITableViewDataSource {
    
    @IBOutlet weak var table: UITableView!
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 5
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "myCell",for: indexPath) as! MyTableViewCell
        cell.myLabel.text = name[indexPath.row]
        print("c=\(indexPath.description)")
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print(indexPath.description)
    }
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return 6
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        table.dataSource = self
        table.delegate = self
    }


}

 


"옵셔널 체이닝"

 


1. 옵셔널 + 언래핑 방식 전체 예제

var x: String? = "Hi"   // 옵셔널: 값이 있을 수도(nil 가능)

// ❌ 바로 사용 불가
// print(x.count)

// 1) 강제 언래핑
let a = x!.count        // x가 nil 아니라고 확신할 때만 사용
                        // nil이면 바로 크래시

// 2) 옵셔널 바인딩
if let value = x {      // x가 nil 아니면 value에 값 들어감
    print(value.count)  // 안전하게 사용
}                       // nil이면 이 블록 자체 실행 안됨

// 3) nil 병합 연산자
let b = x ?? ""         // x가 nil이면 "" 사용

// 4) 옵셔널 체이닝
let c = x?.count        // x가 nil이면 c = nil
                        // 값 있으면 count 실행
                        // 결과는 항상 Optional(Int?)

2. UILabel (옵셔널 체이닝 필요한 이유)

let cell = UITableViewCell()

cell.textLabel?.text = "Hello"
// textLabel 타입: UILabel? (옵셔널)
// → nil일 수도 있어서 ?. 사용
// → nil이면 아무 일도 안 일어남
// → 값 있으면 text에 접근해서 값 넣음

cell.textLabel!.text = "Hello"
// 강제 언래핑
// textLabel이 nil이면 → 크래시

3. 옵셔널 체이닝 동작 방식

var x: String? = "Hi"

let result = x?.count
// 실행 순서:
// 1. x가 nil인지 확인
// 2. nil이면 → result = nil
// 3. 값 있으면 → count 실행
// 4. 결과 타입은 Int? (무조건 Optional)

print(result) // Optional(2)

4. Person 예제 (옵셔널 접근)

class Person {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

let han: Person? = Person(name: "Han", age: 25)

// print(han.age)   // ❌ 에러 (옵셔널이라 직접 접근 불가)

print(han?.age)     // ✅ 옵셔널 체이닝
                    // 값 있으면 → Optional(25)
                    // nil이면 → nil

5. 중첩 if vs 옵셔널 체이닝

class Facebook {
    var account: String = "aaa@bbb.com"
}

class SNS {
    var fb: Facebook? = Facebook()
}

class Person {
    var sns: SNS? = SNS()
}

let p = Person()

// ❌ 기존 방식 (if 중첩)
if let s = p.sns {
    if let f = s.fb {
        print(f.account)
    }
}

// ✅ 옵셔널 체이닝
let account = p.sns?.fb?.account
// 중간에 하나라도 nil이면 → 전체 결과 nil
// 결과 타입은 String?

if let acc = account {
    print(acc)   // 값 있을 때만 출력
}

6. Company 예제 (실전 사용)

class Company {
    var ceo: Person?   // 옵셔널
}

class Person {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

let apple = Company()
apple.ceo = Person(name: "Kim")

// print(apple.ceo.name)   // ❌ 에러 (옵셔널 접근)

// 1) 옵셔널 체이닝
print(apple.ceo?.name)    // Optional("Kim")

// 2) 옵셔널 바인딩
if let ceo = apple.ceo {
    print(ceo.name)       // Kim
}

// 3) 기본값 처리
print(apple.ceo?.name ?? "CEO 없음")
// ceo nil이면 → "CEO 없음"

7. 핵심 정리 (코드로)

obj?.property

// obj가 nil → 결과 nil
// obj가 값 있음 → property 접근
// 결과는 항상 Optional


"오류 제어"


1. throwing 함수란

  • throws 붙은 함수 = 실행 중 에러를 던질 수 있음
  • 그냥 호출 못함 → 반드시 try 붙여야 함

👉 안 붙이면 컴파일 에러
Call can throw, but it is not marked with 'try'


2. do-try-catch 역할

  • try → 에러 발생 가능성 표시
  • do → 실행 블록
  • catch → 에러 발생 시 처리

흐름

  1. try 실행
  2. 성공 → do 내부 계속 실행
  3. 실패 → 즉시 catch로 이동

3. catch 여러 개

  • 여러 에러를 나눠서 처리 가능
  • 위에서부터 순서대로 검사
  • 마지막 catch는 보통 모든 에러 처리용

4. try 3종류 (핵심)

1) try

  • 기본 형태
  • 반드시 do-catch 안에서 사용
  • 에러 직접 처리

2) try?

  • 에러 나면 → nil 반환
  • 에러를 무시하고 싶을 때 사용
  • 결과는 Optional

3) try!

  • 에러 나면 → 바로 크래시
  • 절대 실패 안 한다고 확신할 때만 사용

5. 언제 뭐 쓰냐

  • 에러 처리 필요 → do + try + catch
  • 간단하게 무시 → try?
  • 100% 성공 확신 → try!

6. 핵심 한 줄 정리

👉 throws 함수는 무조건 try로 호출해야 하고, 처리 방법을 선택해야 한다

  • 직접 처리 → do-catch
  • 무시 → try?
  • 강제 → try!

 


"제네릭"


1. 제네릭이란

  • 타입을 나중에 정하는 방식
  • 같은 로직을 여러 타입에 재사용 가능
  • <T> 같은 형태로 사용

👉 핵심:
"타입만 바뀌는 코드 줄이기"


2. 제네릭 함수

// 제네릭 함수 선언
func myPrint<T>(a: T, b: T) {
    print(b, a)   // 타입 T는 호출 시 결정됨
}

// 사용 예
myPrint(a: 1, b: 2)         
// T = Int로 결정

myPrint(a: 2.5, b: 3.5)     
// T = Double로 결정

myPrint(a: "Hi", b: "Hello")
// T = String로 결정

// 주의:
// a와 b는 같은 타입이어야 함 (둘 다 T)
// myPrint(a: 1, b: "Hi") → ❌ 타입 다르면 에러

3. 제네릭 안 쓰면 문제

// 제네릭 없이 만들면 타입마다 함수 필요

func myPrintInt(a: Int, b: Int) {
    print(b, a)
}

func myPrintDouble(a: Double, b: Double) {
    print(b, a)
}

// 타입마다 함수 계속 만들어야 함 → 비효율

4. 제네릭 클래스

// 일반 클래스 (Int 전용)
class Box {
    var item: Int
    
    init(item: Int) {
        self.item = item
    }
    
    func getItem() -> Int {
        return item
    }
}

// 제네릭 클래스
class BoxGeneric<T> {
    var item: T          // 어떤 타입이든 가능
    
    init(item: T) {
        self.item = item
    }
    
    func getItem() -> T {
        return item
    }
}

// 사용 예
let intBox = BoxGeneric(item: 10)      
// T = Int

let stringBox = BoxGeneric(item: "Hi") 
// T = String

print(intBox.getItem())    // 10
print(stringBox.getItem()) // Hi

5. 타입 추론 (중요)

let box = BoxGeneric(item: 123)
// 실제로는 BoxGeneric<Int>
// 하지만 Swift가 자동으로 타입 추론해서 <Int> 생략 가능

6. Array도 제네릭

var a: [Int] = [1, 2, 3]
// 실제 형태 → Array<Int>

var b: Array<Int> = [1, 2, 3]
// 위랑 동일

var c: Array<Double> = [1.1, 2.2]
// Double 타입 배열

// 즉, Array도 내부적으로
// struct Array<Element> 형태의 제네릭 구조체

7. 핵심 정리

  • <T> = 타입 자리
  • 호출할 때 실제 타입 결정됨
  • 같은 로직을 여러 타입에서 재사용 가능
  • Swift 기본 타입(Array 등)도 제네릭으로 구현됨


"Array"

 

// 배열 선언 방법 3가지

let number = [1, 2, 3, 4]
// 값으로 타입 추론됨 → [Int]
// 즉 number의 타입은 Array<Int>

let odd: [Int] = [1, 3, 5]
// 축약형 문법
// [Int] = Int 타입 배열

let even: Array<Int> = [2, 4, 6]
// 정식 제네릭 문법
// Array<Int> == [Int] (완전히 동일한 의미)

// 타입 확인
print(type(of: number)) // Array<Int>
print(number)           // [1, 2, 3, 4]

print(type(of: odd))    // Array<Int>
print(odd)              // [1, 3, 5]

print(type(of: even))   // Array<Int>
print(even)             // [2, 4, 6]

// 문자열 배열
let animal = ["dog", "cat", "cow"]
// 값이 String이므로 자동으로 [String]으로 추론됨

print(type(of: animal)) // Array<String>
print(animal)           // ["dog", "cat", "cow"]

// 핵심 정리:
// 1. 배열은 같은 타입만 저장 가능
// 2. [Int] == Array<Int>
// 3. 값 넣으면 타입 자동 추론됨

 

// 빈 배열 선언 방법 3가지

var number: [Int] = []
// Int 타입 배열인데 현재 비어 있음

var odd = [Int]()
// 생성자 방식 → 빈 Int 배열

var even: Array<Int> = Array()
// 정식 제네릭 생성 방식

print(number) // []
print(odd)    // []
print(even)   // []

// 왜 타입을 써야 하냐?
// [] 만 쓰면 Swift는 어떤 타입 배열인지 모름
// 그래서 [Int], [String] 등 타입을 명시해줘야 함

 

let number: [Int] = []
// let으로 빈 배열 생성 가능하지만

print(number) // []

// print(number[0])
// ❌ 에러
// 이유: 배열이 비어 있어서 0번 인덱스 자체가 없음

// number.append(100)
// ❌ 에러
// 이유: let은 불변(immutable) → 수정 불가

// ----------------------------------

var arr: [Int] = []
// var로 선언하면 변경 가능

// arr[0] = 1
// ❌ 에러
// 이유: 아직 0번 칸이 존재하지 않음

arr.append(1)
// 배열에 값 추가 → 이제 0번 인덱스 생성됨

print(arr) // [1]

arr[0] = 10
// 이제 0번 칸이 존재하므로 수정 가능

print(arr) // [10]

// 핵심 정리:
// 1. 빈 배열은 인덱스 접근 불가
// 2. 먼저 append로 값을 넣어야 함
// 3. let 배열은 수정 자체가 불가능

 

var number: [Int] = []

// number[0] = 1
// ❌ crash → 배열에 칸이 없음

number.append(1)
// 이제 배열 = [1]

print(number) // [1]

number[0] = 10
// 값 수정 가능

print(number) // [10]

// 핵심:
// 배열은 자동으로 공간 생성 안 해줌
// 무조건 append → 그 다음 index 접근

 

// 같은 값을 여러 개 넣어서 배열 생성

var x = [0, 0, 0, 0, 0]
// 직접 작성한 방식

print(x) // [0, 0, 0, 0, 0]

// ----------------------------------

var x1 = Array(repeating: 0, count: 5)
// 0을 5개 넣은 배열 생성

print(x1) // [0, 0, 0, 0, 0]

// ----------------------------------

var x2 = [Int](repeating: 1, count: 3)
// Int 배열에 1을 3개

print(x2) // [1, 1, 1]

// ----------------------------------

var x3 = [String](repeating: "A", count: 4)
// String 배열에 "A"를 4개

print(x3) // ["A", "A", "A", "A"]

// ----------------------------------

// 추가 이해 포인트

let a = Array(repeating: 5, count: 4)
// [5, 5, 5, 5]

let b = Array(repeating: 1, count: 0)
// count = 0이면 빈 배열 []

// 타입은 repeating 값으로 결정됨
// Int 넣으면 [Int], String 넣으면 [String]

// 핵심:
// 반복 값 + 개수로 배열을 빠르게 생성
// 초기값 세팅할 때 매우 많이 사용됨

 

 


1. for-in으로 배열 접근

let colors = ["red", "green", "blue"]
// String 배열

print(colors) // ["red", "green", "blue"]

for color in colors {
    print(color)
    // 배열의 각 요소를 하나씩 꺼내서 반복
    // color = "red" → "green" → "blue"
}

// 핵심:
// for-in은 배열의 값을 직접 꺼내서 반복할 때 사용
// 인덱스 없이 "값" 자체를 다룰 때 가장 간단한 방식

2. count / isEmpty

let num = [1, 2, 3, 4]
var x = [Int]() // 빈 배열

print(num.isEmpty) // false → 값 있음
print(x.isEmpty)   // true  → 비어 있음

if num.isEmpty {
    print("비어 있습니다")
} else {
    print(num.count)
    // 배열 요소 개수 출력 → 4
}

// 핵심:
// isEmpty → 비었는지 (Bool)
// count → 요소 개수 (Int)
// 배열뿐 아니라 String, Dictionary에도 동일하게 존재

3. first / last

let num = [1, 2, 3, 4]
let num1 = [Int]() // 빈 배열

print(num.first, num.last)
// Optional(1), Optional(4)
// first, last는 Optional 반환 (빈 배열 대비)

print(num1.first, num1.last)
// nil, nil (빈 배열이므로 값 없음)

if let f = num.first, let l = num.last {
    print(f, l) // 1 4
}

// 핵심:
// first, last는 Optional
// 배열이 비어있으면 nil 반환

4. subscript (인덱스로 접근)

var num = [1, 2, 3, 4]

print(num[0], num[3]) // 1 4
// 인덱스로 접근 (0부터 시작)

print(num.first!) 
// first는 Optional이므로 !로 강제 언래핑

for i in 0..<num.count {
    print(num[i])
    // 인덱스를 이용한 반복
}

// 슬라이스 (부분 배열)
print(num[1...2]) 
// [2, 3]

// 범위 교체
num[0...2] = [10, 20, 30]
// 앞 3개 요소를 새로운 값으로 교체

print(num) // [10, 20, 30, 4]

// 핵심:
// num[i] → 인덱스 접근
// 범위로 접근하면 부분 배열 가능
// 범위 대입으로 여러 값 한 번에 변경 가능

5. 배열 추가 / 제거

var num = [1, 2, 3]

print(num) // [1, 2, 3]

// 값 하나 추가
num.append(4)
print(num) // [1, 2, 3, 4]

// 여러 개 추가
num.append(contentsOf: [6, 7, 8])
print(num) // [1, 2, 3, 4, 6, 7, 8]

// 특정 위치에 삽입
num.insert(5, at: 4)
print(num) // [1, 2, 3, 4, 5, 6, 7, 8]

// 특정 위치 제거
num.remove(at: 3)
print(num) // [1, 2, 3, 5, 6, 7, 8]

// 마지막 요소 제거
num.removeLast()
print(num) // [1, 2, 3, 5, 6, 7]

// 특정 값의 인덱스 찾기
print(num.firstIndex(of: 2)) // Optional(1)

// 찾은 인덱스로 값 수정
if let i = num.firstIndex(of: 2) {
    num[i] = 20
}
print(num) // [1, 20, 3, 5, 6, 7]

// 배열 이어붙이기
num = num + num
print(num)

// += 연산
num += [8, 9]
print(num)

// 전체 삭제
num.removeAll()
print(num) // []

// 핵심:
// append → 뒤에 추가
// insert → 특정 위치 삽입
// remove → 삭제
// firstIndex → 위치 찾기
// +, += → 배열 합치기

6. 배열은 값 타입 (중요)

var num = [1, 2, 3]
var x = num   // 복사됨 (참조 아님)

num[0] = 100

print(num) // [100, 2, 3]
print(x)   // [1, 2, 3]

// 핵심:
// Array는 struct → 값 타입
// 복사하면 서로 완전히 독립적인 배열이 됨

7. min / max

var num = [1, 2, 3, 10, 20]

print(num) // [1, 2, 3, 10, 20]

print(num.min()) // Optional(1)
print(num.max()) // Optional(20)

// Optional이라서 강제 언래핑 가능
print(num.min()!) // 1
print(num.max()!) // 20

// 핵심:
// min, max는 Optional 반환 (빈 배열 대비)
// 값 있다고 확신할 때만 ! 사용

8. 정렬 (sort / sorted)

var num = [1, 5, 3, 2, 4]

// 오름차순 정렬 (원본 변경)
num.sort()
print(num) // [1, 2, 3, 4, 5]

// 다시 값 변경
num = [2, 3, 4, 5, 1]

// 내림차순 정렬 (원본 변경)
num.sort(by: >)
print(num) // [5, 4, 3, 2, 1]

// reverse (순서 뒤집기)
num.reverse()
print(num)

// sorted (원본 유지, 새 배열 반환)
print(num.sorted()) // 오름차순 결과 반환
print(num)          // 원본 그대로 유지

// 내림차순 sorted
print(num.sorted(by: >))
print(num)          // 여전히 원본 유지

// 핵심:
// sort() → 원본 변경
// sorted() → 새 배열 반환 (원본 유지)
// reverse() → 순서 뒤집기

 

'iOS' 카테고리의 다른 글

iOS_grade3 - 9주차  (0) 2026.04.28
iOS_3grade - 7주차  (0) 2026.04.14
iOS_grade3 - 5주차  (0) 2026.03.31
iOS_3grade - 4주차  (0) 2026.03.24
iOS_3grade - 3주차  (1) 2026.03.17