티스토리 뷰

회원가입 페이지를 각 항목을 입력하는 화면으로 나눠서 만들고 싶어서 UIPageViewController라는 ViewController를 사용하려 했지만 막상 공부를 해 보니 예상했던 기능과는 조금 다른 용도로 사용하는 것 같다.

먼저 만들고자 하는 회원가입 절차를 생각해보자. 지금까지 만든 회원 정보 중에 처음에 사용자가 입력할 수 있는 부분은 2가지다. 이름은 텍스트로 입력을 받고 프로필 사진은 현재 라이브러리 또는 카메라로 사진을 선택할 수 있도록 한다. 이렇게 입력받은 2개의 데이터를 서버에 있는 데이터베이스에 등록하고 다음번에 로그인 할 때 이 데이터를 불러와서 화면에 그릴 수 있도록 한다.

 

테스트를 위해서 빨간 백도어 흐름으로 진행되도록 만든다.

 

이미 firebase로 연동을 했기 때문에 정상적으로 회원가입을 하려면 token이 필요하다. 하지만 테스트를 할 때 마다 구글 아이디를 새로 만들 수는 없기 때문에 테스트 데이터를 사용할 수 있도록 흐름을 조금 수정한다.

 

 

 

UIPageViewController

PageViewController는 ViewController를 화면에 보여주고, swipe 동작으로 다음 또는 이전 ViewController로 이동할 수 있다. setViewControllers는 지금 화면에 보여야 하는 view controller를 설정한다. 함수 이름에 s가 붙어 있고, 실제로 매개변수는 [UIViewController]라는 배열을 받는다. iPad UI의 경우 가로로 두었을 때 2개 이상의 page가 보일 수 있기 때문인 것 같다.

 

class SignUpPageController: UIPageViewController {
	override func viewDidLoad() {
		super.viewDidLoad()
	
		let page = UIViewController()
		self.setViewControllers([page], direction: .forward, animated: false, completion: nil)
}

 

iPhone은 기본적으로 page가 하나씩 밖에 보이지 않지만 위처럼 배열로 만들어서 전달해야 한다. direction은 방향을 뜻하며, viewControllers로 바뀔 때 animation 방향을 정할 수 있다. 특별히 page라는 view를 사용하는게 아니라 ViewController를 transition시키면서 화면에 보여주는 기능을 제공하는 controller인 것 같다.

회원가입의 경우 이름을 입력할 수 있는 ViewController, 프로필 이미지를 선택할 수 있는 ViewController를 page로 넘기면서 처리하도록 만들 것이기 때문에 이러한 ViewController를 저장할 수 있는 배열을 선언한다. UIViewController를 상속 받아서 새로운 ViewController를 정의하는 것이 좋은데, PageViewController의 dataSource에서 처리하기 쉽도록 index를 추가하기 위함이다.

dataSource는 사용자가 스크롤 조작으로 page를 넘길 때 또는 setViewController 등으로 page의 전환이 일어나야 할 때 다음 또는 이전 화면에 보여질 ViewController를 질의하는 함수들을 제공하는데, 이 함수들과 SignUpPageController에 저장한 SignUpViewController배열을 참고해서 따라 다음 또는 이전 page로 사용 될 SignUpViewController를 제공할 수 있다.

 

extension SignUpPageController: UIPageViewControllerDataSource {
	func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
		
		guard let vc = viewController as? SignUpViewController, vc.index > 0 else {
			return nil
		}

		return signUpViews[vc.index - 1]
	}

	func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
		
		guard let vc = viewController as? SignUpViewController, vc.index < signUpViews.count - 1 else {
			return nil
		}

		return signUpViews[vc.index + 1]
	}
}

 

signUpViews 배열은 회원가입이 진행됨에 따라 다음 SignUpViewController를 추가하면서 점점 크기가 커지도록 만들었다. 이러한 방법은 PageViewController의 기능과는 상관없는 내용이기 때문에 구체적인 코드는 생략하고 간략하게 그림으로 표현하면 이렇게 된다.

 

회원가입이 진행될 수록 새로운 page가 점점 추가된다.

 

각 page(SignUpViewController)는 SignUpPageController에서 관리하기 때문에 사용자가 각각 입력한 데이터들은 SignUpPageController가 모두 저장하도록 만들었다.

 

 

 

UIImagePickerController

IOS는 windows나 OSX처럼 파일 탐색기, Finder가 없다. 대신 현재 기기에 저장된 사진 파일들을 볼 수 있는 PhotoLibrary라는 제한된 파일 시스템을 제공한다. 이 파일 시스템에 접근할 수 있는 controller가 바로 UIImagePickerController이다.

 

let picker = UIImagePickerController()
picker.delegate = self
picker.sourceType = .photoLibrary
present(picker, animated: true, completion: nil)

 

일반적인 ViewController와 같이 present를 통해 화면에 보이게 할 수 있다.

UIImagePickerController는 총 3가지 sourceType이 있는데, 이미 저장된 라이브러리에 접근하는 type, 카메라로 새롭게 사진은 찍어서 source로 사용할 수 있는 type이 있다. 단, 대부분 IOS 기기가 카메라를 가지고 있지만 시뮬레이터의 경우 카메라 기능을 지원하지 않으며, 예외 상황이 생길 수 있기 때문에 해당 sourceType이 사용 가능한지 미리 검사를 하고 사용해야 한다.

 

if UIImagePickerController.isSourceTypeAvailable(.camera) {
	picker.sourceType = .camera
} else {
	picker.sourceType = .photoLibrary
}

 

delegate는 PickerController에서 사용자가 어떤 사진 파일을 선택했을 때, 취소했을 때 등 이벤트를 처리할 수 있는 대리자로 UINavigationViewContrllerDelegate와 같이 사용해야 한다. delegate를 초기화하지 않으면 사진을 선택했을 때 Controller를 dismiss 시키는 기본 delegate를 사용한다.

 

extension SignUpProfileImageController: UIImagePickerControllerDelegate, UINavigationViewControllerDelegate {
	func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
		
		if let image = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
			profileImage.image = image
			profileImage.contentMode = .scaleToFill
		}

		self.dismiss(animated: true, completion: nil)
	}
}

 

이미지를 선택했을 때 선택한 이미지에 대한 정보를 info라는 dictionary로 넘겨주는데, ImagePicker이지만 동영상이나 live photo 등 다른 미디어를 선택할 수도 있기 때문에 그에 맞는 다양한 정보가 들어있다. 위 코드에서는 editedImage를 가지고 와서 프로필 이미지로 사용했다.

이럴거면 UIMediaPicker라고 이름을 지었으면 더 괜찮지 않았을까?

delegate를 새롭게 정의해 주었기 때문에 dismiss도 직접 호출해야 한다.

 

 

 

Result

디자인에 따라 달라질 수 있는 부분들은 빼고 UIPageViewController, UIImagePickerController를 사용하는 부분만 빼서 정리했다. 이름을 입력하는 page, 프로필 사진을 선택하는 page 이렇게 두개의 page를 관리하고, 프로필 사진을 선택하는 page에서 UIImagePickerController로 사진을 선택할 수 있도록 만들었다.

 

시뮬레이터에서 구동했기 때문에 photoLibrary만 접근할 수 있다.

 

library를 선택하는 부분은 UIAlertController로 만들었는데, 위처럼 actionSheet로 그릴 경우 constraint error가 발생한다. 찾아보니 IOS 12.2 ~ 14.2 까지 있는 버그라고 한다.

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함