안녕하세요. KataRN입니다.
(수정_220120) 2개의 글을 하나로 합쳤습니다.
오늘은 화면간의 데이터 전달에 대해서 알아보겠습니다.
화면에서 화면으로 이동할때 데이터를 전달하고 받는 방법입니다.
여러가지 방법들이 있는데 매번 뭘써야되는지 왜써야되는지를 고민해왔는데 일단 뭐가 있는지부터 파악해보도록하죠.
- 프로퍼티에 직접 접근하는 방식
- segue를 이용해서 전달
- delegate 패턴을 이용한 방식
- closure를 이용한 방식
- NotificationCenter와 Observer pattern을 이용한 방식
1. 프로퍼티에 직접 접근
- 프로퍼티와 함수를 이용해서 데이터를 주고 받아보겠습니다.
1-1. A화면에 버튼을 누르면 데이터가 B화면에 들어가도록 B화면의 Data에게 데이터를 넘겨줍니다.
1-2. B화면에서 A화면으로 돌아왔을때 값을 받기 위해 함수를 넣어준다.
A화면
import UIKit
class AViewController: UIViewController{
@IBOutlet weak var sendButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func sendData(_ sender: Any) {
guard let nextVC = self.storyboard?.instantiateViewController(identifier: "BViewController") as? BViewController else { return }
nextVC.data = "Hello B화면"
//B화면 -> A화면 돌아올때 함수를 사용하기위해
nextVC.AVC = self self.navigationController?.pushViewController(nextVC, animated: true)
}
func check(str : String) -> Void {
self.dataLabel.text = str
}
}
1-3. B화면에서는 A화면에서 데이터를 넘겨주면 받을 수 있도록 준비하고, 화면이 열릴때 Label에 data가 들어가도록 해줍니다.
1-4. B화면이 뜨고 A화면의 ViewController의 함수 check함수를 호출합니다.
B화면
import UIKit
class BViewController: UIViewController {
var data : String = "" var AVC : UIViewController?
@IBOutlet weak var dataLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
dataLabel.text = data
guard let beforeVC = AVC as? AViewController else { return }
beforeVC.check(str: "nice 2 meet u")
}
}
1-4. A화면이 뜰때 VC
결과
간단합니다.
하지만 이 방법은 push, present 방식으로 화면을 전환하는 경우에만 데이터가 정상적으로 넘어갑니다.
2. segue를 이용해서 데이터 전달
아래의 과정에서 segue 실행 시 prepare 함수가 호출되는데 이 함수에서 전달하고자 하는 데이터를 함께 넘깁니다.
// segue 실행 전 전달하려는 데이터 set
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.destination is BViewController {
guard let vc = segue.destination as? BViewController else { return }
vc.data = "Hello B화면"
}
}
위의 prepare함수만 정의 해둔다면 결과는 1번과 같습니다.
3. Delegate 패턴을 이용해서 데이터 받기(중요함😎)
앞으로 delegate패턴 정말 많이 사용하게 되실겁니다.
delegate
그리고 꼭 나오는 프로토콜 채택 헷갈리시죠? 제가 정리해드릴게요.
우선 프로토콜 만들기 -> 프로토콜 채택 -> 위임하기(delegate) 3가지 단계가 필요합니다.
- 프로토콜은 메소드와 프로퍼티의 목록입니다. 설계도라고 생각하셔도 될 것 같습니다.
- delegate는 어떤 객체가 해야 하는 일을 부분적으로 확장해서 대신 처리를 해줍니다.
- 효율성 관점에서 중요합니다. 기능을 위임할 수 있는 객체가 있다는 것은 그만큼 직접 구현해야 하는 부분이 적다는 뜻이기 때문에 큰 규모의 프로그램을 빠르게 작성할 수 있습니다.
예제로 확인해보도록 하죠.
우선 데이터 전달을 하는 화면(BViewController)에 프로토콜을 만듭니다.
프로토콜에 recieveData에 대한 함수를 정의해줍니다.
그리고 delegate 변수로 객체를 지정하고 함수에 접근하도록 합니다.
protocol SendDataDelegate {
func recieveData(response : String)
}
class BViewController: UIViewController {
var delegate : SendDataDelegate?
@IBOutlet weak var labelVC: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func backBtn(_ sender: Any) {
if let text = labelVC.text {
delegate?.recieveData(response: text)
}
self.navigationController?.popViewController(animated: true)
}
}
그 다음 데이터를 받는 화면(AViewController)을 보도록 하죠.
지금 3가지 중에 2가지가 아직 안나왔죠?
위임하기, 채택하기 입니다.
채택하기부터 설명드리겠습니다. 2가지 방법이 있습니다.
class에 바로 쓰거나 extension으로 빼는 방법입니다. 아래 두가지 모두 보여드리겠습니다.
class AViewController: UIViewController, SendDataDelegate{
...
func recieveData(response: String) {
dataLabel.text = response
}
...
}
class AViewController: UIViewController {
@IBOutlet weak var dataLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func nextBtn(_ sender: Any) {
guard let nextVC = self.storyboard?.instantiateViewController(withIdentifier: "BViewController") as? BViewController else {return}
nextVC.delegate = self
self.navigationController?.pushViewController(nextVC, animated: true)
}
}
extension AViewController: SendDataDelegate {
func recieveData(response: String) {
dataLabel.text = response
}
}
아무래도 extension을 쓰면 표현하기가 쉽죠? 분리된게 수정도 쉽고 파악도 쉽고.
그리고 nextBtn의 IBAction의 중간쯤을 보시면 'nextVC.delegate = self' 코드가 보이십니까?
이 부분이 BViewController에게 위임하는 것입니다.
내가(A가) 너의(B의) 대리자가 되겠다.
이게 좀 헷갈리실 수 있는데 직관적으로 정리하자면 A에서 위임을 먼저 하고 B에가서 Back버튼과 함께 데이터를 전달해주는 것입니다.(navigation의 back버튼이 아닌 backBtn을 누르셔야 작동합니다.)
앞으로 프로토콜, delegate, extension은 정말 자주 보게 될것입니다.
4. closure를 이용한 방식
closure를 사용하는 방식이 프로퍼티에 접근하는것, delegate 패턴을 이용해서 전달하는것과 크게 형태가 다르지 않습니다.
하지만 2가지 장점이 있습니다.
- 쓰기 간결하다
- 프로토콜이나 함수의 사용없이 지역 변수 스코프 내에서 처리가 가능하다
우선 예제를 통해 보도록 하겠습니다.
전달하는 BViewController입니다.
우선 completionHandler로 closure를 정의해줬습니다.
그리고 뒤로가기를 하면 closure로 인해 데이터가 전달됩니다.
class BViewController: UIViewController {
@IBOutlet weak var labelVC: UILabel!
var completioHandler : ((String) -> (Void))?
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func backBtn(_ sender: Any) {
if let text = labelVC.text {
_ = completioHandler?(self.labelVC.text ?? "")
}
self.navigationController?.popViewController(animated: true)
}
}
이번엔 전달받는 AViewController입니다.
completionHandler를 통해 받은 text를 label에 넣어줍니다.
class AViewController: UIViewController {
@IBOutlet weak var dataLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func nextBtn(_ sender: Any) {
guard let nextVC = self.storyboard?.instantiateViewController(withIdentifier: "BViewController") as? BViewController else {return}
nextVC.completioHandler = {
text in
self.dataLabel.text = text
}
self.navigationController?.pushViewController(nextVC, animated: true)
}
}
5. NotificationCenter와 Observer pattern을 이용한 방식
- NotificationCenter는 방송국이라고 생각하시면 됩니다.
- 데이터를 송신하는 post 부분과 데이터를 수신하는 observer 부분으로 구성되어 있습니다.
- 그리고 매우 놀랍게도 제가 이부분은 작성한 글이 있습니다. 하하하하하 급 기분 좋아짐ㅎㅎ
잘 정리된 글입니다. 제가 썼거든요ㅎㅎ
https://katarnios.tistory.com/22
오늘도 긴글 읽어주셔서 감사합니다.
'Old_SWIFT(221012) > 기본이야기' 카테고리의 다른 글
알림창(UIAlertAction, Alert, ActionSheet, addTextField) 다루기 (0) | 2022.01.25 |
---|---|
UserDefaults(데이터저장) 다루기 (0) | 2022.01.24 |
화면전환하기, 화면이동하기(쉬움주의, 따라만해) (0) | 2021.12.29 |
NotificationCenter 다루기(쉬움주의, 따라만해) (0) | 2021.12.21 |
UDID 확인 방법(쉬움주의, 따라만해) (0) | 2021.12.21 |