[swift] 다른 페이지로 직접 데이터 전달하기


1️⃣ 목표

  • 첫번째 뷰에서 두번째 뷰데이터를 전달하는 방법에 대해 알아볼 예정입니다.
  • 먼저 첫번째 뷰에서 버튼을 클릭하면 present메서드를 이용하여 두번째 뷰가 열림과 동시에 데이터를 전달하도록 구현했습니다.

base code

  • 단, User Defauls**, 커스텀 plist, DB를 이용하지 않습니다.
  • 뷰컨트롤은 스토리보드에서 구현했기 때문에 다음과 같이 두번째 뷰컨트롤러를 불러왔습니다.

initialrize storyboard

2️⃣ 최초로 열어줄 페이지에 데이터 전달하기

  • 부제목이 “최초로 열어줄 페이지”라고 썼지만 사실 한페이지위에서 다른페이지를 열어줄 때를 뜻합니다. 다른페이지에서 dismiss()와 같은 메서드로 되돌아가면 자동으로 인스턴스 메모리를 해제시키기 때문에 또다시 다른페이지를 열더라도 최초로 열어준 것 처럼 동작할 것 이기 때문입니다.
  • 그리고 다음과 같이 세가지 방법으로 선언된 변수를 비교하는데 있어서 중요한 요소는 최초로 페이지를 열었을 때만 동작하는 함수인 viewDidLoad()이기 때문입니다.

base code

(1) 스토리뷰 Label: 스토리뷰에서 생성해준 라벨
(2) 코드Label: 코드로 만든 Label. viewDidLoad()에서 UILabel인스턴스가 할당.
(3) 클래스코드Label: 코드로 만든 Label. 클래스레벨에서 UILabel인스턴스가 할당.

(1) 단순하게 전달하기

  • 먼저 다음과 같이 첫번째 뷰컨트롤러에서 데이터를 전달해준 뒤 두번째 뷰컨트롤러present메서드를 이용하여 열어줬습니다.
@IBAction func nextPageBtn(_ sender: Any) {
    /* 중략 */
    vc.storyLabel?.text = "바뀐 스토리 Label"
    vc.codeLabel?.text = "바뀐 코드 Label"
    vc.classLevelCodeLabel.text = "바뀐 클래스코드 Label"
    self.present(vc, animated: true, completion: nil)
}

all fail

  • 아쉽게도 세가지 Label 모두 값이 변경되지 않았습니다.
  • 비교를 하기 이전에 실수한점이 있었습니다. 두번째 뷰컨트롤러에서 viewDidLoad()함수에서 코드로 구현했던 2번째, 3번째 라벨의 텍스트를 변경해 줬습니다. present함수가 호출된 이후에 화면이 로드된다는 것을 생각해보면 당연한 결과입니다.
  • 다음과 같이 텍스트변경코드를 주석처리하고 다시 실행해봤습니다.

modify comment third label succeed

  • 이번에는 3번째 Label만이 값이 변경되었습니다.
  • 다시한번 두번째뷰컨트롤러에서 변수가 선언된 모습을 보겠습니다.

Declaration value

  • 2번째 Label은 클래스레벨에서 타입만 지정되어있을 뿐 viewDidLoad()메서드 안에서 인스턴스를 할당받습니다. 결국 이전에 실패한 이유와 동일합니다.
  • 반면 3번째 Label클래스레벨에서 선언되었습니다. 즉, 다른 언어의 클래스문법과 동일하게 첫번째 뷰컨트롤러에서 두번째 뷰컨트롤러의 인스턴스를 생성하는 시점에서 인스턴스변수들이 생성됩니다.
  • 스토리보드로 직접 만들어준 첫번째 Label은 왜 바뀌지 않았을까요?

(2) 스토리보드적용시점 생각해보기

  • 스토리보드로 직접 만들어준 첫번째 Label 또한 클래스레밸에서는 인스턴스 생성없이 UILabel!로 타입선언만 되어있기 때문에 당연한 결과일 수 도 있습니다.
  • 하지만 swift언어에서 스토리보드 개념을 처음 접한 입장에서 스토리보드가 언제 적용되는지 알 수 없었습니다. 타입만 선언되었지만 스토리보드만의 특수한 힘(?)으로 인스턴스 생성과정에서 적용될 것 같다는 생각을 했습니다. 그렇기 때문에 Label이 바뀌지 않은 이유를 다음과 같이 2가지로 생각할 수 있습니다.
    1. UILable()인스턴스는 생성됐지만 인터페이스설정이 로드된 이후에 적용되기 때문에 텍스트가 덮혀씌워졌기 때문
    2. 스토리보드적용은 뷰가 로드된 이후에 적용되기 때문
  • 둘중 맞는 이유를 확인하는 방법은 간단합니다. 다음과 같이 데이터를 적용하는 시점에서 !키워드를 적용해서 실행해보면 됩니다.

Declaration value

  • 위의 오류를 통해 두번째 컨트롤뷰를 생성한 이후에도 스토리보드를 통해 생성된 아웃렛변수에 값이 nil임을 알 수 있습니다.
  • 결론적으로 스토리보드에서 만들어준 뷰가 로드되는 시점에 초기화된 것 입니다.

(3) 라이프사이클, 코드의 절차적인 처리 우선순위?

  • 지금까지의 과정을 통해 swift언어라이프사이클, 스토리보드 적용시점등등을 고려해야해서 코드를 작성해 줘야합니다.
  • 그렇다면 코드의 절차적 처리의 특성을 이용해서 present메서드로 뷰를 호출한 후에 데이터를 변경해준다면 어떻게 될까요?
 @IBAction func nextPageBtn(_ sender: Any) {
    /* 중략 */
    self.present(vc, animated: true, completion: nil) // 먼저 호출
    vc.storyLabel?.text = "바뀐 스토리 Label"
    vc.codeLabel?.text = "바뀐 코드 Label"
    vc.classLevelCodeLabel.text = "바뀐 클래스코드 Label"
}

all fail

  • 놀랍게도 호출 순서를 바꿔준 것 만으로 모든 라벨의 정상적으로 변경되었습니다.
  • 하지만 이것이 코드의 절차적인 처리라이프 사이클보다 우선순위기 때문이라고 생각하기는 힘들 것 같습니다. 만약 우선순위 비교가 가능하다면 좀 전에 viewDidLoad()메서드호출 -> 데이터전달 -> 뷰로드순으로 적용된 것을 설명 못합니다.
  • 오히려 “present를 이용한 뷰를 로드하는 과정이 비동기로 처리되는데 라이프사이클단계중 viewDidLoadview(Will)Appear사이에 데이터가 변경되었기 때문”이라고 생각하는게 맞을 수도 있을 것 같습니다. 아직 이 부분에 대해서는 좀 더 공부가 필요할 것 같습니다.

(4) 베스트 프렉티스

  • 개인적인 입장일 수도 있지만 단순히 present를 먼저 호출해서 처리하는 것은 안심할 수 없을 것 같습니다. 또한 실수로 present메서드를 더 늦게 호출할 수도 있습니다.
  • 하지만 각 클래스인스턴스 변수들은 생성직후 생성된 다는 것은 확실합니다.
  • 이점을 이용하여 다음코드와 같이 을 대신 받아줄 인스턴스 변수를 만들어준 뒤 viewDidLoad()함수에서 값을 옮겨주는 방법을 쓰면 안전하게 사용할 수 있을 것 같습니다.

best practice

  • 추가로 스토리보드에서 생성한 아웃렛 변수private키워드를 붙여주면 외부에서 참조를 못해 좀 더 안전하게 사용할 수 있습니다.
  • 코드로 구현한 라벨의 경우도 동일하게 사용하거나, 클래스레벨에서 인스턴스를 생성해주는 방식으로 사용하면 될 것같습니다.

3️⃣ 반대 페이지로 데이터 전달할 때

  • 이번에는 첫번째페이지 -> 두번째페이지 -> 첫번째페이지의 순서로 돌아오면서 첫번째 페이지데이터를 전달하는 것에 대해 생각해보겠습니다.

return page and push data

  • 이 과정에서 첫번째 컨트롤러viewDidLoad()는 다시 호출되지 않습니다. 그렇기 때문에 viewwillAppear()에서 값을 옮겨줘야 합니다.
  • 이전에 이와 관련된 포스트가 있기 때문에 자세한 설명은 생략 하겠습니다.

관련 포스트👉🏻👉🏻👉🏻 present, dismiss를 이용한 페이지 전환에서 viewWillAppear메서드가 호출이 안되는 문제 해결하기

  • 그리고 이번경우에는 첫번째 뷰가 한번 호출된 후 이기 때문에 아웃렛변수에 직접 값을 대입해줘도 됩니다.
  • 하지만 통일성 있는 코드를 위해서는 동일하게 임시 변수를 선언하여 사용하고 라이프사이클관련 함수를 이용하는 것이 좋을 것 같다고 생각합니다.




© 2021.02. by kirim

Powered by kkrim