★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公眾號:山青詠芝(shanqingyongzhi)
➤博客園地址:山青詠芝(www.zengqiang.org)
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址:https://www.cnblogs.com/strengthen/p/11398867.html
➤如果鏈接不是山青詠芝的博客園地址,則可能是爬取作者的文章。
➤原文已修改更新!強烈建議點擊原文地址閱讀!支持作者!支持原創!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
SwiftUI可與所有Apple平台上的現有UI框架無縫協作。例如,您可以在SwiftUI視圖中放置UIKit視圖和視圖控制器,反之亦然。
本教程將向您展示如何從主屏幕轉換特色地標以包裝和的實例。您將使用顯示SwiftUI視圖的輪播,並使用狀態變量和綁定來協調整個用戶界面中的數據更新。UIPageViewController
UIPageControl
UIPageViewController
按照步驟構建此項目,或者下載完成的項目以自行探索。
一、創建視圖以表示UIPageViewController
要在SwiftUI中表示UIKit視圖和視圖控制器,可以創建符合和協議的類型。您的自定義類型創建和配置它們所代表的UIKit類型,而SwiftUI管理它們的生命周期並在需要時更新它們。UIViewRepresentable
UIViewControllerRepresentable
第1步
創建一個名為的新SwiftUI視圖文件,並將該類型聲明為符合PageViewController.swift
PageViewController
UIViewControllerRepresentable.
頁面視圖控制器存儲一組實例。這些是在地標之間滾動的頁面。UIViewController
接下來,添加協議的兩個要求。UIViewControllerRepresentable
1 import SwiftUI 2 import UIKit 3 4 struct PageViewController: UIViewControllerRepresentable { 5 var controllers: [UIViewController] 6 }
第2步
添加一個使用所需配置創建的方法。makeUIViewController(context:)
UIPageViewController
當SwiftUI准備好顯示視圖時,它會調用此方法一次,然后管理視圖控制器的生命周期。
1 import SwiftUI 2 import UIKit 3 4 struct PageViewController: UIViewControllerRepresentable { 5 var controllers: [UIViewController] 6 7 func makeUIViewController(context: Context) -> UIPageViewController { 8 let pageViewController = UIPageViewController( 9 transitionStyle: .scroll, 10 navigationOrientation: .horizontal) 11 12 return pageViewController 13 } 14 }
第3步
添加一個方法,該方法調用以在數組中顯示第一個視圖控制器以供顯示。updateUIViewController(_:context:)
setViewControllers(_:direction:animated:)
創建另一個SwiftUI視圖以顯示您的視圖。UIViewControllerRepresentable
1 import SwiftUI 2 import UIKit 3 4 struct PageViewController: UIViewControllerRepresentable { 5 var controllers: [UIViewController] 6 7 func makeUIViewController(context: Context) -> UIPageViewController { 8 let pageViewController = UIPageViewController( 9 transitionStyle: .scroll, 10 navigationOrientation: .horizontal) 11 12 return pageViewController 13 } 14 15 func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) { 16 pageViewController.setViewControllers( 17 [controllers[0]], direction: .forward, animated: true) 18 } 19 }
第4步
創建一個名為的新SwiftUI視圖文件,並更新要聲明為子視圖的類型。PageView.swift
PageView
PageViewController
請注意,通用初始化程序采用一組視圖,並將每個視圖嵌套在一個視圖中。A 是一個子類,表示UIKit上下文中的SwiftUI視圖。UIHostingController
UIHostingController
UIViewController
1 import SwiftUI 2 3 struct PageView<Page: View>: View { 4 var viewControllers: [UIHostingController<Page>] 5 6 init(_ views: [Page]) { 7 self.viewControllers = views.map { UIHostingController(rootView: $0) } 8 } 9 10 var body: some View { 11 PageViewController(controllers: viewControllers) 12 } 13 } 14 15 struct PageView_Preview: PreviewProvider { 16 static var previews: some View { 17 PageView() 18 } 19 }
第5步
更新預覽提供程序以傳遞所需的視圖數組,預覽開始工作。
1 import SwiftUI 2 3 struct PageView<Page: View>: View { 4 var viewControllers: [UIHostingController<Page>] 5 6 init(_ views: [Page]) { 7 self.viewControllers = views.map { UIHostingController(rootView: $0) } 8 } 9 10 var body: some View { 11 PageViewController(controllers: viewControllers) 12 } 13 } 14 15 struct PageView_Preview: PreviewProvider { 16 static var previews: some View { 17 PageView(features.map { FeatureCard(landmark: $0) }) 18 .aspectRatio(3/2, contentMode: .fit) 19 } 20 }
第6步
在繼續之前將預覽固定到畫布上 - 此視圖是所有操作的位置。PageView
二、創建View Controller的數據源
在幾個簡短的步驟中,您已經做了很多 - 使用a 來從SwiftUI視圖中顯示內容SwiftUI。現在是時候啟用滑動交互以從一個頁面移動到另一個頁面。PageViewController
UIPageViewController

表示UIKit視圖控制器的SwiftUI視圖可以定義Coordinator
SwiftUI管理的類型,並將其作為可表示視圖的上下文的一部分提供。
第1步
Coordinator
在里面聲明一個嵌套類。PageViewController
SwiftUI管理您的類型的協調器,並在調用上面創建的方法時將其作為上下文的一部分提供。UIViewControllerRepresentable
1 import SwiftUI 2 import UIKit 3 4 struct PageViewController: UIViewControllerRepresentable { 5 var controllers: [UIViewController] 6 7 func makeUIViewController(context: Context) -> UIPageViewController { 8 let pageViewController = UIPageViewController( 9 transitionStyle: .scroll, 10 navigationOrientation: .horizontal) 11 12 return pageViewController 13 } 14 15 func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) { 16 pageViewController.setViewControllers( 17 [controllers[0]], direction: .forward, animated: true) 18 } 19 20 class Coordinator: NSObject { 21 var parent: PageViewController 22 23 init(_ pageViewController: PageViewController) { 24 self.parent = pageViewController 25 } 26 } 27 }
第2步
添加另一個方法來創建協調器。PageViewController
SwiftUI 之前調用此方法,以便在配置視圖控制器時可以訪問協調器對象。makeCoordinator()
makeUIViewController(context:)
提示:
您可以使用此協調器實現常見的Cocoa模式,例如委托,數據源以及通過目標操作響應用戶事件。
1 import SwiftUI 2 import UIKit 3 4 struct PageViewController: UIViewControllerRepresentable { 5 var controllers: [UIViewController] 6 7 func makeCoordinator() -> Coordinator { 8 Coordinator(self) 9 } 10 11 func makeUIViewController(context: Context) -> UIPageViewController { 12 let pageViewController = UIPageViewController( 13 transitionStyle: .scroll, 14 navigationOrientation: .horizontal) 15 16 return pageViewController 17 } 18 19 func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) { 20 pageViewController.setViewControllers( 21 [controllers[0]], direction: .forward, animated: true) 22 } 23 24 class Coordinator: NSObject { 25 var parent: PageViewController 26 27 init(_ pageViewController: PageViewController) { 28 self.parent = pageViewController 29 } 30 } 31 }
第3步
添加對類型的一致性,並實現兩個必需的方法。UIPageViewControllerDataSource
Coordinator
這兩種方法建立了視圖控制器之間的關系,因此您可以在它們之間來回滑動。
1 import SwiftUI 2 import UIKit 3 4 struct PageViewController: UIViewControllerRepresentable { 5 var controllers: [UIViewController] 6 7 func makeCoordinator() -> Coordinator { 8 Coordinator(self) 9 } 10 11 func makeUIViewController(context: Context) -> UIPageViewController { 12 let pageViewController = UIPageViewController( 13 transitionStyle: .scroll, 14 navigationOrientation: .horizontal) 15 16 return pageViewController 17 } 18 19 func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) { 20 pageViewController.setViewControllers( 21 [controllers[0]], direction: .forward, animated: true) 22 } 23 24 class Coordinator: NSObject, UIPageViewControllerDataSource { 25 var parent: PageViewController 26 27 init(_ pageViewController: PageViewController) { 28 self.parent = pageViewController 29 } 30 31 func pageViewController( 32 _ pageViewController: UIPageViewController, 33 viewControllerBefore viewController: UIViewController) -> UIViewController? 34 { 35 guard let index = parent.controllers.firstIndex(of: viewController) else { 36 return nil 37 } 38 if index == 0 { 39 return parent.controllers.last 40 } 41 return parent.controllers[index - 1] 42 } 43 44 func pageViewController( 45 _ pageViewController: UIPageViewController, 46 viewControllerAfter viewController: UIViewController) -> UIViewController? 47 { 48 guard let index = parent.controllers.firstIndex(of: viewController) else { 49 return nil 50 } 51 if index + 1 == parent.controllers.count { 52 return parent.controllers.first 53 } 54 return parent.controllers[index + 1] 55 } 56 } 57 }
第4步
添加協調器作為數據源。UIPageViewController
1 import SwiftUI 2 import UIKit 3 4 struct PageViewController: UIViewControllerRepresentable { 5 var controllers: [UIViewController] 6 7 func makeCoordinator() -> Coordinator { 8 Coordinator(self) 9 } 10 11 func makeUIViewController(context: Context) -> UIPageViewController { 12 let pageViewController = UIPageViewController( 13 transitionStyle: .scroll, 14 navigationOrientation: .horizontal) 15 pageViewController.dataSource = context.coordinator 16 17 return pageViewController 18 } 19 20 func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) { 21 pageViewController.setViewControllers( 22 [controllers[0]], direction: .forward, animated: true) 23 } 24 25 class Coordinator: NSObject, UIPageViewControllerDataSource { 26 var parent: PageViewController 27 28 init(_ pageViewController: PageViewController) { 29 self.parent = pageViewController 30 } 31 32 func pageViewController( 33 _ pageViewController: UIPageViewController, 34 viewControllerBefore viewController: UIViewController) -> UIViewController? 35 { 36 guard let index = parent.controllers.firstIndex(of: viewController) else { 37 return nil 38 } 39 if index == 0 { 40 return parent.controllers.last 41 } 42 return parent.controllers[index - 1] 43 } 44 45 func pageViewController( 46 _ pageViewController: UIPageViewController, 47 viewControllerAfter viewController: UIViewController) -> UIViewController? 48 { 49 guard let index = parent.controllers.firstIndex(of: viewController) else { 50 return nil 51 } 52 if index + 1 == parent.controllers.count { 53 return parent.controllers.first 54 } 55 return parent.controllers[index + 1] 56 } 57 } 58 }
第5步
啟用實時預覽並測試滑動互動。
三、在SwiftUI視圖的狀態中跟蹤頁面
要准備添加自定義,您需要一種從內部跟蹤當前頁面的方法。UIPageControl
PageView
為此,您將聲明一個@State
屬性,並將綁定傳遞給該屬性到視圖。該更新綁定,以匹配可見頁面。PageView
PageViewController
PageViewController

第1步
首先添加一個綁定作為屬性。currentPage
PageViewController
除了聲明@Binding
屬性之外,還要更新調用,傳遞綁定的值。setViewControllers(_:direction:animated:)
currentPage
1 import SwiftUI 2 import UIKit 3 4 struct PageViewController: UIViewControllerRepresentable { 5 var controllers: [UIViewController] 6 @Binding var currentPage: Int 7 8 func makeCoordinator() -> Coordinator { 9 Coordinator(self) 10 } 11 12 func makeUIViewController(context: Context) -> UIPageViewController { 13 let pageViewController = UIPageViewController( 14 transitionStyle: .scroll, 15 navigationOrientation: .horizontal) 16 pageViewController.dataSource = context.coordinator 17 18 return pageViewController 19 } 20 21 func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) { 22 pageViewController.setViewControllers( 23 [controllers[currentPage]], direction: .forward, animated: true) 24 } 25 26 class Coordinator: NSObject, UIPageViewControllerDataSource { 27 var parent: PageViewController 28 29 init(_ pageViewController: PageViewController) { 30 self.parent = pageViewController 31 } 32 33 func pageViewController( 34 _ pageViewController: UIPageViewController, 35 viewControllerBefore viewController: UIViewController) -> UIViewController? 36 { 37 guard let index = parent.controllers.firstIndex(of: viewController) else { 38 return nil 39 } 40 if index == 0 { 41 return parent.controllers.last 42 } 43 return parent.controllers[index - 1] 44 } 45 46 func pageViewController( 47 _ pageViewController: UIPageViewController, 48 viewControllerAfter viewController: UIViewController) -> UIViewController? 49 { 50 guard let index = parent.controllers.firstIndex(of: viewController) else { 51 return nil 52 } 53 if index + 1 == parent.controllers.count { 54 return parent.controllers.first 55 } 56 return parent.controllers[index + 1] 57 } 58 } 59 }
第2步
聲明@State
變量,並在創建子項時將綁定傳遞給屬性。PageView
PageViewController
重要
請記住使用$
語法創建綁定到存儲為狀態的值。
1 import SwiftUI 2 3 struct PageView<Page: View>: View { 4 var viewControllers: [UIHostingController<Page>] 5 @State var currentPage = 0 6 7 init(_ views: [Page]) { 8 self.viewControllers = views.map { UIHostingController(rootView: $0) } 9 } 10 11 var body: some View { 12 PageViewController(controllers: viewControllers, currentPage: $currentPage) 13 } 14 } 15 16 struct PageView_Preview: PreviewProvider { 17 static var previews: some View { 18 PageView(features.map { FeatureCard(landmark: $0) }) 19 .aspectRatio(3/2, contentMode: .fit) 20 } 21 }
第3步
通過更改其初始值來測試值是否通過綁定流向。PageViewController
實驗
添加一個按鈕,使頁面視圖控制器跳轉到第二個視圖。PageView
1 import SwiftUI 2 3 struct PageView<Page: View>: View { 4 var viewControllers: [UIHostingController<Page>] 5 @State var currentPage = 1 6 7 init(_ views: [Page]) { 8 self.viewControllers = views.map { UIHostingController(rootView: $0) } 9 } 10 11 var body: some View { 12 PageViewController(controllers: viewControllers, currentPage: $currentPage) 13 } 14 } 15 16 struct PageView_Preview: PreviewProvider { 17 static var previews: some View { 18 PageView(features.map { FeatureCard(landmark: $0) }) 19 .aspectRatio(3/2, contentMode: .fit) 20 } 21 }
第4步
添加帶有屬性的文本視圖,以便您可以關注屬性的值。currentPage
@State
請注意,當您從一個頁面滑動到另一個頁面時,該值不會更改。
1 import SwiftUI 2 3 struct PageView<Page: View>: View { 4 var viewControllers: [UIHostingController<Page>] 5 @State var currentPage = 0 6 7 init(_ views: [Page]) { 8 self.viewControllers = views.map { UIHostingController(rootView: $0) } 9 } 10 11 var body: some View { 12 VStack { 13 PageViewController(controllers: viewControllers, currentPage: $currentPage) 14 Text("Current Page: \(currentPage)") 15 } 16 } 17 } 18 19 struct PageView_Preview: PreviewProvider { 20 static var previews: some View { 21 PageView(features.map { FeatureCard(landmark: $0) }) 22 } 23 }
第5步
在,使協調器符合,並添加方法。PageViewController.swift
UIPageViewControllerDelegate
pageViewController(_:didFinishAnimating:previousViewControllers:transitionCompleted completed: Bool)
因為只要頁面切換動畫完成,SwiftUI就會調用此方法,您可以找到當前視圖控制器的索引並更新綁定。
1 import SwiftUI 2 import UIKit 3 4 struct PageViewController: UIViewControllerRepresentable { 5 var controllers: [UIViewController] 6 @Binding var currentPage: Int 7 8 func makeCoordinator() -> Coordinator { 9 Coordinator(self) 10 } 11 12 func makeUIViewController(context: Context) -> UIPageViewController { 13 let pageViewController = UIPageViewController( 14 transitionStyle: .scroll, 15 navigationOrientation: .horizontal) 16 pageViewController.dataSource = context.coordinator 17 18 return pageViewController 19 } 20 21 func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) { 22 pageViewController.setViewControllers( 23 [controllers[currentPage]], direction: .forward, animated: true) 24 } 25 26 class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate { 27 var parent: PageViewController 28 29 init(_ pageViewController: PageViewController) { 30 self.parent = pageViewController 31 } 32 33 func pageViewController( 34 _ pageViewController: UIPageViewController, 35 viewControllerBefore viewController: UIViewController) -> UIViewController? 36 { 37 guard let index = parent.controllers.firstIndex(of: viewController) else { 38 return nil 39 } 40 if index == 0 { 41 return parent.controllers.last 42 } 43 return parent.controllers[index - 1] 44 } 45 46 func pageViewController( 47 _ pageViewController: UIPageViewController, 48 viewControllerAfter viewController: UIViewController) -> UIViewController? 49 { 50 guard let index = parent.controllers.firstIndex(of: viewController) else { 51 return nil 52 } 53 if index + 1 == parent.controllers.count { 54 return parent.controllers.first 55 } 56 return parent.controllers[index + 1] 57 } 58 59 func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { 60 if completed, 61 let visibleViewController = pageViewController.viewControllers?.first, 62 let index = parent.controllers.firstIndex(of: visibleViewController) 63 { 64 parent.currentPage = index 65 } 66 } 67 } 68 }
第6步
除數據源外,還將協調器指定為該委托的委托。UIPageViewController
在兩個方向上連接綁定后,文本視圖會在每次滑動后更新以顯示正確的頁碼。
1 import SwiftUI 2 import UIKit 3 4 struct PageViewController: UIViewControllerRepresentable { 5 var controllers: [UIViewController] 6 @Binding var currentPage: Int 7 8 func makeCoordinator() -> Coordinator { 9 Coordinator(self) 10 } 11 12 func makeUIViewController(context: Context) -> UIPageViewController { 13 let pageViewController = UIPageViewController( 14 transitionStyle: .scroll, 15 navigationOrientation: .horizontal) 16 pageViewController.dataSource = context.coordinator 17 pageViewController.delegate = context.coordinator 18 19 return pageViewController 20 } 21 22 func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) { 23 pageViewController.setViewControllers( 24 [controllers[currentPage]], direction: .forward, animated: true) 25 } 26 27 class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate { 28 var parent: PageViewController 29 30 init(_ pageViewController: PageViewController) { 31 self.parent = pageViewController 32 } 33 34 func pageViewController( 35 _ pageViewController: UIPageViewController, 36 viewControllerBefore viewController: UIViewController) -> UIViewController? 37 { 38 guard let index = parent.controllers.firstIndex(of: viewController) else { 39 return nil 40 } 41 if index == 0 { 42 return parent.controllers.last 43 } 44 return parent.controllers[index - 1] 45 } 46 47 func pageViewController( 48 _ pageViewController: UIPageViewController, 49 viewControllerAfter viewController: UIViewController) -> UIViewController? 50 { 51 guard let index = parent.controllers.firstIndex(of: viewController) else { 52 return nil 53 } 54 if index + 1 == parent.controllers.count { 55 return parent.controllers.first 56 } 57 return parent.controllers[index + 1] 58 } 59 60 func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { 61 if completed, 62 let visibleViewController = pageViewController.viewControllers?.first, 63 let index = parent.controllers.firstIndex(of: visibleViewController) 64 { 65 parent.currentPage = index 66 } 67 } 68 } 69 }
四、添加自定義頁面控件
您已准備好在視圖中添加自定義,包含在SwiftUI 視圖中。UIPageControl
UIViewRepresentable

第1步
創建一個名為的新SwiftUI視圖文件。更新類型以符合協議。PageControl.swift
PageControl
UIViewRepresentable
UIViewRepresentable
和類型具有相同的生命周期,其方法與其基礎UIKit類型相對應。UIViewControllerRepresentable
1 import SwiftUI 2 import UIKit 3 4 struct PageControl: UIViewRepresentable { 5 var numberOfPages: Int 6 @Binding var currentPage: Int 7 8 func makeUIView(context: Context) -> UIPageControl { 9 let control = UIPageControl() 10 control.numberOfPages = numberOfPages 11 12 return control 13 } 14 15 func updateUIView(_ uiView: UIPageControl, context: Context) { 16 uiView.currentPage = currentPage 17 } 18 }
第2步
用頁面控件替換文本框,從a切換VStack
到ZStack
for布局。
因為您正在將頁面計數和綁定傳遞給當前頁面,所以頁面控件已顯示正確的值。
接下來,使頁面控件具有交互性,以便用戶可以點擊一側或另一側在頁面之間移動。
1 import SwiftUI 2 3 struct PageView<Page: View>: View { 4 var viewControllers: [UIHostingController<Page>] 5 @State var currentPage = 0 6 7 init(_ views: [Page]) { 8 self.viewControllers = views.map { UIHostingController(rootView: $0) } 9 } 10 11 var body: some View { 12 ZStack(alignment: .bottomTrailing) { 13 PageViewController(controllers: viewControllers, currentPage: $currentPage) 14 PageControl(numberOfPages: viewControllers.count, currentPage: $currentPage) 15 .padding(.trailing) 16 } 17 } 18 } 19 20 struct PageView_Preview: PreviewProvider { 21 static var previews: some View { 22 PageView(features.map { FeatureCard(landmark: $0) }) 23 } 24 }
第3步
在其中創建嵌套Coordinator
類型,並添加一個方法來創建和返回新的協調器。PageControl
makeCoordinator()
因為UIControl
子類使用目標操作模式而不是委托,所以這實現了一種更新當前頁面綁定的方法。UIPageControl
Coordinator
@objc
1 import SwiftUI 2 import UIKit 3 4 struct PageControl: UIViewRepresentable { 5 var numberOfPages: Int 6 @Binding var currentPage: Int 7 8 func makeCoordinator() -> Coordinator { 9 Coordinator(self) 10 } 11 12 func makeUIView(context: Context) -> UIPageControl { 13 let control = UIPageControl() 14 control.numberOfPages = numberOfPages 15 16 return control 17 } 18 19 func updateUIView(_ uiView: UIPageControl, context: Context) { 20 uiView.currentPage = currentPage 21 } 22 23 class Coordinator: NSObject { 24 var control: PageControl 25 26 init(_ control: PageControl) { 27 self.control = control 28 } 29 30 @objc func updateCurrentPage(sender: UIPageControl) { 31 control.currentPage = sender.currentPage 32 } 33 } 34 }
第4步
添加協調器作為事件的目標,將方法指定為要執行的操作。valueChanged
updateCurrentPage(sender:)
1 import SwiftUI 2 import UIKit 3 4 struct PageControl: UIViewRepresentable { 5 var numberOfPages: Int 6 @Binding var currentPage: Int 7 8 func makeCoordinator() -> Coordinator { 9 Coordinator(self) 10 } 11 12 func makeUIView(context: Context) -> UIPageControl { 13 let control = UIPageControl() 14 control.numberOfPages = numberOfPages 15 control.addTarget( 16 context.coordinator, 17 action: #selector(Coordinator.updateCurrentPage(sender:)), 18 for: .valueChanged) 19 20 return control 21 } 22 23 func updateUIView(_ uiView: UIPageControl, context: Context) { 24 uiView.currentPage = currentPage 25 } 26 27 class Coordinator: NSObject { 28 var control: PageControl 29 30 init(_ control: PageControl) { 31 self.control = control 32 } 33 34 @objc func updateCurrentPage(sender: UIPageControl) { 35 control.currentPage = sender.currentPage 36 } 37 } 38 }
第5步
現在嘗試所有不同的交互 - 顯示UIKit和SwiftUI視圖和控制器如何協同工作。PageView