기본 ViewController의 생명주기

  1. viewDidLoad
  2. viewWillAppear
  3. viewDidAppear
  4. viewWillDisappear
  5. viewDidDisappear

가 존재한다.

 

생명주기는 크게 Navigation Controller를 이용한 Push 와 Present 방식으로 나눠진다.

 

Push

A ViewController에서 B ViewController를 호출(즉, Push)한다고 하자.

  1. A에서 B를 호출 시 B(viewDidLoad)
  2. A(viewWillDisappear)
  3. B(viewwillAppear)
  4. A(viewDidDisappear)
  5. B(viewDidAppear)

B ViewController에서 pop한다고 하자

  1. B(viewWillDisAppear)
  2. A(viewWillAppear)
  3. B(viewDidDisappear)
  4. A(viewDidAppear)

Present

똑같이 A ViewController에서 B ViewController를 호출(즉, Present)한다고 하자.

  1. B(viewDidLoad)
  2. A(viewWillDisappear)
  3. B(viewWillAppear)
  4. B(viewDidAppear)
  5. A(viewDidDisappear)

B ViewController에서 dismiss한다고 하자

  1. B(viewWillDisappear)
  2. A(viewWillAppear)
  3. A(viewDidAppear)
  4. B(viewDidDisappear)

여기서 주의할 A의 viewDidLoad가 불리지 않았다!

또한 주의할 점은 Over 방식은 또 다르게 동작한다는 것이다. Over가 붙으면 현재 화면 위에 새로운 VC를 올린다고 생각하면 된다. 따라서 현재 화면은 사라지거나, 나타나거나 하지 않고 계속 상태가 유지되며 위에 올라오는 화면만 생명주기를 수행하게 된다.

 

 

 

Segue로 ViewController 전환

  1. StoryBoard 내의 버튼에 Segue 추가하기
  2. StoryBoard 내의 VC에 Segue 추가하고 Segue Identifier 지정하기
  3. StoryBoard 내의 VC에 Identifier 지정하기

1번 방법

버튼으로 Segue추가

위와 같은 방법으로 Tap했을 때 Segue를 발생시키고 싶은 버튼을 Ctrl+드래그 한다.

해당 Button을 클릭했을 때, 이동하고 싶은 VC에 드래그한다.

 

이렇게 설정해놓으면 EditButton을 Tap하게 되면 LoginVC로 이동하는 것을 볼 수 있다.

 

데이터 전달

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
            if let loginVC = segue.destination as? LoginViewController{
                loginVC.nameText = self.nameLabel.text ?? "name"
                loginVC.descriptionText = self.descriptionLabel.text ?? "description"
            }
        
    }

Presenting VC에서 prepare 메소드를 재정의한다.

destination은 Presented VC가 되고 옮기고 싶은 데이터들을 옮겨주면 된다.

이때, LoginViewController는 아직 메모리에 올라가지 않았기 때문에, IBOutlet 에는 접근할 수 없다.


2번 방법

VC에서 Swgue 추가

위와 같은 방법으로 Segue를 발생시키고 싶은 VC를 Ctrl+드래그한다.

해당 VC에서 이동하고 싶은 VC에 드래그한다.

Segue Identifier

생성된 Segue에 Identifier를 지정해준다.

나는 Profile에서  Login으로 가는 Segue이기 때문에, ProfileToLogin이라고 지었다.

이때, 복잡한 UI와 애니메이션이 들어가는 App을 개발할 때는 Modal방식인지 직접 커스텀한 애니메이션을 가진 방식인지에 따라서 네이밍을 더 복잡하게 하기도 한다.

@IBAction func tappedEditButton(_ sender: Any) {
        performSegue(withIdentifier: "ProfileToLogin", sender: nil)
        
    }

EditButton을 눌렀을 때, 이동시키기 위해 IBAction에 performSegue를 넣어주었다.

이때, withIdentifier에는 위에서 네이밍한 Swgue Identifier를 적어주면된다.

 

EditButton을 Tap하게 되면 LoginVC로 이동하게 된다.

 

데이터 전달

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "ProfileToLogin"{
            if let loginVC = segue.destination as? LoginViewController{
                loginVC.nameText = self.nameLabel.text ?? "name"
                loginVC.descriptionText = self.descriptionLabel.text ?? "description"
            }
        }
        
    }

또한, Presenting VC에서 하나의 Segue만 가지고 있을 때는 상관없는데, 만약 2개 이상의 Segue를 처리하게 된다면

prepare에서 발생한 Segue의 Identifier를 확인하여 원하는 데이터 전달을 구현할 수 있다.

 

 


3번 방법

@IBAction func tappedEditButton(_ sender: Any) {
        if let loginVC = self.storyboard?.instantiateViewController(withIdentifier: "LoginViewController") as? LoginViewController{
            present(loginVC, animated: true, completion: nil)
        }
    }

코드만 사용해서 EditButton을 Tap했을 때 LoginVC로 이동하게 된다.

 

이렇게만 해서는 안되고 한단계가 더 있다.

 

위의 withIdentifier에 들어가는 값은 LoginViewController의 타입이 아닌 String이다!

눈치 빠른 분들은 아셨겠지만, Segue에 Identifier를 설정한 것처럼 VC에도 Identifier를 설정가능하다.

VC Identifier 설정

그림처럼 VC에 Identifier를 설정하면 된다.

데이터 전달은 present(...)하기 전에 데이터를 전달하면 된다.

StoryBoard vs Code

iOS 프로젝트를 처음 그리고 초반에 진행을 할 때는 무조건 스토리보드로 UI를 작성하고 ViewController와 연동하였다.

why?

쉬우니까! 간편하니까! 내가 작성한 것이니 내가 제일 잘 아니 헷갈리지도 않으니까!

BUT!

혹시라도 2명이상과 프로젝트를 진행하게 된다면 스토리보드의 가장 큰 단점이 보인다.

  • 하나의 스토리보드로 함께 작업시에 충돌할 가능성이 진부함
  • 스토리보드를 나눠서 작업을 해도 굳이 하나의 스토리보드에 들어갈 화면들이 나눠짐 -> 이후에 다시 합쳐야 하는 불편함
  • 이후에 다시 합치는 과정에서도 Ctrl+C/V를 하더라도 다시 AutoLayout을 위한 Constraint를 지정해주는 것이 어렵다!
    • 물론 정리된 문서가 있으면 금방할 수 있다고 생각했지만, 아니었다...
    • 내가 UI Component들에 지정한 이름을 보고 이게 이거였나? 이게 이거였겠지? 하면서 헷갈리는 순간 시간이 굉장히 딜레이됐다.
    • 그래서 이후로는 UI Component들의 이름도... 어떠한 체계로 명확히 작성해야한다는 것을 꺠달았따...

TabBar

예제로 진행할 탭바 StoryBoard

목표 : 이처럼 TabBarVC를 가지고 있고 2개의 VC를 삽입하여 간단한 TabBar를 구현하는 것!!

 

class TabBarViewController: UITabBarController{
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let profileVC = ProfileViewController()
        let vc2 = VC2()
        
        let icon1 = UITabBarItem(title: "프로필", image: UIImage(systemName: "person.circle"), selectedImage: UIImage(systemName: "person.circle"))
        let icon2 = UITabBarItem(title: "VC2", image: UIImage(systemName: "person.circle"), selectedImage: UIImage(systemName: "person.circle"))
        
        profileVC.tabBarItem = icon1
        vc2.tabBarItem = icon2
        let vcArr = [profileVC, vc2]
        self.setViewControllers(vcArr, animated: true)
    }
        
}

 

  1. TabBarVC라는 ContainerVC에 들어갈 ProfileVC, VC2를 생성한다.
  2. 각각의 VC들이 TabBar에서 보일 Item을 만든다.
  3. 2번에 이어서 붙여준다.
  4. VC들을  TabBarVC의 VC들로 지정한다.

이과정을 진행하면 문제없이 시뮬레이터에서 실행이될 것이다.

ProfileVC
VC2

보라! 정상적으로 TabBar가 구현되었다!!

응??? 근데 뭔가 이상하다??

스토리보드에 있는 ProfileVC

스토리보드에 있는 ProfileVC에는 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ라고 적힌 Label이 있는데??

아! ProfileVC에 Outlet으로 가져오면 되겠구나!

 

class ProfileViewController: UIViewController {
    
    @IBOutlet weak var label: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .white
        
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        self.label.text = "aa"
    }


}

하고 실행!

Outlet을 지정한 후의 ProfileVC

엥? 아직도 안뜨네? 거기다가

Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value 라는 에러도 나타났다.

이 에러는 타입에 !를 선언해주고 nil에 접근했을 때 나타나는 건데....

스토리보드에서 profileVC와 ProfileViewController를 연결하고 Outlet에 Label을 연결했고! 아무 문제가 없어야하는데 문제가 발생??




해결 방법

guard let profileVC = self.storyboard?.instantiateViewController(withIdentifier: "ProfileVC") else { return }

위 코드처럼 바꿔주면된다.

해석하자면

  1. self가 존재하는 스토리보드에 있는 VC들 중에 "ProfileVC"라는 ID를 가진 VC를 가져온다.

스토리보드에 UI Component들을 작업하고 그 컴포넌트들을 코드로 생성한 VC에서 사용하고 싶으면 이런 방법을 써야한다.

물론 스토리보드에 UI Component들을 넣지 않고 코드로

let label = UILabel() 로 선언한 후에

text와 constraint들을 지정해 줘도 되지만... 아직까지는 그렇게 익숙하지 않으니 이 방법도 알아보게 되었다.

View

  • 사용자와 상호작용을 할 수 있는 그림(?)이라고 생각하면 된다.
    • 당연히 그림만 가지고는 어떠한 동작이나 임무를 수행할 수 없다.
  • View가 사용자로부터 User Interation을 받으면 iOS 운영체제 커널 -> 이벤트 Queue -> VC의 순서로 이벤트가 전달된다.
    • 따라서 User와 Controller 사이에 연결을 담당하는 것이 View인 것이다.

ViewController

  • UIKit의 User Interface를 관리한다.
  • 한 개의 Application 내에는 1개 이상의 ViewController를 가지고 있다.

ViewController의 종류

  1. ViewController
    • 일반적인 VC
    • 안드로이드에서의 Activity와 비슷한 역할을 수행한다.
  2. Container ViewController
    • 다른 VC(ViewController)를 자신의 rootView에 넣는다.
    • rootView 내의 VC를 상황/조건에 따라 바꿔준다. -> 메모리 공간에서의 이점은 있겠으나, 이후의 Flow를 진행할 떄에, 예기치못한 상황이나 class 타입인 VC를 다루는 데에 포인터를 확실히 제거하지 않으면 retain Cycle이 발생할 것 같다.

TabBar

  • 화면 가장 하단에 위치하여, 모드 선택에 따라 그에 맞는 View를 보여주는 Interface이다.
  • 쉽게 페이스북, 카카오톡을 생각하면 된다

TabBarViewController

  • UIKit에 UIViewController를 상속받은 UITabBarController가 존재한다.

TabBar 구현 방법(1)

  1. StoryBoard에서 Root가 될 VC에 Tab Bar Controller 를 Embed한다
  2. Item으로 넣고 싶은 VC들을 ctrl+drag를 통해 삽입한다.TabBar 구현 방법(2)
  3. Root가 될 VC에 UITabBarController를 상속한다.
  4. 넣고 싶은 VC들을 생성하고 각각의 VC들의 tabBarItem을 초기화한다.
    • 이때, tabBarItem이란, TabBar에 보일 Item(즉, 버튼)이다
  5. 해당 VC array를 rootVC의 viewControllers에 넣는다.

VC 이동 하는 방법

  • 하단에 TabBar Item Button을 터치/클릭 하여 전환

H.I.G in Apple

  • 일반적으로 3~5개의 탭을 사용하라.
    • 너무 적으면 굳이 TabBar를 사용할 이유가 없으며, 너무 많으면 사용자에게 혼란을 줄 것이다.
  • 다른 영역으로 이동할 때, Tab Bar를 숨기지 마라

+ Recent posts