육식하는야채의 개발일지
article thumbnail

iOS에서 화면을 전환하는 방법에는 크게 2가지가 있다.

하나는 소스 코드를 통해 전환, 또 하나는 스토리보드가 제공하는 기능을 이용하여 전환하는 방식이다.

4.1 📌 iOS에서의 화면 전환 개념

iOS에서의 화면 전환 방식은 4가지 정도로 나눌 수 있다.

1. 뷰 컨트롤러의 뷰 위에 다른 뷰를 가져와 바꿔치기

2. 뷰 컨트롤러에서 다른 뷰 컨트롤러를 호출하여 화면 전환

3. 내비게이션 컨트롤러를 사용하여 화면 전환

4. 화면 전환용 객체 세그웨이(Segueway)를 사용하여 화면 전환

 

보통의 화면 전환은 뷰 컨트롤러를 호출하는 방식으로 이루어진다.

전환할 화면을 담당하는 뷰 컨트롤러의 인스턴스를 생성하고, 이를 불러들여 기존의 화면 위에 덮으면 화면 전환된다.

그렇기에 화면 전환은 새로운 화면으로 교체되는 것이 아니라, 새로운 화면이 얹어지는 것이다.

 

화면이 교체되는 것이 아니라 얹어지는 것이기 때문에 기존 화면과 새로운 화면은 서로 참조 관계가 발생한다.

이 특성 때문에 2가지 특성을 가진다.

 

1. 다음 화면으로 이동하는 방법과 이전 화면으로 돌아가는 방법이 다름

2. 화면 전환 방식에 따라 이전 화면으로 되돌아가는 방법이 다름

 

예들들어 새로운 화면으로 이동하는 것은 +1을 한 것이고 이전 화면으로 돌아가는 것은 -1을 하여 화면을 걷어내는 것이다.

하지만 이전 화면을 새롭게 호출한다면 기존 화면 + 현재 화면 + 기존 화면 호출이 되기 때문에 +2가 되는 문제가 발생한다.

 

4.2 📌 화면 전환 기법 1 : 뷰를 이용한 화면 전환

1. 하나의 뷰 컨트롤러 안에 두 개의 루트 뷰를 준비하여 교체해주는 방식으로 화면 전환하는 방식

하지만 하나의 뷰 컨트롤러가 두 개 이상의 루트 뷰를 관리해야 하므로 추천하는 방식은 아님

 

될 수 있으면 뷰를 이용한 화면 전환은 지양하는 것이 좋다.

 

4.3 📌 화면 전환 기법 2 : 뷰 컨트롤러 직접 호출에 의한 화면 전환

프레젠테이션 방식이라고 부르는 화면 전환 방식이다.

화면을 표시하는 모든 뷰 컨트롤러는 UIViewController 클래스를 상속받는데 이 클래스에서 정의된

present 메소드를 사용하면 화면을 전환할 수 있다. 

뷰 컨트롤러를 모달로 표시해주는 메소드라고 설명되어있다.

present(_:animated:completion:)

https://developer.apple.com/documentation/uikit/uiviewcontroller/1621380-present

 

present(_:animated:completion:) | Apple Developer Documentation

Presents a view controller modally.

developer.apple.com

 

메소드의 구조를 보면 아래처럼 구성되어 있는데 

첫 번째 인자값 : 새로운 화면을 담당하는 뷰 컨트롤러의 인스턴스

두 번째 인자값 : 애니메이션 효과의 유무를 표시 ( true이면 O, false이면 X)

세 번째 인자값 : 화면 전환이 완전히 끝난 후에 호출하는 실행 구문으로 보통 클로저나 함수 형식으로 입력받는다.

func present(
    _ viewControllerToPresent: UIViewController,
    animated flag: Bool,
    completion: (() -> Void)? = nil
)

 

화면 전환이 끝난 후 실행할 구문을 present메소드 다음 줄에 그냥 적지 않고 메소드 인자 값으로 입력 받는 이유는

present 메소드 다음에 작성된 구문이 화면 전환이 끝난 후 실행된다는 보장을 할 수 없기 때문이다.

 

화면 전환은 애니메이션 효과가 들어가기 때문에 시간이 걸리기도 한다.

때문에 화면 전환이 끝나기를 기다리지 않고 다음 라인에 작성된 코드가 바로 실행되기도 한다.

이러한 작업 처리를 비동기 방식이라고 부르며 화면 전환은 비동기 방식으로 동작하기 때문에 화면 전환 후 실행 할 구문을 인자값으로 넘겨주는 것이다.

 

present메소드를 이용한 화면 전환은 기존의 뷰 컨트롤러를 그대로 둔 채

그 위에 새로운 뷰 컨트롤러의 화면을 덮는 방식이다. 이런 방식을 모달 프레젠테이션 스타일이라고 부른다.

기존 화면을 VC1 새로운 화면을 VC2라고 했을 때 VC1에서는 presentedViewController 속성을 이용해 VC2를 참조할 수 있고

VC2는 presentingViewController 속성을 사용해서 VC1을 참조할 수 있다.

 

서로 참조를 왜 할까? 만약 참조할 수 있는 방법이 없다면 메모리에 로딩된 이전 화면으로 돌아가는 대신 새로운 화면을 생성해야 하는데

이러면 불필요하게 메모리가 낭비되고 이전 화면에서의 상태 정보를 유지할 수 없어서 입력했던 값이나 화면 스크롤 위치등이 초기 상태로

설정되어 사용자들이 불편함을 느낄 수 있다.

 

이전 화면으로 복귀할 때는 dismiss메소드를 사용한다.

dismiss(animated:completion:)

https://developer.apple.com/documentation/uikit/uiviewcontroller/1621505-dismiss

 

dismiss(animated:completion:) | Apple Developer Documentation

Dismisses the view controller that was presented modally by the view controller.

developer.apple.com

func dismiss(
    animated flag: Bool,
    completion: (() -> Void)? = nil
)

 

이전 화면으로 돌아가기 때문에 따로 뷰 컨트롤러 인스턴스를 인자값으로 받지 않고

애니메이션 여부와 실행 완료 후 구문을 클로저와 함수 형식으로 받는다.

 

기존 화면을 덮고 있던 새 화면을 걷어내는 것이기 때문에 

걷어낸 화면의 뷰 컨트롤러 객체는 운영체제에 의해 곧 메모리에서 해제된다.

 

주의해야 할 점❗️
화면을 걷어내는 주체가 자기 자신이 아니라는 점이다.

iOS에서 화면이 사라지게 하는 것은 사라질 뷰 컨트롤러 자신이 아니라 호출해 준 뷰 컨트롤러가 걷어내는 것이다.

VC1에서 VC2를 호출하여 화면에 표시되었다면 이전 화면으로 돌아갈 때도 VC1이 내려주는 것이라는 점이다.

VC1에게 요청을 하여 화면 복귀가 이루어진다고 생각하면 된다.

 

때문에 새로운 화면은 presentingViewController속성을 사용해 VC1(기존 화면)에 참조할 수 있기 때문에 요청을 보낼 VC1을 이 속성을 통해 참조할 수 있다.

그렇기 때문에 dismiss메소드를 호출할 때는

self.dismiss(animated:)

라고 호출하는 것이 아니라

self.presentingViewController?.dismiss(animated:)

라고 호출해야 한다.

 

[4.3.1] 화면 전환 실습

화면을 구성해주고 버튼을 눌렀을 때 두 번째 뷰 컨트롤러로 화면 전환이 되도록 하는 것이 목표이다.

프로그래밍 코드를 통해 화면 전환을 처리하려면 스토리보드에 있는 뷰 컨트롤러의 인스턴스를 소스 코드에서 참조할 수 있어야 한다.

뷰 컨트롤러를 참조하기 위해서 속성값을 사용한다.

Storyboard ID를 SecondVC라고 입력한다.

 

import UIKit

class ViewController: UIViewController {

	override func viewDidLoad() {
		super.viewDidLoad()
		// Do any additional setup after loading the view.
	}

	@IBAction func moveNext(_ sender: UIButton) {
		let uvc = self.storyboard!.instantiateViewController(withIdentifier: "SecondVC")
		
		uvc.modalTransitionStyle = UIModalTransitionStyle.coverVertical
		
		self.present(uvc, animated: true)
	}
	
}

버튼을 IBAction으로 연결하고 moveNext메소드 안에 코드를 적는다.

 

let uvc = self.storyboard!.instantiateViewController(withIdentifier: "SecondVC")

스크린에 새로 표시할 뷰 컨트롤러를 스토리보드로부터 읽어와 인스턴스화하는 부분이다.

인자 값으로 입력된 Stroyboard ID와 일치하는 뷰 컨트롤러를 찾아, 인스턴스를 생성하고 이 값을 받아온다.

self.storyboard! 이 코드는 스토리보드의 파일 내용을 참조하기 위해서 Main.storyboard를 참조하는 것을 self.storyboard! 속성을 통해 참조하는 것이다.

 

책에서는 withIdentifier라고 되어 있지만

identifier를 사용해도 된다. 차이점은 아래와 같다.

  • withIdentifier: : iOS 5.0 부터 사용된 메소드
  • identifier: iOS 13.0부터 지원되었으며, 의존성 주입에 유리함.

 

프로젝트에서 스토리보드 파일이 여러 개일 경우

self.storyboard 속성을 사용하지 않고 다른 방법으로 가져온다.

let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main
let uvc = storyboard.instantiateViewController(withIdentifier:"SecondVC")

스토리 보드의 파일명을 인자 값으로 넣어서 UIStoryboard 객체를 만들고 이 객체를 self.storyboard 속성 대신 사용한다.

storyboard 상수에 읽어들일 스토리보드 파일명을 지정하여 UIStoryboard객체를 생성하여 할당하고

이 할당된 UIStoryboard객체에서 인스턴스를 생석하고 SecondVC라는 ID를 인식해서 불러오는 uvc라는 상수에 할당한다.

 

uvc.modalTransitionStyle = UIModalTransitionStyle.coverVertical

화면 전환 스타일을 정의하는 부분이다.

UIViewController 클래스에 정의된 속성인 modalTransitionStyle은 어떤 스타일을 적용해서 전환할 것인지를 결정한다.

전환 효과는 UIModalTransitionStyle 객체에 enum 타입으로 정의되어 있다.

효과를 지정하지 않으면 기본으로 UIModalTransitionStyle.coverVertical이 적용된다.

 

self.present(uvc, animated: true)

첫 번째 인자값으로는 전환할 대상이 되는 뷰 컨트롤러 인스턴스를 입력받는다.

instantiateViewController(withIdentifier:)

이 메소드를 통해서 인스턴스를 만들어 uvc 상수에 저장했으므로 uvc를 인자 값으로 넘겨준다.

두 번째 인자값은 애니메이션을 보여줄 것인지의 여부이다. true로 설정해서 애니메이션을 설정한다.

 

수정해야하는 부분

let uvc = self.storyboard!.instantiateViewController(withIdentifier: "SecondVC")

이 구문에서 self.storyboard 값은 옵셔널 타입이다.

!를 사용해서 강제언래핑을 하고 있으므로 옵셔널 체인과 옵셔널 바인딩으로 수정하는 것이 좋다.

 

if let 사용

if let uvc = self.storyboard?.instantiateViewController(withIdentifier: "SecondVC") {
			
			uvc.modalTransitionStyle = UIModalTransitionStyle.coverVertical
			
			self.present(uvc, animated: true)
		}
	}

if let 바인딩을 소용하여 언래핑하고 nil이 아니면 안의 구문들이 실행되도록 코드를 수정한다.

 

guard문 사용

guard let uvc = self.storyboard?.instantiateViewController(withIdentifier: "SecondVC") else { return }
		
		uvc.modalTransitionStyle = UIModalTransitionStyle.coverVertical
		
		self.present(uvc, animated: true)

 

실행 결과

profile

육식하는야채의 개발일지

@육식하는야채

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!