★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公众号:山青咏芝(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