190421-iOS-And-Swift-Study


SubmitValue file

SubmitValue-2 file


위에 링크된 submitValue 를 가지고 여러가지 다른 기능들을 넣어 구현한 것이 submitValue-2 이다

이 글은 submitValue-2를 만들면서 공부하고 그것을 정리하는 글이다


네비게이션 컨트롤러를 통해 화면 전환하면서 값 전달하기

기본적으로 값을 전달하는 방식은 present 방식과 동일하다, 다른 점이라곤 화면 전환용 메소드뿐이다.

만약 스토리보드 내에 미리 만들어 놓은 뷰 컨트롤러들이 있다면 그 뷰 컨트롤러들 맨 앞에 네비게이션 컨트롤러를 추가하는 방법은,

메뉴에서 [Editor] -> [Embed In] -> [NavigationController]를 차례로 클릭하면 맨 앞에 네비게이션 컨트롤러가 추가 된다.

그러면 이렇게 네비게이션 컨트롤러가 제일 앞에 추가가 되면, 미리 만들어 놓은 뷰 컨트롤러 중 맨 앞에 있는 뷰 컨트롤러는 루트 뷰 컨트롤러가 된다.

맨앞에 뷰 컨트롤러는 루트 뷰 컨트롤러가 되어 상단에 내비게이션 바가 내장이 된다.

내비게이션 바 위에 내비게이션 아이템을 추가하면 타이틀을 넣어 줄 수 있다.

내비게이션 바를 활용해 이동 버튼이나 검색 바 같은 항목을 배치 할 수 있다.


기존의 버튼 내비게이션 바로 옮겨 활용하기

submitValue 에서는 Submit이라는 버튼을 만들고 그 버튼을 활용하여 present로 화면을 넘겻는데, 내비게이션 컨트롤러가 추가되므로서 그 버튼이 활용하기 어려워졌다,

그래서 그 버튼을 끌어서 내비게이션 바 오른쪽으로 옮겼더니, 내비게이션 바 오른쪽에 들어갔다.

이렇게 새로 내비게이션 바에 추가된 버튼은 따로 어트리뷰트 인스펙터 탭 내에 스타일이나 시스템 아이템 등을 바꿔서 내비게이션 바에 알맞는 버튼으로 재구성 할 수 있다


내비게이션 바 안에 새로 연결한 버튼의 액션 메소드 재연결

이 글을 쓰기 전에 이미 submitValue라는 프로젝트 내에서 Submit 버튼을 만들고 그 버튼을 통해 뷰 컨트롤러간의 데이터 전달과 화면 전환을 구현했다

그런데 위에 보이듯이 Submit 버튼을 내비게이션 바 오른쪽에 넣었다, 원래있던 버튼을 내비게이션 바 위로 위치를 옮기는 과정에서 액션 메소드의 연결이 끊어진것이다.

다시말해, submitValue 프로젝트에서 Submit 버튼을 만들고 그 버튼을 눌렀을때 행동을 구현해 놓은 액션 메소드가 끊어진것이다.

그래서 새로 연결한 버튼의 액션 메소드를 재연결 해야 한다.

즉, 버튼과 메소드를 다시 연결해주어야 한다.

ViewController.swift 파일을 보조 에디터를 통해서 열면 submitValue 프로젝트 내에 구현해 놓은 액션 메소드

즉, Submit 버튼을 만들면서 구현해 놓은 메소드가 있는데 아까 말햇듯이 내비게이션 바 오른쪽으로 버튼을 옮기는 과정에서 연결이 끊어져있을 것이다

그러면 새로 옮긴 버튼을 끌어다가 다시 그 액션 메소드와 재연결을 해주면 된다.


새로 연결한 액션 메소드의 코드 수정

새로 옮긴 위치에 있는 버튼과 미리 만들어 놓았던(옮기는 과정에서 끊져진) 액션 메소드를 연결했다.

그러나 여기서 한가지 문제점이 있는데 present 를 이용해 화면을 옮기고 데이터를 전송 할 수 없다는 점이다.

다시말해, 내비게이션을 활용한 화면 전환 방식과 버튼 액션을 이용해 화면 전환 방식이 다르기 때문에 메소드도 달리 써야 한다는 말이다

그래서 화면 전환은 내비게이션 컨트롤러를 이용한 푸시 방식으로 변경해야 한다

self.present(rvc, animated: true) // present 방식 메소드 -> 지움
self.navigationController?.pushViewController(rvc, animated: true) // 내비게이션 컨트롤러를 이용한 push 방식으로 화면 전환 메소드 -> 새로 만듦

위의 코드에 보이듯이 이렇게 수정하고 내비게이션 바 오른쪽으로 옮긴 버튼을 눌러보면 이전과 다름 없이 값이 전달되며 화면이 전환되는 것을 확인 할 수 있다

이처럼 내비게이션 컨트롤러를 이용한 화면 전환 과정은 값을 전달하는 방식이 프리젠테이션 방식과 거의 같다

세그웨이를 이용한 화면 전환

내비게이션 컨트롤러를 이용한 화면 전환 과정은 값을 전달하는 방식이 프리젠테이션 방식과 거의 같지만 세그웨이를 이용한 화면 전환은 약간 다르다

화면을 전환해주는 객체인 세그웨이는 수동으로 실행할 수 있는 메뉴얼 세그웨이와 자동으로 실행되는 액션 세그웨이로 나누어진다

하지만 어느 세그웨이를 사용하든 값을 전달하는 방식은 모두 동일하다

세그웨이를 연결하는 방법은 도크 바에서 첫번째 아이콘을 + 클릭한 다음 두 번째 뷰 컨트롤러로 드래그하여 메뉴얼 세그웨이를 생성하면

세그웨이의 종류를 선택하는 창이 나타나는데 앱에 알맞는 종류를 선택하면 그 선택한 타입의 세그웨이가 만들어진다.

메뉴얼 세그웨이는 소스코드에서 직접 호출해 주어야 한다.

직접 소스 코드에서 호출해주기 위해서는 세그웨이에 아이디를 부여해야 한다

세그웨이에 아이디를 부여하는 방법은 두 뷰 컨트롤러 사이에 연결된 세그웨이를 선택하고 어트리뷰트 인스펙터 탭 내에 [Identifier] 항목 값을 원하는 아이디 문자열 값으로 넣어주면 된다


세그웨이를 실행하는 기능 구현

일단 먼저 내비게이션에 새 버튼을 추가한다, 그다음에 액션 메소드를 만들어 연결하면 된다

그러려면 다시 버튼을 만드는것 부터 해야 하므로 어짜피 만들어져 있는 버튼을 사용한다

submitValue-2 파일을 보면 내비게이션 바 오른쪽에 아까 내비게이션으로 화면 전환구현 하면서 만들어 놨던 버튼(Submit)에 액션 메소드를 끊고 새로운 메소드를 생성한다

이 새로운 메소드가 바로 세그웨이에서 활용할 액션 메소드이다

그전에 원래 있던 액션 메소드를 끊는 법을 알아야 한다, 원래 있던 버튼을 누르고 커넥션 인스펙터 탭을 열면 선택된 버튼에 연결된 메소드에 대한 연결 정보나 탭에 표시 된다

그 표시된것을 X 아이콘을 클릭하여 삭자하면 된다, 여기서 더 알아야 하는 점이 있는데 연결이 끊어진 메소드는 아에 삭제되는 것이 아니라 유지되지만, 에디터에서 속이 빈 동그라미 아이콘 모양으로 표시되어 연결이 끊어졌음을 알 수 있다.

다시말해, 에디터 내에 소스코드는 지워지지 않고 그대로 있지만 어떠한 버튼과 연결되지 않아서 아무런 기능을 하지 않는다는 말이다.

이제 연결을 끊었으니 버튼에 세그웨이를 실행하는 새로운 메소드를 연결해 주어야 한다.

먼자, 버튼을 보조 에디터로 드레그하여 @IBAction 액션 메소드를 생성하고 그 메소드 내에는 세그웨이를 실행하는 구문을 작성해주면 된다.

메뉴얼 세그웨이로 연결했으므로 메뉴얼 세그웨이를 실행하는 메소드를 써야한다

그 매소드가 바로

performSegue(withIdentifier:sender:)

이것이다.

코드 내에서 활용된 것을 보면 아래와 같다

  @IBAction func onPerformSegue(_ sender: Any) {
        self.performSegue(withIdentifier: "ManualSubmit", sender: self)
    }

세그웨이 메소드 prepare 알아보기

위에서 말햇듯이 세그웨이로 연결 후 메뉴얼 세그웨이를 실행하려면 performSegue(withIdentifier:sender:) 메소드를 사용해야 한다고 했다.

그렇다면 세그웨이를 실행하는 메소드는 구현했는데 이제는 값을 전달하는 코드는 어디에다가 구현해야 할까??

처음에는 이 performSegue(withIdentifier:sender:) 메소드에 넣으면 되겠다 라고 생각했는데 아니였다

바로 세그웨이 실행을 위한 준비 메소드 부분에 값을 전달하는 코드를 넣어줘야 한다

다시말해, 세그웨이 전처리 메소드를 활용해야 한다는 말이다

세그웨이 전처리 메소드인 prepare(for:sender:) 를 사용하면 되는데, 이 메소드는 세그웨이가 실행되기 직전게 호출되는 메소드이다

이 메소드는 우리가 직접 호출하는 것이 아니라 필요한 내용을 작성해 둔 다음 가만히 놔두면 iOS 시스템이 알아서 적절한 시점에 호출한다

메뉴얼 새그웨이나 액션 세그웨이에 구분없이 모든 세그웨이가 실행되기 전에 iOS는 prepare(for:sender:) 메소드를 먼저 호출한다

그럼 이제 저 prepare 메소드 안에 값을 전달하는 코드를 넣어야 한다는 것을 알았다 그러면 또 한가지 궁굼증이 생기는데, 값을 전달할 뷰 컨트롤러의 인스턴스 정보는 어떻게 얻어올 수 있는것일까??

submitValue 파일 안에 코드를 보면 지금까지는 값을 전달할 뷰 컨드롤러의 인스턴스를 직접 생성했다, 아래의 메소드를 이용해서

instantiateViewController(withIdentifier:)

하지만 세그웨이를 다룰 때에는 이동할 대상 뷰 컨트롤러의 인스턴스를 직접 생성해서는 안된다.

세그웨이의 목적지로 연결된 뷰 컨트롤러의 인스턴스는 세그웨이에 의해 자동으로 생성되기 때문에, 위의 메소드를 이용하여 생성한 인스턴스와 일치하지 않는다

그렇기 때문에 세그웨이 객체를 통해 생성된 뷰 컨트롤러 인스턴스 참조를 읽어와야 한다

이 정보는 prepare(for:sender:) 메소드의 첫 번째 인자값인 segue 매개변수를 통해 얻을 수 있다

func prepare(for segue: UIStoryboardSegue, sender: Any?) -> prepare의 정의

prepare 메소드를 작성하여 인스턴스를 가져오고 값을 전달하는 과정까지를 아래의 코드를 보고 이해해보자

  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        
        // 목적지 뷰 컨트롤러 인스턴스 읽어오기
        let dest = segue.destination
        
        
        
        guard let rvc = dest as? ResultViewController else {
            return
        }
        
        // 값 전달
        
        rvc.paramEmail = self.email.text! // 이메일
        rvc.paramUpdate = self.isUpdate.isOn // 자동갱신여부
        rvc.paramInterval = self.interval.value // 갱신주기
    }

위 메소드에서 일어나는 일들은 다음과 같다

  • 메소드의 매개변수 segue의 속성 destination을 이용하여 목적지 뷰 컨트롤러의 인스턴스 참조를 가져온다
  • 인스턴스 타입을 UIViewController에서 ResultViewController 타입으로 캐스팅한다. 실패하면 메소드 실행을 종료한다
  • 캐스팅된 인스턴스 상수 rvc를 이용하여 값을 전달한다

주어진 메소드가 전달받는 segue 인자값은 세그웨이 객체를 뜻한다

세그웨이는 항상 출발지와 목적지로 연결된 뷰 컨트롤러 인스턴스가 존재하는데, destination 속성을 이용하면 세그웨이가 향하는 목적지 뷰 컨트롤러의 인스턴스를 얻을 수 있다

이 인스턴스(dest)는 UIViewController 타입으로 반환되므로 ResultViewController 클래스의 프로퍼티를 사용하기 위해서는 다운 캐스팅이 필요하다

이를 통해 얻은 ResultViewController 타입의 인스턴스를 rvc 상수에 대입하고, rvc의 프로퍼티인 paramEmail, paramUpdate, paramInterval 에 알맞는 값을 대입해주면 된다

화면 이동 과정은 세그웨이가 알아서 처리하는 부분이므로 화면 전환을 위해 메소드를 직접 호출해 줄 필요는 없다