Notice
Recent Posts
Recent Comments
Link
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Tags
more
Archives
Today
Total
관리 메뉴

Ruff! Ruff!

#3: CS50 track - lesson2 본문

Ios

#3: CS50 track - lesson2

maeng-kim 2024. 1. 13. 14:45

UIkit

- xcode는 UI기반으로 앱을 빌드함.  UI 한 개의 각 요소들이 오브젝트(object)로써 기능함

- UIkit는 UI or user interface 요소를 화면에 만드는 역할을 함

- tables, images, buttons 등등...다 있음

- mvc(model, view, controller) 패턴을 따름

   ->M = model : how you model your data/ model은 그냥 data 그 자체를 나타냄

   ->V = view : data를 어떻게 보여줄 것인지.. ex. 데이터를 어떻게 가공할 것인지

   -> C = controller : model 과 view의 연결다리 역할/ 상호작용을 가능케 함 .. 따라서 우린.. 주로 controller layer에서 작업하게 될 것... 임

 

 

Table View

- ios앱과 뗄 수 없는 관계

- ex. 트위터에서 트윗의 리스트를 보여줄 때와 같은...

- 걍 list 

- 늘 이것 먼저

 

 

Storyboards

- 앱의 흐름을 나타냄.

- 시각적으로 화면을 구성하는 공간

- 앱의 UI layer를 만드는 것.

 

 

IBOutlet 

- 코드와 스토리보드와의 연결고리 담당

- 정의된 변수 또는 상수와 스토리 보드의 View객체의 연결

 

IBAction

- View객체가 특정 이벤트를 발생시켰을 때 취하는 행동

 

 

Segues

- 화면 이동 담당

 

 

 

~실습시간~

Team: apple계정. 만약 앱스토어에 등록하면 해당 Team정보로 등록됨.

Organization: 마찬가지로 apple계정이 있다면 해당 계정의 정보를 입력하면 됨.

interface: StoryBorad -> 드래그&드랍으로 쉽게 앱 구성 가능한 프레임워크

                  SwiftUI -> brand new framework

Language: Swift/ objective-c -> Swift는 new, objective-c는 옛날

 

 

 

생성 이후 화면

Delegate: 기본적인 앱의 main method 

import UIkit -> UIkit: 기본적인 method를 담고 있는 라이브러리, UI라이브러리를 사용하려면 이를 import해야지요

Main: StoryBoard

Assets: 이미지 등을 앱에 추가했을 때 모이는 파일

LaunchScreen: 앱 시작 시 제일 먼저 보여지는 .. (프로그램이 로드 되는 동안 나오는 애니메이션이라든지..)

                           -> 앱이 로드되기 전에 보여지는 화면

info.plist: 앱의 전반적인 정보들 (우클릭을 통해 소스코드로도 볼 수 있음. xml파일이라고 생각하면 됨)

 

 

 

-sceneDelegate

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate { //애플이 제공하는 프로토콜

    var window: UIWindow? //optional


    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
        guard let _ = (scene as? UIWindowScene) else { return }
    }
...

 

-ViewController

import UIKit

class ViewController: UIViewController { //컨트롤러 프로토콜

    override func viewDidLoad() { //이 뷰를 로드할 때 ios에 의해 자동적으로 call됨
        super.viewDidLoad() //viewDiDLoad를 super class로
        // Do any additional setup after loading the view.
    }


}

 

 

 

기존에 있던 뷰컨트롤러를 삭제하고 테이블뷰컨트롤러를 추가

(추가는 상단에 있는 '+'버튼에서 object란에 TableViewController를 검색 후 선택해서 추가)

 

 

이후 뷰컨트롤러 파일에서 컨트롤러 클래스 프로토콜을 테이블뷰컨트롤러 프로토콜로 수정

import UIKit

class ViewController: UITableViewController { //컨트롤러 프로토콜

    override func viewDidLoad() { //이 뷰를 로드할 때 ios에 의해 자동적으로 call됨
        super.viewDidLoad() //viewDiDLoad를 super class로
        // Do any additional setup after loading the view.
    }


}

 

 

이제 스토리보드와 소스파일을 연결해주자 !

스토리보드에서 커스텀 클래스를 선택한 후 class를 드롭다운해 연결할 컨트롤러 class를 선택한다.

이걸 안 해주면 xcode는 UI와 컨트롤러가 mapping되어 있음을 모른다.

 

이후 5번째 항목을 선택해 initial view에 체크해준다.

-> 이러면 앱 시작할 때 가장 먼저 보이게 된다 !

 

 

이후에 run을 누르게 되면 시뮬레이터가 시작된다.

시뮬레이터에서 내가 만든 TableView를 볼 수 있다.

 

 

이후 pokemon정보를 넣기 위해 포켓몬 파일을 하나 더 만들고 포켓몬 정보를 담을 struct를 만든다.

뷰컨트롤러에 만든 포켓몬 struct를 사용하여 pokemon정보를 대충.. 3개 정도 넣어두고 

테이블뷰컨트롤러의 함수를 알맞게 override해줍시다

 

1. numberOfSection 함수

- 섹션의 수를 담당함

- 섹션이란? 아래와 같이 나눠져 있는 것이 하나의 섹션

- 섹션의 구성: 한 개의 header, 다수의 cell, 한 개의 footer

- 아래는 섹션이 4개로 나눠져 있는 것.. 일 걸..?

 

2. numberOfRowInSection 함수

- 한 섹션 안에 있는 Row(열, 세로)의 갯수를 리턴해줌

- 위 사진에서 첫번째 섹션에는 2개의 row가 있음

 

3. cellForRowAt 함수

- 테이블 뷰의 특정 위치에 삽입할 cell의 데이터 소스를 요청함

- 파라미터로 tableView(셀에 요청하는 테이블 뷰 obj), indexPath(테이블 뷰 내부의 row에 위치하는 indexpath=함수가 요청하는 섹션의 행에 대한 정보, 이를 기준으로 주어진 행에 대한 데이터를 표시하도록 셀을 구성함)

- 리턴값으로 테이블 뷰가 지정된 row에 사용할 수 있는 UITableViewCell에 상속된 개체

 

 

 

함수들을 작성한 후 

cell을 구분하기 위해 identifier를 설정해준다.

 

 

테이블뷰셀은 아래와 같이 구성되어 있다.

위의 화면에서 테이블뷰 셀의 악세서리를 설정해주자

Disclosure Indicator를 선택하면 작은 arrow가 오른쪽 악세서리뷰 칸에 생긴당~ 귀엽다.

왼쪽 편집기에서는 백그라운드 색상, 틴트 등등.. 셀에 대한 다양한 걸 설정해줄 수 있다!

 

 

이후 reusablecell을 지정해준다 

import UIKit

class ViewController: UITableViewController { //컨트롤러 프로토콜
    let pokemon = [
        Pokemon(name: "Bulbasaur", number: 1), //컴파일러는 struct를 알고 있음 글서 알아서 포멧을 가져와줘
        Pokemon(name: "Ivysaur", number: 2),
        Pokemon(name: "Venusaur", number: 3)
    ]
    
    
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return pokemon.count //3 rows
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "PokemonCell", for: indexPath) //셀을 잡고 이를 텍스트로 바꾸고 셀을 리턴함
        cell.textLabel?.text = pokemon[indexPath.row].name //만약 이 textLabel이 nil이라면 걍 이 라인을 무시함 다음 라인으로
        return cell
    }
}

cell을 테이블뷰의 dequeueReusableCell로 지정해준다.

 

*dequeueReusableCell이란?

- 지정된 재사용 식별자에 대한 재사용 가능한 테이블 뷰 객체를 반환하고 이를 테이블에 추가함

- 파라미터로 받는 identifier은 string형 재사용할 객체를 나타냄

- for은 테이블뷰 메소드가 파라미터로 받는 indexPath를 받는다. -> 셀의 위치를 지정하는 indexPath. + 인덱스 경로를 사용하여 테이블뷰에서 셀의 위치를 기반으로 추가 구성을 수행함

-> 메모리를 줄이기 위해 사용. 만약 1000개의 항목을 갖는 테이블이 있다면 테이블을 만들 때 1000개의 항목마다 셀을 만들게 되고 1000개의 테이블 뷰 셀에 대해서 메모리 할당이 이뤄짐.

-> dequeueReusableCell을 사용하면 만약 한 화면에서 테이블 뷰 셀을 4개 볼 수 있다고 가정한다면 테이블뷰는 테이블 높이과 셀 높이를 기반으로 셀 수를 생성함. -> 즉 4개. -> 그럼 현재 메모리 할당은 4개 셀에 대한 메모리 할당만 하면 됨

-> 스크롤을 하게 되면? 여전히 동일한 셀이 사용되지만 안의 데이터를 기반으로 셀 내용만 변화함. 

셀이 스크롤 화면 밖으로 밀려나면 셀은 reuse pool에 들어가고 dequeueReusableCell을 호출할 때 테이블 셀에 의해 반환됨.

(OS에서 배웠던 스레드풀 같은..)

 

 

 

위 과정을 하나씩 하면 짜잔 

아래와 같이 내가 넣은 3개의 포켓몬리스트가 보인다 !

(참고로 Bulbasaur은 이상해씨임 !, 밑으로 갈수록 진화형)

나중에 보면 엄청 별 거 아닐 것 같지만

일단 지금은 기분이 매우 좋다. 🤩

 

 

 

 

이제 다른 어플들처럼 네비게이션바를 만들어보자

뷰컨트롤러를 클릭하고 에디터를 누른다음 embed in에서 네비게이션 컨트롤러를 클릭하면

네비게이션 컨트롤러가 추가된다

 

 

 

 

테이블뷰의 상단을 클릭하여 네비게이션 타이틀을 지정해주자

 

 

 

 

 

 

이제 두번째 뷰를 만들어보자!

각 포켓몬의 개인적인 정보를 담을 뷰를 만들 것이다.

xcode 상단에 '+' 버튼을 클릭하여 또 다른 viewcontroller을 추가해주자

 

plain viewcontroller을 드래그해서 옆에다가 만들고 

해당 뷰 컨트롤러에 정보를 담을 text field를 만들기 위해 다시 '+'버튼을 클릭하여 UI label이라는 것을 선택해 추가해주자

 

 

드래그에서 넣고 싶은 부분에 넣어주고

클릭하여 글씨 크기와 정렬 등등을 변경해준다

이렇게 정보를 볼 수 있는 뷰 컨트롤러를 추가했다

 

이후 이 뷰 컨트롤러의 소스가 될 하나의 스위프트 파일을 더 만든다. (PokemonViewController)

이곳에 일단 IBOutlet을 만들 것이다.

이는 뷰 컨트롤러에 추가될 label들과 스위프트 코드를 연결시켜주는 역할이다.

이를 통해 뷰 컨트롤러에서 label을 조작할 수 있다.  ~ 그래서 난 일단 강의에 나온대로 2개의 field를 만들 것이다.

 

import Foundation
import UIKit

class PokemonViewController: UIViewController {
    @IBOutlet var nameLabel: UILabel!
    @IBOutlet var numberLabel: UILabel!
}

 

 

만들고 나서 스토리보드로 돌아가 class를 만든 PokemonViewController로 설정해준다.

 

 

 

옆에 보이는 동그라미는 

- 꽉찬 동그라미 : UI와 코드가 연결되어 있음

- 빈 동그라미 : 연결이 되어있지 않은 상태 -> 이 상태로 접근하면 crush발생. 연결되어 있지 않은 outlet은 nil값을 갖게 됨. -> nil값을 추정하다가 crush

위는 연결이 되어 있지 않은 상태..이다.. 

스토리보드에서 코드와 드래그해서 연결해준다. -> ctrl+우클릭 드래그

 

이렇게 네임레이블과 넘버레이블을 스위프트와 스토리 보드에 추가해서 조작할 수 있게 되었다.

이렇게 연결되어 있는 것을 확인할 수 있다.

위에서 작은 엑스를 클릭하면 연결을 끊을 수도 있다.

 

자 이렇게 두번째 뷰 설정을 마쳤다.

우리는 첫번째 뷰에서 두번째 뷰로 이어지게 하려는 것이니 두번째 뷰에 포켓몬 obj를 보여줄 속성을 만들 것이다.

var으로 선언한다. 물론 이전에 만들었던 Pokemon class로 (IBOutlet으로 하지 않는 이유는 이것이 뷰 컨트롤러의 뷰와 부합하지 않기 때문임)

이제 포켓몬 정보를 넘길 것이다.

그 전에 우리가 이미 해당 사항을 다 만들었다고 가정하면 우리의 뷰가 어떻게 동작할지 확인해보자

이를 위해선 viewDidLoad method를 사용해야한다.

이 메소드는 뷰 컨트롤러가 막 로딩을 끝냈을 때 자동적으로 불러와지는 메소드이다. (by ios)

그래서 암튼 우리는 이 함수를 override할 것이다.  

 

class PokemonViewController: UIViewController {
    @IBOutlet var nameLabel: UILabel!
    @IBOutlet var numberLabel: UILabel!
    var pokemon: Pokemon!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

*다른 클래스로부터 상속 받는 클래스: supclass/ 그 외: superclass

*superclass 버전의 method를 선언해주려면 위와 같이 super.method명 해주면 된다.

*super.viewDidLoad()를 하지 않아도 에러가 발생하지 않지만 해줘야하는 이유는 UIViewController 기본 클래스에서 해당 메소드를 살펴보면 아무것도 구현되지 않은 상태이므로 먼저 호출해서 설정해주는 게 좋은 습관이다.

 

import Foundation
import UIKit

class PokemonViewController: UIViewController {
    @IBOutlet var nameLabel: UILabel!
    @IBOutlet var numberLabel: UILabel!
    var pokemon: Pokemon!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        nameLabel.text = pokemon.name
        numberLabel.text = String(pokemon.number)
    }
}

이후 2개의 label에 value을 세팅한다.

아까 pokemon struct에서는 number가 int로 선언했기 때문에 String()으로 감싸서 text형식으로 받을 수 있게 해준다.

 

 

이제 첫번째 뷰 컨트롤러에서 두번째로 어떻게 데이터를 넘기는지 알아보자.

현재는 스토리보드에 들어가서 보면 첫번째 뷰 컨트롤러와 두번째 뷰 컨트롤러 사이에 아무런 connection이 없다.

따라서 segue라고 불리는 것을 만들어서 연결해줄 것이다.

(뷰 컨트롤러 간의 전환을 맡고 있음. segue는 일종의 화면이동..)

 

 

 

연결하고픈 cell을 ctrl+우클릭 드래그해서 연결하고픈 뷰 컨트롤러와 연결해주면 된다.

그러면 아래와 같이 두 뷰 컨트롤러 사이에 segue가 형성된다.

드래그하면 다양한 segue가 나오는데 현재는 가장 기본인 show를 선택했다.

 

 

 

이후 해당 segue에 identifier을 설정해주자.

 

 

이제 우리는 이 string을 첫번째 뷰 컨트롤러 안에서 사용할 것이다.

우리가 segue를 사용하기 위해서는 UIViewController안에 있는 method를 override해줘야하는데

해당 method는 아래와 같다.

 

지금 이 케이스에선 segue가 하나지만 나중엔 막 여러개의 segue가 존재할 것이고..

그래서 암튼 우리는 identifier를 사용해야 한다. 

여러 segue를 정확하게 구분해야하기 때문에..

 if segue.identifier == "PokemonSegue"

이런식으로 identifier을 식별해서 사용한다.

이후 우리가 움직일 목적지 뷰 컨트롤러를 설정한다.

목적지 type은 regular UIViewController이다.

근데 우리는 PokemonViewController에 접근하고 싶으니깐 (왜냐면 우리는 Pokemon이란 field를 만들었고 우리는 그 필드를 데이터를 넘길 때 사용해야 하니깐 근데 어떤 기본 UIViewController도 Pokemon이라는 field를 갖고 있지 않음.) UIViewController 목적지를 PokemonViewController로 변경해줘야한다.

이를 위해 아래와 같은 스위프트 구문을 사용할 것이다.

 

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "PokemonSegue" {
            if let destination = segue.destination as? PokemonViewController {
                
            }
        }

위 구문은 만약 목적지가 segue.destination이라면 PokemonViewControllor로 cast될 수 없고 nil을 반환하고 절대 하위 블럭 안으로 들어갈 수 없다.

그러나 만약 이게 cast된다면 블럭 안으로 들어갈 수 있다.

 

 

이제 다 만들었다. 근데 포켓몬 리스트에서 클릭해서 상세페이지로 넘어가면 back버튼이 보여야하는데 

안 보임.. 뭐지 뭘 잘못한거지.. 하고 구글링해보니 .. 

ViewController에 네비게이션 컨트롤러를 embed in 안 했음..

그래서 부랴부랴 메인 뷰 컨트롤러를 클릭 한 후 (스토리보드에서) 상단에 editer에 들어가서 embed in을 누른 후 네비게이션 컨트롤러를 추가해 주었다.

 

 

 

 

 

 

 

그렇게 완-성 - !!!!!!!!!!!

아직 작고 .. 아무렇지도 않은.. 뷰 컨트롤러도 몇 개 없는... 작은 완전 시작일 뿐인 아기지만 그래도 완성🤩

첫 화면. 포켓몬 리스트가 나열되어 있음

 

두번째 화면 !

포켓몬을 클릭하면 포켓몬 이름과 번호가 뜸 !

마지막에 부랴부랴 수정해준 back 버튼도 잘 뜬다 🤓

(네비게이션 아이템의 title이름을 Pokedex로 설정해두어서 back이 아니라 Pokedex로 뜸 !)

 

 

 

후 ~ 하 이렇게 cs50 lesson2 끝.

영알못이라 매우 힘들었삼. 휴 ~

'Ios' 카테고리의 다른 글

#2: CS50 track - lesson1  (0) 2024.01.13
#1. ios개발을 해보자  (0) 2024.01.13