원문 : 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 매개변수의 일부로 제공합니다.

'SwiftUI > Tutorials' 카테고리의 다른 글

Interfacing with UIKit  (0) 2019.08.13
Working with UI Controls  (0) 2019.08.13
Composing Complex Interfaces  (0) 2019.08.13
Animating Views and Transitions  (0) 2019.08.13
Drawing Paths and Shapes  (0) 2019.08.13
Handling User Input  (0) 2019.08.13
Posted by 까칠코더