반응형

원문 : Framework Intergration - Interfacing with UIKit

Interfacing with UIKit

SwiftUI는 모든 Apple 플랫폼에서 기존 UI 프레임워크와 원활하게 동작합니다. 예를 들어, SwiftUI 뷰 내부에 UIKit 뷰와 뷰 컨트롤러를 배치할수 있고 그 반대도 마찬가지입니다. 

이 튜토리얼은 홈 화면에서 특정 랜드마크를 UIPageViewController와 UIPageControl를 감싼 인스턴스로 변환하는 방법을 보여줍니다. SwiftUI 뷰의 회전메뉴를 보여주기 위해 UIPageViewController을 사용할 것이고, 상태 변수와 바인딩을 사용해서 사용자 인터페이스 전체에서 데이터 업데이트를 조정합니다.

이 프로젝트를 빌드하기 위해 다음 단계를 따르거나, 완성된 프로젝트를 다운로드 받아 살펴보세요.

프로젝트 파일 다운로드

Section 1. UIPageViewcontroller을 표현하는 뷰 만들기(Create View to Represent a UIPageViewController)

SwiftUI에서 UIKit 뷰와 뷰 컨트롤러를 표현하기 위해서, UIViewRepresentable과 UIViewControllerRepresentable 프로토콜을 준수하는 타입을 만듭니다. 여러분의 사용자정의 타입은 표현하고자 하는 UIKit 타입을 만들고 구성하며, SwiftUI는 생명주기를 관리하고 필요할때 업데이트합니다.

1 단계

PageViewController.swift라는 이름을 가진 SwiftUI 뷰 파일을 만들고, UIViewControllerRepresentable을 준수하는 PageViewcontroller 타입을 선언합니다.

페이지 뷰 컨트롤러는 UIViewController 인스턴스의 배열을 저장합니다. 그것들은 랜드마크 간에 스크롤하는 페이지들입니다.

import SwiftUI
import UIKit

struct PageViewController: UIViewControllerRepresentable {
    var controllers: [UIViewController]
}

다음으로, UIViewControllerRepresentable 프로토콜에 대한 2개의 요구사항을 추가합니다.

2 단계

원하는 구성으로 UIPageViewController을 만드는 makeUIViewController(context:) 메소드를 추가합니다.

SwiftUI는 뷰가 보여질 준비가 되었을때, 이 메소드를 한번 호출하고, 그리고나서 뷰 컨트롤러의 생명주기를 관리합니다.

import SwiftUI
import UIKit

struct PageViewController: UIViewControllerRepresentable {
    var controllers: [UIViewController]

    func makeUIViewController(context: Context) -> UIPageViewController {
        let pageViewController = UIPageViewController(
            transitionStyle: .scroll,
            navigationOrientation: .horizontal)

        return pageViewController
    }
}

3 단계

배열의 첫번째 뷰 컨트롤러를 표현하기 위해, updateUIViewController(_:context:) 메소드에 setViewControllers(_:direction:animated:)를 호출을 추가합니다.

import SwiftUI
import UIKit

struct PageViewController: UIViewControllerRepresentable {
    var controllers: [UIViewController]

    func makeUIViewController(context: Context) -> UIPageViewController {
        let pageViewController = UIPageViewController(
            transitionStyle: .scroll,
            navigationOrientation: .horizontal)

        return pageViewController
    }

    func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
        pageViewController.setViewControllers(
            [controllers[0]], direction: .forward, animated: true)
    }
}

다른 SwiftUI 뷰를 만들어서 UIViewControllerRepresetable뷰를 표현합니다.

4 단계

PageView.swift 이름의 새로운 SwiftUI 뷰 파일을 만들고, PageViewController를 자식 뷰처럼 선언하기 위해 PageView 타입을 업데이트합니다. 

일반적인 초기화가 뷰의 배열을 가지고 오는 것을 주의하고, 각 항목을 UIHostingController로 만듭니다. UIHostingController는 UIKit 문맥상에 SwiftUI 뷰을 나타내는 UIViewController의 하위클래스 입니다.

import SwiftUI

struct PageView<Page: View>: View {
    var viewControllers: [UIHostingController<Page>]

    init(_ views: [Page]) {
        self.viewControllers = views.map { UIHostingController(rootView: $0) }
    }

    var body: some View {
        PageViewController(controllers: viewControllers)
    }
}

struct PageView_Preview: PreviewProvider {
    static var previews: some View {
        PageView()
    }
}

5 단계

미리보기 공급자를 업데이트해서 필요한 뷰의 배열을 전달하고, 미리보기를 시작합니다. 

import SwiftUI

struct PageView<Page: View>: View {
    var viewControllers: [UIHostingController<Page>]

    init(_ views: [Page]) {
        self.viewControllers = views.map { UIHostingController(rootView: $0) }
    }

    var body: some View {
        PageViewController(controllers: viewControllers)
    }
}

struct PageView_Preview: PreviewProvider {
    static var previews: some View {
        PageView(features.map { FeatureCard(landmark: $0) })
            .aspectRatio(3/2, contentMode: .fit)
    }
}

6 단계

계속하기 전에 캔버스에 PageView 미리보기를 고정합니다. - 이 뷰는 어디에서든 동작합니다.

Section 2. 뷰 컨트롤러의 데이터 소스 만들기(Create the View Controller’s Data Source)

짧은 몇 단계에서, 여러분은 많은 것을 했습니다 - PageViewController는 UIPageViewController을 사용해서 SwiftUI 뷰에서 SwiftUI 컨텐츠를 보여줍니다. 이제 페이지에서 페이지로 스와이핑으로 이동이 가능해야 합니다.

UIKit 뷰 컨트롤러를 표현하는 SwiftUI 뷰는 SwiftUI 관리하는 Coordinator 타입을 정의할수 있고 표현가능한(representable) 뷰의 컨텍스트의 일부로 제공됩니다.

1 단계

PageViewController에서 중첩된 Coordinator 클래스를 선언합니다.

SwiftUI는 UIViewControllerRepresentable 타입의 조정자(coordinator)를 관리하고 위에서 만들었던 메소드를 호출할때 컨텍스트의 일부로 제공합니다. 

import SwiftUI
import UIKit

struct PageViewController: UIViewControllerRepresentable {
    var controllers: [UIViewController]

    func makeUIViewController(context: Context) -> UIPageViewController {
        let pageViewController = UIPageViewController(
            transitionStyle: .scroll,
            navigationOrientation: .horizontal)

        return pageViewController
    }

    func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
        pageViewController.setViewControllers(
            [controllers[0]], direction: .forward, animated: true)
    }

    class Coordinator: NSObject {
        var parent: PageViewController

        init(_ pageViewController: PageViewController) {
            self.parent = pageViewController
        }
    }
}

320

 

2 단계

coordinator를 만들기 위해 PageViewController에 다른 메소드를 추가합니다.

SwiftUI는 makeUIViewController(context:) 전에 makeCoordinator() 메소드를 호출하며, 뷰 컨트롤러를 구성할때 coordinator 객체를 사용합니다.


coordinator를 사용해서 델리게이터, 데이터소스, 대상-동작으로 사용자 이벤트 응답 등 일반적인 Cocoa 패턴을 구현할 수 있습니다.

import SwiftUI
import UIKit

struct PageViewController: UIViewControllerRepresentable {
    var controllers: [UIViewController]

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIViewController(context: Context) -> UIPageViewController {
        let pageViewController = UIPageViewController(
            transitionStyle: .scroll,
            navigationOrientation: .horizontal)

        return pageViewController
    }

    func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
        pageViewController.setViewControllers(
            [controllers[0]], direction: .forward, animated: true)
    }

    class Coordinator: NSObject {
        var parent: PageViewController

        init(_ pageViewController: PageViewController) {
            self.parent = pageViewController
        }
    }
}

3 단계

Coordinator 타입에 UIPageViewControllerDataSource를 준수하도록 추가하고 두개의 필수 메소드를 구현합니다.

이 두 메소드들은 뷰 컨트롤러들 간의 관계를 형성하며, 그것들 사이를 스와이프로 왔다갔다 할 수 있습니다.

import SwiftUI
import UIKit

struct PageViewController: UIViewControllerRepresentable {
    var controllers: [UIViewController]

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIViewController(context: Context) -> UIPageViewController {
        let pageViewController = UIPageViewController(
            transitionStyle: .scroll,
            navigationOrientation: .horizontal)

        return pageViewController
    }

    func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
        pageViewController.setViewControllers(
            [controllers[0]], direction: .forward, animated: true)
    }

    class Coordinator: NSObject, UIPageViewControllerDataSource {
        var parent: PageViewController

        init(_ pageViewController: PageViewController) {
            self.parent = pageViewController
        }

        func pageViewController(
            _ pageViewController: UIPageViewController,
            viewControllerBefore viewController: UIViewController) -> UIViewController?
        {
            guard let index = parent.controllers.firstIndex(of: viewController) else {
                return nil
            }
            if index == 0 {
                return parent.controllers.last
            }
            return parent.controllers[index - 1]
        }

        func pageViewController(
            _ pageViewController: UIPageViewController,
            viewControllerAfter viewController: UIViewController) -> UIViewController?
        {
            guard let index = parent.controllers.firstIndex(of: viewController) else {
                return nil
            }
            if index + 1 == parent.controllers.count {
                return parent.controllers.first
            }
            return parent.controllers[index + 1]
        }
    }
}

4 단계

coordinator를 UIPageViewController의 데이터 소스로 추가합니다.

import SwiftUI
import UIKit

struct PageViewController: UIViewControllerRepresentable {
    var controllers: [UIViewController]

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIViewController(context: Context) -> UIPageViewController {
        let pageViewController = UIPageViewController(
            transitionStyle: .scroll,
            navigationOrientation: .horizontal)
        pageViewController.dataSource = context.coordinator

        return pageViewController
    }

    func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
        pageViewController.setViewControllers(
            [controllers[0]], direction: .forward, animated: true)
    }

    class Coordinator: NSObject, UIPageViewControllerDataSource {
        var parent: PageViewController

        init(_ pageViewController: PageViewController) {
            self.parent = pageViewController
        }

        func pageViewController(
            _ pageViewController: UIPageViewController,
            viewControllerBefore viewController: UIViewController) -> UIViewController?
        {
            guard let index = parent.controllers.firstIndex(of: viewController) else {
                return nil
            }
            if index == 0 {
                return parent.controllers.last
            }
            return parent.controllers[index - 1]
        }

        func pageViewController(
            _ pageViewController: UIPageViewController,
            viewControllerAfter viewController: UIViewController) -> UIViewController?
        {
            guard let index = parent.controllers.firstIndex(of: viewController) else {
                return nil
            }
            if index + 1 == parent.controllers.count {
                return parent.controllers.first
            }
            return parent.controllers[index + 1]
        }
    }
}

5 단계

라이브 미리보기를 켜고 스와이프(swip) 동작을 테스트합니다.

https://docs-assets.developer.apple.com/published/2fa551fd64/_p42_swipe_interactions.mp4

Section 3. SwiftUI 뷰의 상태에서 페이지 추적하기(Track the Page in a SwiftUI View’s State)

사용자정의 UIPageControl을 추가를 준비하기 위해, PageView내의 현재 페이지를 추적하는 방법이 필요합니다.

이를 위해서, PageView에 @State 속성을 추가할 것이고, 이 속성에 대한 바인딩을 PageViewController 뷰로 전달합니다. PageViewController는 보이는 페이지와 일치하도록 바인딩을 업데이트 합니다.

1 단계

PageViewController의 속성처럼 currentPage 바인딩을 추가해서 시작합니다.

@Binding 속성을 선언 하는 것 외에, setViewControllers(_:direction:animated:) 호출을 업데이트 하며, currentPage 바인딩의 값을 전달합니다.

import SwiftUI
import UIKit

struct PageViewController: UIViewControllerRepresentable {
    var controllers: [UIViewController]
    @Binding var currentPage: Int

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIViewController(context: Context) -> UIPageViewController {
        let pageViewController = UIPageViewController(
            transitionStyle: .scroll,
            navigationOrientation: .horizontal)
        pageViewController.dataSource = context.coordinator

        return pageViewController
    }

    func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
        pageViewController.setViewControllers(
            [controllers[currentPage]], direction: .forward, animated: true)
    }

    class Coordinator: NSObject, UIPageViewControllerDataSource {
        var parent: PageViewController

        init(_ pageViewController: PageViewController) {
            self.parent = pageViewController
        }

        func pageViewController(
            _ pageViewController: UIPageViewController,
            viewControllerBefore viewController: UIViewController) -> UIViewController?
        {
            guard let index = parent.controllers.firstIndex(of: viewController) else {
                return nil
            }
            if index == 0 {
                return parent.controllers.last
            }
            return parent.controllers[index - 1]
        }

        func pageViewController(
            _ pageViewController: UIPageViewController,
            viewControllerAfter viewController: UIViewController) -> UIViewController?
        {
            guard let index = parent.controllers.firstIndex(of: viewController) else {
                return nil
            }
            if index + 1 == parent.controllers.count {
                return parent.controllers.first
            }
            return parent.controllers[index + 1]
        }
    }
}

2 단계

PageView에 @State 변수를 선언하고 자식 PageViewController를 만들때 그 속성에 바인딩을 전달합니다.

중요
상태들 저장하는 값에 대한 바인딩을 만들기 위해 $ 구문을 사용하는 것을 기억하세요.

import SwiftUI
import UIKit

struct PageViewController: UIViewControllerRepresentable {
    var controllers: [UIViewController]
    @Binding var currentPage: Int

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIViewController(context: Context) -> UIPageViewController {
        let pageViewController = UIPageViewController(
            transitionStyle: .scroll,
            navigationOrientation: .horizontal)
        pageViewController.dataSource = context.coordinator

        return pageViewController
    }

    func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
        pageViewController.setViewControllers(
            [controllers[currentPage]], direction: .forward, animated: true)
    }

    class Coordinator: NSObject, UIPageViewControllerDataSource {
        var parent: PageViewController

        init(_ pageViewController: PageViewController) {
            self.parent = pageViewController
        }

        func pageViewController(
            _ pageViewController: UIPageViewController,
            viewControllerBefore viewController: UIViewController) -> UIViewController?
        {
            guard let index = parent.controllers.firstIndex(of: viewController) else {
                return nil
            }
            if index == 0 {
                return parent.controllers.last
            }
            return parent.controllers[index - 1]
        }

        func pageViewController(
            _ pageViewController: UIPageViewController,
            viewControllerAfter viewController: UIViewController) -> UIViewController?
        {
            guard let index = parent.controllers.firstIndex(of: viewController) else {
                return nil
            }
            if index + 1 == parent.controllers.count {
                return parent.controllers.first
            }
            return parent.controllers[index + 1]
        }
    }
}

3 단계

초기값을 변경해서 PageViewController에 바인딩을 처리되는지 테스트합니다.

실험
페이지 뷰 컨트롤러를 두번째 뷰로 이동하는 PageView에 버튼을 추가합니다.

import SwiftUI

struct PageView<Page: View>: View {
    var viewControllers: [UIHostingController<Page>]
    @State var currentPage = 1

    init(_ views: [Page]) {
        self.viewControllers = views.map { UIHostingController(rootView: $0) }
    }

    var body: some View {
        PageViewController(controllers: viewControllers, currentPage: $currentPage)
    }
}

struct PageView_Preview: PreviewProvider {
    static var previews: some View {
        PageView(features.map { FeatureCard(landmark: $0) })
            .aspectRatio(3/2, contentMode: .fit)
    }
}

4 단계

currentPage 속성이 있는 텍스트뷰를 추가하며, @State 속성의 값을 계속 볼 수 있습니다.

페이지간에 스와이프할때 그 옵져버는 값을 변경하지 않습니다.

import SwiftUI

struct PageView<Page: View>: View {
    var viewControllers: [UIHostingController<Page>]
    @State var currentPage = 0

    init(_ views: [Page]) {
        self.viewControllers = views.map { UIHostingController(rootView: $0) }
    }

    var body: some View {
        VStack {
            PageViewController(controllers: viewControllers, currentPage: $currentPage)
            Text("Current Page: \(currentPage)")
        }
    }
}

struct PageView_Preview: PreviewProvider {
    static var previews: some View {
        PageView(features.map { FeatureCard(landmark: $0) })
    }
}

5 단계

PageViewController.swift에서, coordinator가 UIPageControllerDelegate를 준수하고, pageViewController(_:didFinishAnimating:previousViewControllers:transitionCompleted completed: Bool)메소드를 추가합니다.

SwiftUI는 페이지 교체 애니메이션이 완료될때, 이 메소드를 호출하기 때문에, 현재 뷰 컨트롤러의 인덱스를 찾을 수 있고 바인딩을 업데이트 할 수 있습니다.

import SwiftUI
import UIKit

struct PageViewController: UIViewControllerRepresentable {
    var controllers: [UIViewController]
    @Binding var currentPage: Int

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIViewController(context: Context) -> UIPageViewController {
        let pageViewController = UIPageViewController(
            transitionStyle: .scroll,
            navigationOrientation: .horizontal)
        pageViewController.dataSource = context.coordinator

        return pageViewController
    }

    func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
        pageViewController.setViewControllers(
            [controllers[currentPage]], direction: .forward, animated: true)
    }

    class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
        var parent: PageViewController

        init(_ pageViewController: PageViewController) {
            self.parent = pageViewController
        }

        func pageViewController(
            _ pageViewController: UIPageViewController,
            viewControllerBefore viewController: UIViewController) -> UIViewController?
        {
            guard let index = parent.controllers.firstIndex(of: viewController) else {
                return nil
            }
            if index == 0 {
                return parent.controllers.last
            }
            return parent.controllers[index - 1]
        }

        func pageViewController(
            _ pageViewController: UIPageViewController,
            viewControllerAfter viewController: UIViewController) -> UIViewController?
        {
            guard let index = parent.controllers.firstIndex(of: viewController) else {
                return nil
            }
            if index + 1 == parent.controllers.count {
                return parent.controllers.first
            }
            return parent.controllers[index + 1]
        }

        func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
            if completed,
                let visibleViewController = pageViewController.viewControllers?.first,
                let index = parent.controllers.firstIndex(of: visibleViewController)
            {
                parent.currentPage = index
            }
        }
    }
}

6 단계

데이터 소스외에도, UIPageViewController에 대한 델리게이터에 coordinator를 지정합니다.

바인딩으로 양방향을 연결되면, 스와이프 한 후에, 텍스트 뷰는 현재 페이지 숫자를 보여주기 위해 업데이트 합니다.

import SwiftUI
import UIKit

struct PageViewController: UIViewControllerRepresentable {
    var controllers: [UIViewController]
    @Binding var currentPage: Int

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIViewController(context: Context) -> UIPageViewController {
        let pageViewController = UIPageViewController(
            transitionStyle: .scroll,
            navigationOrientation: .horizontal)
        pageViewController.dataSource = context.coordinator
        pageViewController.delegate = context.coordinator

        return pageViewController
    }

    func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
        pageViewController.setViewControllers(
            [controllers[currentPage]], direction: .forward, animated: true)
    }

    class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
        var parent: PageViewController

        init(_ pageViewController: PageViewController) {
            self.parent = pageViewController
        }

        func pageViewController(
            _ pageViewController: UIPageViewController,
            viewControllerBefore viewController: UIViewController) -> UIViewController?
        {
            guard let index = parent.controllers.firstIndex(of: viewController) else {
                return nil
            }
            if index == 0 {
                return parent.controllers.last
            }
            return parent.controllers[index - 1]
        }

        func pageViewController(
            _ pageViewController: UIPageViewController,
            viewControllerAfter viewController: UIViewController) -> UIViewController?
        {
            guard let index = parent.controllers.firstIndex(of: viewController) else {
                return nil
            }
            if index + 1 == parent.controllers.count {
                return parent.controllers.first
            }
            return parent.controllers[index + 1]
        }

        func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
            if completed,
                let visibleViewController = pageViewController.viewControllers?.first,
                let index = parent.controllers.firstIndex(of: visibleViewController)
            {
                parent.currentPage = index
            }
        }
    }
}

Section 4. 사용자정의 페이지 컨트롤 추가하기(Add Custom Page Control)

여러분의 뷰에 SwiftUI UIViewRepresentable 뷰로 감싸진 사용자정의 UIPageControl을 추가할 준비가 되었습니다. 

1 단계

PageControl.swift 라는 새로운 SwiftUI 뷰 파일을 만듭니다. PageControl 타입을 UIViewRepresentable 프로토콜을 준수하도록 합니다.

UIViewRepresentable과 UIViewControllerRepresentable 타입은 기본 UIKit 타입에 해당하는 메소드들을 사용하며, 동일한 생명주기를 가지고 있습니다.

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
    }
}

2 단계

페이지 컨트롤로 텍스트 박스를 교체하고, 레이아웃을 VStack을 ZStack로 교체합니다.

페이지 수를 전달하고 현재 페이지를 바인딩하기 때문에, 페이지 컨트롤은 이미 정확한 값을 보여주고 있습니다.

import SwiftUI

struct PageView<Page: View>: View {
    var viewControllers: [UIHostingController<Page>]
    @State var currentPage = 0

    init(_ views: [Page]) {
        self.viewControllers = views.map { UIHostingController(rootView: $0) }
    }

    var body: some View {
        ZStack(alignment: .bottomTrailing) {
            PageViewController(controllers: viewControllers, currentPage: $currentPage)
            PageControl(numberOfPages: viewControllers.count, currentPage: $currentPage)
                .padding(.trailing)
        }
    }
}

struct PageView_Preview: PreviewProvider {
    static var previews: some View {
        PageView(features.map { FeatureCard(landmark: $0) })
    }
}

다음으로, 사용자가 페이지간에 이동할 수 있도록 페이지가 상호작용하는 페이지 컨트롤을 만듭니다.

3 단계

PageControl에서 중첩된 Coordinator 타입을 만들고 새로운 coordinator를 만들고 반환하기 위해makeCoordinator()` 메소드를 추가합니다.

UIPageControl같은 UIControl 하위클래스는 델리게이터(delegation) 대신에 대상-동작(target-action)을 사용하기 때문에, Coordinator는 현재 페이지 바인딩을 업데이트 하기 위해 @objc 메소드를 구현합니다.

import SwiftUI
import UIKit

struct PageControl: UIViewRepresentable {
    var numberOfPages: Int
    @Binding var currentPage: Int

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> UIPageControl {
        let control = UIPageControl()
        control.numberOfPages = numberOfPages

        return control
    }

    func updateUIView(_ uiView: UIPageControl, context: Context) {
        uiView.currentPage = currentPage
    }

    class Coordinator: NSObject {
        var control: PageControl

        init(_ control: PageControl) {
            self.control = control
        }

        @objc func updateCurrentPage(sender: UIPageControl) {
            control.currentPage = sender.currentPage
        }
    }
}

4 단계

valueChanged 이벤트에 대한 대상에 coordinator를 추가하며, 수행 동작으로updateCurrentPage(sender:)` 메소드를 지정합니다.

import SwiftUI
import UIKit

struct PageControl: UIViewRepresentable {
    var numberOfPages: Int
    @Binding var currentPage: Int

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> UIPageControl {
        let control = UIPageControl()
        control.numberOfPages = numberOfPages
        control.addTarget(
            context.coordinator,
            action: #selector(Coordinator.updateCurrentPage(sender:)),
            for: .valueChanged)

        return control
    }

    func updateUIView(_ uiView: UIPageControl, context: Context) {
        uiView.currentPage = currentPage
    }

    class Coordinator: NSObject {
        var control: PageControl

        init(_ control: PageControl) {
            self.control = control
        }

        @objc func updateCurrentPage(sender: UIPageControl) {
            control.currentPage = sender.currentPage
        }
    }
}

5 단계

이제 모든 다른 상호작용을 시도해보세요 - PageView는 UIKit과 SwiftUI 뷰와 컨트롤러가 함께 동작할 수 있습니다.

https://docs-assets.developer.apple.com/published/46374ee39b/_p42_all_interactions.mp4

이해하는지 확인하기(Check Your Understanding)

퀴즈 1. 어떤 프로토콜이 UIKit 뷰 컨트롤러를 SwiftUI로 연결하는데 사용하나요?

  1. UIViewRepresentable
  2. UIHostingController
  3. UIViewControllerRepresentable

[정답 : 3]
UIViewControllerRepresentable를 준수하는 구조체를 만들고 SwiftUI 뷰 계층구조에 UIViewController를 포함하도록 프로토콜 요구사항을 구현합니다.

퀴즈 2. 어떤 메소드로 UIViewControllerRepresentable 타입에 대한 델리게이터나 데이터 소스를 만드나요?

  1. 델리게이터나 데이터 소스를 사용하기 때문에, 동일한 makeUIViewController(context:) 메소드에서 UIVeiwController를 만듭니다.
  2. UIViewControllerRepresentable 타입에 대한 초기화에서
  3. makeCoordinator() 메소드에서

[정답 : 3]
makeCoordinator()에서 coordinator 타입 인스턴스를 반환합니다. SwiftUI는 생명주기를 관리하고 다른 필수 메소드에서 Context 매개변수의 일부로 제공합니다.

반응형
Posted by 까칠코더
,