SwiftUI는 애플 플랫폼의 기존의 UI 프레임워크들과도 잘 동작합니다. 예를 들면 UIKit뷰와 뷰 컨트롤러를 SwiftUI 뷰의 안에 넣거나 그 반대의 경우 등이 모두 가능합니다.
이번 튜토리얼에서는 홈 화면의 주요(Featured) 랜드마크를 UIPageViewController와 UIPageControl의 인스턴스로 래핑하는 방법을 보여줍니다.
당신은 SwiftUI 뷰의 캐로셀로 보여주기 위해 UIPageViewController를 사용해볼 것이고, 유저 인터페이스 전체에서 데이터 업데이트를 조정하기 위해 state 변수와 바인딩을 사용할 것입니다.

프로젝트의 Views 그룹에 PageView라는 그룹을 추가해준 뒤, PageViewController.swift라는 이름의 스위프트 파일을 새로 만들고 다음과 같이 작성해줍니다.
**// PageViewController.swift
import SwiftUI
import UIKit
// 페이지 뷰 컨트롤러는 View 타입의 Page 인스턴스의 배열을 저장합니다. 이 배열은 랜드마크 간에 스크롤을 할 수 있는 페이지들입니다.
struct PageViewController<Page: View>: UIViewControllerRepresentable {
var pages: [Page]
// UIPageViewController를 생성해주는 메서드
// SwiftUI에서는 뷰가 화면에 보여질 준비가 됐을 때 이 메서드를 한 번 호출해주고, 이후에는 생명 주기를 관리해줍니다.
func makeUIViewController(context: Context) -> UIPageViewController {
let pageViewController = UIPageViewController(
transitionStyle: .scroll,
navigationOrientation: .horizontal)
return pageViewController
}
// setViewControllers(_:direction:animated:)를 호출하여 디스플레이용 뷰 컨트롤러를 제공하는 메서드
// 현재 상태에서는 모든 업데이트에서 상황에서 SwiftUI 뷰 페이지를 호스팅하는 UIHostingController를 만들어줍니다.
// 나중에 페이지 뷰 컨트롤러의 생명 주기 동안 컨트롤러를 한 번만 초기화함으로써 이를 더 효율적으로 만들 수 있습니다.
func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
pageViewController.setViewControllers(
[UIHostingController(rootView: pages[0])], direction: .forward, animated: true)
}
}**
Landmark.swift에 다음과 같이 주요 랜드마크 이미지에 대한 프로퍼티를 추가해줍니다.
**// Landmark.swift**
import Foundation
import SwiftUI
import CoreLocation
// landmarkData.json 파일의 키:값에 맞는 구조체를 정의해준다.
// Codable 프로토콜 : 구조체와 데이터 파일 간에 데이터를 더 쉽게 이동할 수 있게 해준다. Codable의 컴포넌트인 Decodable을 사용해서 파일로부터 데이터를 읽어올 수 있다.
// Identifiable : 데이터가 identifiable하도록 준수하는 프로토콜
struct Landmark: Hashable, Codable, Identifiable {
...
private var imageName: String // 이미지 데이터의 이름을 불러오기 위한 프로퍼티
var image: Image {
Image(imageName)
}
**// 주요 랜드마크 이미지가 존재한다면 반환해주는 계산 프로퍼티
var featureImage: Image? {
isFeatured ? Image(imageName + "_feature") : nil
}**
...
주요 랜드마크의 이미지를 보여주는 FeatureCard.swift라는 이름의 새로운 SwiftUI 뷰 파일을 만들어 줍니다.
**// FeatureCard.swift
import SwiftUI
struct FeatureCard: View {
var landmark: Landmark
var body: some View {
landmark.featureImage?
.resizable()
.aspectRatio(3 / 2, contentMode: .fit)
.overlay {
TextOverlay(landmark: landmark)
}
}
}
struct TextOverlay: View {
var landmark: Landmark
var gradient: LinearGradient {
.linearGradient(
Gradient(colors: [.black.opacity(0.6), .black.opacity(0)]),
startPoint: .bottom,
endPoint: .center)
}
var body: some View {
ZStack(alignment: .bottomLeading) {
gradient
VStack(alignment: .leading) {
Text(landmark.name)
.font(.title)
.bold()
Text(landmark.park)
}
.padding()
}
.foregroundColor(.white)
}
}
struct FeatureCard_Previews: PreviewProvider {
static var previews: some View {
FeatureCard(landmark: ModelData().features[0])
}
}**

이제 UIViewControllerRepresentable 뷰를 보여줄 커스텀 뷰를 만들어 줍니다.
PageView.swift라는 이름의 새로운 SwiftUI view 파일을 만들어 주고, PageView타입이 PageViewController를 자식 뷰로 선언하도록 수정해줍니다.
**// PageView.swift
import SwiftUI
struct PageView<Page: View>: View {
var pages: [Page]
var body: some View {
PageViewController(pages: pages)
}
}
struct PageView_Previews: PreviewProvider {
static var previews: some View {
// PreviewProvider를 업데이트하여 필요한 뷰의 배열을 전달해주면 미리보기 화면에 나오는 것을 볼 수 있다.
PageView(pages: ModelData().features.map { FeatureCard(landmark: $0) })
.aspectRatio(3 / 2, contentMode: .fit)
}
}**
앞에서 진행한 몇 가지의 단계에서 당신은 많은 것을 했습니다.
PageViewController는 UIPageViewController를 사용하여 SwiftUI 뷰의 컨텐츠를 화면에 표시해줍니다.