Language/Objective-C & Swift | 2021. 9. 13. 20:51

본 게시글은 How To: Pass Data Between View Controllers in Swift를 바탕으로 작성하였습니다.

 

여러분의 앱이 여러 개의 사용자 인터페이스(UI)를 가지고 있다면 여러분은 하나의 UI에서 다른 UI로 데이터를 전달해야 하는 경우도 생길 것입니다. Swift에서는 뷰 컨트롤러(View Controller) 사이에 어떤 방법으로 데이터를 전달할 수 있을까요?

뷰 컨트롤러 사이에 데이터를 주고 받는 것은 iOS 개발의 중요한 일부입니다. 여러분은 몇 가지 방법으로 이를 해낼 수 있고 각기 다른 이점과 약점을 가지고 있습니다.

뷰 컨트롤러 사이에 쉽게 데이터를 교환하는 방법을 선택하는 것은 여러분이 앱 구조를 어떻게 할 것인지에 달려 있습니다. 앱의 구조(App architecture)는 여러분이 뷰 컨트롤러 사이에 어떻게 작동이 이루어 질 것인지에 영향을 주고, 반대로 여러분이 뷰 컨트롤러 사이에 데이터 교환을 어떻게 할 것인지에 따라 앱의 구조가 달라집니다.

 

Swift에서 여러분은 뷰 컨트롤러 사이에 다음의 6가지 방법으로써 데이터를 주고 받을 수 있습니다.

  1. 인스턴스 프로퍼티(property)를 사용하는 방법(A → B 방향)
  2. 스토리보드(Storyboard)와 세그웨(segue)를 사용하는 방법
  3. 인스턴스 프로퍼티와 함수를 사용하는 방법(A ← B 방향)
  4. 델리게이션(delegation) 패턴을 사용하는 방법
  5. 클로저(closure) 또는 핸들러(completion handler)를 사용하는 방법
  6. NotificationCenter 또는 Observer 패턴을 사용하는 방법

 

이 게시글에서 여러분은 뷰 컨트롤러 사이에 데이터를 주고 받는 6가지의 각기 다른 방법에 대해 익히게 될 것입니다. 이 방법에는 프로퍼티(property)를 사용하는 방법, 세그웨(segue)를 사용하는 방법 및 NSNotificationCenter를 사용하는 방법도 들어 있습니다. 비교적 간단한 이들 방법을 통해 좀 더 복잡한 응용까지 나아갈 수 있습니다. 준비가 되었다면 이제 시작하겠습니다.

 

프로퍼티를 사용하여 뷰 컨트롤러 사이에 데이터를 주고 받기(A → B 방향)

이미 언급했듯이 일부 접근법들은 데이터가 한 뷰 컨트롤러에서 다른 뷰 컨트롤러로 전달되는 단방향성을 갖습니다. 말하자면 그런 일부의 방법은 대칭(bilateral)적이지 않다는 것입니다. 하지만 염려할 것은 없습니다. 대부분의 경우 여러분은 단방향 통신을 필요로 할 것이기 때문입니다.

뷰 컨트롤러 A(현재의 화면)에서 뷰 컨트롤러 B(다음으로 넘어갈 화면)로 데이터를 넘기는 가장 쉬운 방법은 프로퍼티(property)를 사용하는 방법입니다. 프로퍼티는 그 클래스의 일부인 변수입니다. 그러한 클래스로부터 만들어진 모든 인스턴스는 따로따로 프로퍼티를 가지고 있고 여러분은 여기에 값을 배정할 수 있습니다. 다른 클래스와 마찬가지로 UIViewController의 파생형인 뷰 컨트롤러들은 프로퍼티를 가질 수 있습니다.

프로퍼티를 사용하여 뷰 컨트롤러 사이에 데이터를 전달하기.

다음과 같이 text라는 이름을 가진 프로퍼티가 있는 뷰 컨트롤러인 MainViewController가 있습니다.

// Swift
class MainViewController: UIViewController {
    var text: String = ""
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}
// Objective-C
@interface MainViewController: UIViewController {
    @property (atomic, strong) NSString * text;
}

@implementation MainViewController {
    @synthesize text
    
    -(void) viewDidLoad {
        [super viewDidLoad];
    }
}

 

여러분이 MainViewController 클래스 인스턴스를 만들 때마다, 여러분은 다음과 같이 text 프로퍼티에 값을 배정할 수 있습니다.

// Swift
let mainViewController = MainViewController()
mainViewController.text = "Lorem Ipsum"
// Objective-C
MainViewController * mainViewController = [[MainViewController alloc] init];
[mainViewController setText: @"Lorem Ipsum"];

 

참 쉽죠? 이제 이 뷰 컨트롤러가 내비게이션 컨트롤러(Navigation Controller)의 일부라고 가정하고, 이 뷰 컨트롤러에서 다음에 등장할 뷰 컨트롤러로 어떤 값을 전달할 것입니다.

먼저 여러분이 스토리보드(Storyboard)를 사용하고 있다면 이 MainViewController에다가 내비게이션 컨트롤러를 삽입하고 싶을 것입니다. 스토리보드를 사용하고 있지 않다면 여러분은 내비게이션 컨트롤러를 새로 생성한 다음 MainViewController를 그것의 root view controller로 지정할 수 있습니다. 이미 내비게이션 컨트롤러가 적용되어 있다면 완벽합니다!

그 다음 여러분은 새로운 뷰 컨트롤러를 생성하기 위하여 .xib 파일도 생성하고, 서브 클래스도 생성합니다. 이것은 [File] 메뉴에서 [New File...] 항목을 선택 후 "Cocoa Touch Class"를 선택하여 SecondaryViewController라는 이름을 적음으로써 할 수 있습니다. .xib 파일이 생성될 때 "Also create XIB file" 체크박스에 체크하는 것을 절대 잊지 말기를 바랍니다.

뷰 컨트롤러에 대한 소스 코드는 다음과 같습니다.

class SecondaryViewController: UIViewController {
    var text: String = ""
    @IBOutlet weak var textLabel: UILabel!
    override func viewDidLoad() {
        super.viewDidLoad()
        textLabel.text = text
    }
}
@interface SecondaryViewController: UIViewController {
    @property (nonatomic, weak) NSString * text;
}
@implementation SecondaryViewController {
    @synthesize text
    
    -(void)viewDidLoad {
        [super viewDidLoad];
        [self textLabel.setText: [self text]];
    }
}

 

.xib 파일에서 뷰(view)에 UILabel 요소를 추가한 다음 그 요소를 textLabel에 연결합니다. 그러면 여러분은 뷰 컨트롤러에 textLabel라는 이름의 프로퍼티도 가지고 있고, 그 프로퍼티에 연결된 Label 요소도 갖게 됩니다.

위 소스 코드에서도 보다시피 viewDidLoad() 메소드에서 textLabel에 있는 text 프로퍼티는 SecondViewController에 있는 text로부터 할당된 값입니다.

다음 코드에서는 실제로 데이터를 전해주고 있습니다. MainViewController에 다음과 같은 메소드를 추가합니다.

// Swift
@IBAction func onButtonTap() {
    let secondaryViewController(nibName: "SecondaryViewController", bundle: nil)
    secondaryViewController.text = "Lorem Ipsum"
    navigationController?.pushViewController(secondaryViewController, animated: true)
}
// Objective-C
-(IBAction)onButtonTap {
    SecondaryViewController * secondaryViewController = [[SecondaryViewCollector alloc] initWithNibName: "SecondaryViewController" 
                                                                                                 bundle: nil];
    [secondaryViewController setText: @"Lorem Ipsum"]
    [[self navigationController] pushViewController: secondaryViewController
                                           animated: TRUE];
}

MainViewController의 인터페이스에서 버튼을 추가하고 위의 액션에 이를 연결시킨다면, 버튼을 클릭했을 때 코드가 실행될 것입니다. 위 코드를 통해 어떤 일이 벌어지는지를 살펴보겠습니다.

secondaryViewController라는 이름의 변수를 선언하고 여기에 SecondaryViewController 클래스형 인스턴스를 대입합니다. 이 때 여러분은 뷰 컨트롤러가 올바른 XIB 파일을 사용하고 있음을 보증하기 위하여 생성자를 호출할 때 XIB의 정확한 이름을 명시하게 됩니다.

그 다음 secondaryViewControllertext 프로퍼티에 문자열을 대입합니다. 이 때가 뷰 컨트롤러 사이에 실질적으로 데이터를 교환하는 순간입니다!

마지막으로 pushViewController(_:animated:)를 사용하여 내비게이션 스택에 새로 만들어진 뷰 컨트롤러를 푸쉬(push)합니다. 이 때 발생하는 화면 변화에는 애니메이션이 지정되어 있으므로 여러분은 새로 만들어진 뷰 컨트롤러가 단말기 화면의 우측에서 미끄러져 들어오는(slide in) 효과를 보게 될 것입니다.

프로퍼티를 사용하여 뷰 컨트롤러 A에서 뷰 컨트롤러 B로 데이터를 전달하는 방법에 대해 다시 한 번 정리해보겠습니다.

  • 제1단계. 뷰 컨트롤러 B(데이터를 받아서 나중에 나타나는 화면)에서 데이터를 받기 위한 프로퍼티를 선언합니다. 위 예제에서 그 프로퍼티는 text라는 이름을 가졌습니다.
  • 제2단계. 뷰 컨트롤러 B에서 데이터가 변경될 때 수행할 작업을 기술합니다. 위 예제에서 여러분은 text를 통해 받은 문자열을 label로 출력시켰습니다.
  • 제3단계. 뷰 컨트롤러 A(먼저 나타나 있고 데이터를 넘겨 줄 화면)에서 뷰 컨트롤러 B에 대한 새 인스턴스를 만든 다음 프로퍼티에 소정의 값을 설정합니다. 그리고 내비게이션 스택으로 푸쉬(push)합니다.

여기까지가 프로퍼티를 사용한 방법이었습니다. 이어서 스토리보드에서 세그웨(segue)를 사용하여 동일한 기능을 구현해보겠습니다.

댓글