[IOS] 화면 전환 delegate로 데이터 넘기기

2022. 11. 26. 23:23IOS

* 본 포스팅은 패스트캠퍼스의 "30개 프로젝트로 배우는 IOS 앱 개발 with Swift"를 참조하였습니다.

 

화면전환 종류

[IOS] View Controller 화면 전환 및 life cycle

 

[IOS] View Controller 화면 전환 및 life cycle

* 본 포스팅은 패스트캠퍼스의 "30개 프로젝트로 배우는 IOS 앱 개발 with Swift"를 참조하였습니다. View Controller - Content view controller 화면을 구성하는 뷰를 직접 구현하고 관련된 이벤트를 처리하는

hyukji.tistory.com

 

이전 포스팅인 "화면 전환 관련 개념 정리" 에서 설명한 것처럼 화면 전환하는 방법에는 크게 4가지가 있다.

1. 뷰 위에 다른 뷰 가져와 바꿔치기(비추)

2. 다른 뷰로 전환하기(=프레젠테이션) - present, dismiss

3. 네비게이션 컨트롤러 사용하기 - pushViewController, popViewController

4. 화면 전환용 객체 세그웨이 사용(코드사용x)

 

개념은 위와 같은 방식으로 나누어지지만 실제 구현할 때는 2가지 기준에 따라 나누어진다.

1. 세그웨이 사용 여부 -> 다음 페이지로 이동할 때 코드를 사용하냐 사용하지 않고 이동하느냐의 차이.

2. 네이게이션 컨트롤러 사용 여부 -> 페이지를 이동할 때 push, pop을 사용하느냐 present, dismiss를 사용하느냐 차이.

 

따라서 다른 방식으로 구분 할 수 있다.

1. 세그웨이 사용 O, 네이게이션 컨트롤러를 사용 O -> 코드를 사용하지 않고 이동 nav를 이용해 뒤로 갈때는 pop함수를 사용해야함.

2. 세그웨이 사용 O, 네이게이션 컨트롤러를 사용 X -> 코드를 사용하지 않고 이동 뒤로 가려면 dismiss를 사용해야함.

3. 세그웨이 사용 X, 네이게이션 컨트롤러를 사용 O -> present를 사용해 이동 뒤로 가려면 pop사용해야함.

4. 세그웨이 사용 X, 네이게이션 컨트롤러를 사용 X -> present를 사용해 이동 뒤로 가려면 dismiss사용해야함.

 

 

 

Delegate를 이용한 데이터 넘기기

delegate는 위임자라는 뜻이다. protocal로 대략적인 기능의 구조를 만들면 delegate가 이를 직접 실행한다. 당연히 이해가 안될 것이다 필자도 그러했다ㅎ 그렇다면 코드로 살펴보자! 코드를 작성하다 보면 이해가 될 것이다.

 

필자가 작성한 프로젝트의 대략적인 구조는 다음과 같다.

글자를 보여주는 화면 ViewController, 설정을 조정해주는 SettingViewController 두 가지 뷰컨트롤러가 있으며 네비게이션 상으로 settingViewController이 child이다. 두 뷰컨트롤러끼리 글자, 글자색, 배경색 총 3가지 정보를 서로 넘겨주고자 한다! (화면전환에는 segue와 nav를 이용했다 - 4번째 방식)

 

먼저 protocal을 먼저 만들어 보자. protocal은 child ViewController인 SettingViewController에 만들어 주어야 한다. protocal에는 property와 method 둘다 넣는 것이 가능하지만 필요에 의해 method만 넣어 주었다. protocal에서는 실질적인 기능구현이 아닌 선언만 해주어야 한다.

protocol SendDataProtocol : AnyObject {
    func ChangeSetting(text : String?, TextColor : UIColor, BackColor: UIColor)
}

 

protocal을 만들었으니 이제 delegate를 만들어주자.

weak var delegate: SendDataProtocol?

delegate를 만들어 줄때에는 반드시 weak var로 해주어야 한다. weak을 쓰는 이유는 두 viewcontroller가 서로를 참조하고 있어 메모리가 해제되지 않기 때문에 대규모 프로젝트로 이어지면 메모리 누수가 심해진다.

 

이제 저장 버튼을 눌렀을 때 (settingViewController에서 ViewController로 돌아갈 때) delegate에서 함수를 실행하도록 코드를 추가해주자!

    @IBAction func tapSaveBtn(_ sender: UIButton) {
        self.delegate?.ChangeSetting(text: self.TextField.text, TextColor: TextColor, BackColor: BackColor)
        self.navigationController?.popViewController(animated: true)
    }

이제 SettingViewController에서의 작업은 끝났다! ViewController에서 작업을 마무리 해주자.

 

SettingViewController가 해당 protocal을 채택하도록 상속을 시켜주자.

class ViewController: UIViewController, SendDataProtocol {

...

}

이렇게만 한다면 프로토콜 안에 있는 ChangeSetting 함수를 구현하지 못했기 때문에 protocal을 준수하지 않는 다는 에러가 뜬다. 자 그럼 ChangeSetting 함수를 구현해보자.

 

    func ChangeSetting(text: String?, TextColor: UIColor, BackColor: UIColor) {
        if let text = text {
            self.LEDLabel.text = text
        }
        self.LEDLabel.textColor = TextColor
        self.view.backgroundColor = BackColor
    }

 

여기까지만 하면 끝! ...이면 좋겠지만 아직 대리자를 설정해주지 않았다. 대리자 설정 코드는 다음과 같으며 settingViewController로 넘어가는 부분(두번째 ViewController로 넘어가는 부분)에 적어주면 된다.

settingViewController.delegate = self

 

해당 프로젝트는 segue를 이용했기 때문에 prepare 구문을 작성해 적어주었다.

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let settingViewController = segue.destination as? SettingViewController{
            settingViewController.delegate = self
        }
    }

 

만약 ViewController에서 SettingViewController로 넘겨주고 싶은 값이 있다면 여기에 추가해주면 된다. 필자는 글자와 글자색. 배경색을 넘겨주었다.

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let settingViewController = segue.destination as? SettingViewController{
            settingViewController.delegate = self
            settingViewController.Text = self.LEDLabel.text
            settingViewController.TextColor = self.LEDLabel.textColor
            settingViewController.BackColor = self.view.backgroundColor ?? .purple
        }
    }

 

 

만약 본인이 segue가 아닌 코드를 이용했다면, 다음과 같이 작성될 것이다! 아래 코드는 세그웨이X, 네이게이션X인 상황이다.

    @IBAction func tapNextView(_ sender: UIButton) {
        guard let nextviewcontroller = self.storyboard?.instantiateViewController(identifier: "codepresentViewController")
        as? nextViewController else {return}
        nextviewcontroller.name = "next_name"
        nextviewcontroller.delegate = self
        self.present(nextviewcontroller, animated: true, completion: nil)
    }

 

코드 전체

SettingViewController.swift

import UIKit

protocol SendDataProtocol : AnyObject {
    func ChangeSetting(text : String?, TextColor : UIColor, BackColor: UIColor)
}

class SettingViewController: UIViewController {

    ...
    
    weak var delegate: SendDataProtocol?
    
    ...
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // ViewController에서 받은 데이터를 다음과 같이 사용했다.
        if let Text = self.Text {
            self.TextField.text = Text
        }
        self.changeBackColor()
        self.changeTextColor()
    }
    
    @IBAction func tapSaveBtn(_ sender: UIButton) {
        self.delegate?.ChangeSetting(text: self.TextField.text, TextColor: TextColor, BackColor: BackColor)
        self.navigationController?.popViewController(animated: true)
    }
    
    ...
    
}

 

ViewController.swift

import UIKit

class ViewController: UIViewController, SendDataProtocol {

	...
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let settingViewController = segue.destination as? SettingViewController{
            settingViewController.delegate = self
            settingViewController.Text = self.LEDLabel.text
            settingViewController.TextColor = self.LEDLabel.textColor
            settingViewController.BackColor = self.view.backgroundColor ?? .purple
        }
    }
    
    func ChangeSetting(text: String?, TextColor: UIColor, BackColor: UIColor) {
        if let text = text {
            self.LEDLabel.text = text
        }
        self.LEDLabel.textColor = TextColor
        self.view.backgroundColor = BackColor
    }
}