평범한 컴공 대학생의 공부일지

iOS Swift 간단한 목록 Application 만들기 본문

iOS Swift

iOS Swift 간단한 목록 Application 만들기

Taram 2024. 4. 8. 17:21

※이 iOS 카테고리의 글 들은 학교 강의와 과제를 기반으로 작성한 것입니다.※


이번 포스트에서는 간단한 목록을 표시하는 앱을 만들어보겠습니다.
그 전에 중요한 개념 몇가지 짚고 넘어가겠습니다.

  • Access Control  - 코드의 일부에 대한 접근 권한을 제한하는 기능
                               - 코드의 세부 구현 숨김 -> 인터페이스에만 접근 가능 -> 보안성 향상
                               - 모듈화, 캡슐화 촉진
                             -> Java의 Public, Private 등

    - Access Modifier 
    1. Open: 클래스와 클래스 멤버에 대해서만 사용할 수 있으며, 가장 높은 수준의 접근성을 보유
                 다른 모듈에서 상속 및 Override가 가능
    2. Public: Open과 유사하지만, 다른 모듈에서 상속이나 Override 불가능
                  함수, 변수 등 대부분의 타입에 사용할 수 있습니다.
    3. Internal: 기본 액세스 수준
                     같은 모듈 내에서만 접근할 수 있으며, 모듈 외부에서는 접근 불가능
                     모듈 단위의 프로젝트를 개발할 때 유용합니다.
    4. File-private: 정의된 소스 파일 내에서만 사용가능
                          소스 파일 내의 다른 타입이나 함수 등에서 접근 O, 파일의 외부에서는 접근 X
    5. 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 설정하기

  1. Delegate와 DataSource 설정하기
    - UITableView의 동작을 관리하기 위해, ViewController에서 UITableViewDelegate와 UITableViewDataSource 프로토콜을 채택
    - viewDidLoad 메소드에서 이 테이블 뷰의 delegate와 dataSource를 self로 설정

2. UITableView 구현하기

  1. 섹션과 행 설정하기
    - numberOfSections(in tableView:) 메소드를 통해 섹션의 수를 3으로 설정
    - tableView(_:numberOfRowsInSection:) 메소드를 통해  각 섹션 내의 행의 수는 5로 설정
  2. 셀 구성하기
    - 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. 셀 구성하기:
    1) tableView(_:cellForRowAt:) 메소드를 통해 각 행에 대한 셀을 구성
    2) 표준 셀 스타일인
    .subtitle을 사용
    3) 셀의 주 제목에는 맛집의 이름, 부 제목에는 맛집의 전화번호, 그리고 셀의 이미지 뷰에는 맛집의 이미지를 설정
    4) 각 값은
    indexPath.row를 사용하여 배열에서 적절한 데이터를 찾아 할당
        indexPath.row는 현재 셀의 행 번호를 나타내므로, 이를 통해 각 행에 맞는 데이터를 배열에서 가져오기 가능

  2. 섹션 수 설정하기
    1)
    numberOfSections(in:) 메소드를 사용하여 테이블 뷰에 표시할 섹션의 수를 설정 -> 총 3개의 섹션을 사용
    2) 실제 앱에서는 섹션을 통해 데이터를 그룹화하거나 다른 유형의 정보를 분리하여 표시 가능

  3. 행의 수 설정하기:
    1) tableView(_:numberOfRowsInSection:) 메소드를 통해 각 섹션에 표시할 행의 수를 결정 -> 각 섹션에 5개의 행을 표시
    결과 화면

'iOS Swift' 카테고리의 다른 글

영화 순위 Application 만들기(1)  (0) 2024.05.16
iOS Swift 영화 순위 어플리케이션  (2) 2024.04.18
iOS Swift 문법 간단 복습  (0) 2024.04.05
iOS Swift 무드등 앱 만들기  (0) 2024.03.25
iOS Swift 오픈 API 가져오기  (0) 2024.03.13