[SwiftUI] Interfacing with UIKit 2

원문: 튜토리얼 페이지

Section 3. Track the Page in a SwiftUI View’s State

앞서 만들어두었던 UIPageViewController에 몇 페이지 화면인지를 나타내는 UIPageControl을 붙이기 전에 PageView의 현재 페이지가 몇 페이지인지 확인할 수 있도록 수정해보자.

  1. 우선은 PageViewController에 currentPage 프로퍼티를 선언한다.
    • 이 때 currentPage는 binding 프로퍼티로 선언해야 한다. 그리고 updateUIViewController 내에 사용하던 setViewController에도 [currnentPage]로 접근할 수 있도록 수정한다.

PageViewController에 사용되는 currentPage가 @Binding으로 선언되었다면 어딘가에 @State로 선언된 currentPage가 존재할 터.

  1. 이 프로퍼티는 PageView 파일에 선언해준다.
    • 추후에 UIPageControl에서도 이 값을 사용하기 위해서는 UIPageVC와 UIPageControll의 공통 조상에 선언되어야 한다.

  1. PageView에 currentPage 요소를 나타낼 수 있도록 Text를 추가한다.
    • VStack에 넣어서 아래 Text만 추가한다. Page를 움직여도 currentPage가 변화하진 않는다.

image

그렇다면 currentPage의 숫자를 변경하려면 어떻게 해야하는가! 그것은 4단계에서 확인할 수 있다.

  1. pageViewController(_:didFinishAnimating:previousViewControllers:transitionCompleted completed: Bool) 구현하기
    • 요 메서드는 UIPageViewControllerDelegate에 있는 메서드로, 우리의 PageViewController 내의 Coordinator에서 구현해준다.
    • dataSource 지정을 해준 것처럼 delegate도 makeUIViewController에서 context.coordinator로 지정해주어야 한다.
   func pageViewController(_ pageViewController: UIPageViewController,
                            didFinishAnimating finished: Bool,
                            previousViewControllers: [UIViewController],
                            transitionCompleted completed: Bool) {
        if completed,
           let visibleViewController = pageViewController.viewControllers?.first,
           let index = controllers.firstIndex(of: visibleViewController) {
            parent.currentPage = index
        }
    }

page를 움직이는 animating이 끝나면 currentPage를 노출된 viewController의 index로 업데이트 해준다. Binding 된 값이기 때문에 currentPage가 변경되면 아래처럼 Text도 변한다.

Section 4. Add a Custom Page Control

이제 currentPage가 변화하는 것도 확인했으니 진짜 UIPageControl을 붙여보자. UIPageControl은 UIView에 속하는 친구이기 때문에 UIViewRepresentable 을 준수하는 struct로 구현해야 한다.

  1. PageControl.swift 생성
    • UIViewRepresentable을 준수해야 한다. UIViewControllerRepresentable과 구현해야 할 메서드는 같다. (life cycle도 같다.)
import SwiftUI
import UIKit

struct PageControl: UIViewRepresentable {
    var numberOfPages: Int
    @Binding var currentPage: Int
    
    func makeUIView(context: Context) -> UIPageControl {
        let control = UIPageControl()
        control.numberOfPages = numberOfPages
        
        return control
    }
    
    func updateUIView(_ uiView: UIPageControl, context: Context) {
        uiView.currentPage = currentPage
    }
}

UIPageControl을 반환하고, control이 표기해야 할 page 수는 외부에서 주입받는다. currentPage는 PageView의 currentPage를 바인딩한 값을 사용한다.

  1. PageView에 PageControl 추가하기
    • VStack을 ZStack으로 변경해 PageViewController 위에 PageControl이 노출되도록 한다.

image

  1. PageControl을 눌렀을 때도 이동하도록 수정
    • 2번까지 구현하고 나면 화면의 PageControl을 눌렀을 때 PageControl은 변경되지만 이미지는 변경되지 않기 때문에 정상적으로 화면이 넘어갈 수 있도록 currentPage를 업데이트 해 줄 필요가 있다.
    • 업데이트를 할 때는 Coordinator를 선언해 해당 클래스 내에서 값을 조작하도록 한다.

image

valueChange일 때 updateCurrentPage가 호출되도록 makeUIView에서 addTarget을 사용해 control에 동작을 지정해주었다.

  1. CategoryHome에서 임시로 넣어두었던 이미지 대신 PageView를 추가한다.

image

최종적으로 실행하면 다음과 같이 동작한다.

마무리

watchOS와 macOS 부분을 제외하면 SwiftUI Tutorial에 있는 모든 내용을 다 정리했다. SearchApp을 만들어보는 데 아주 큰 도움이 됐다. 확실히 튜토리얼을 끝내고 나니까 조금은 swiftUI에 적응이 된 것 같은 느낌? 정확히 알아서 내 것으로 만드는 게 중요하겠지만 일단은 익숙해진 것만으로도 뿌듯하다 😎

이제 SearchApp 마무리 지으러 가봐야겠다!!

Leave a comment