★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公众号:山青咏芝(shanqingyongzhi)
➤博客园地址:山青咏芝(www.zengqiang.org)
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址:https://www.cnblogs.com/strengthen/p/11398867.html
➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。
➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
热烈欢迎,请直接点击!!!
进入博主App Store主页,下载使用各个作品!!!
注:博主将坚持每月上线一个新app!!!
SwiftUI可与所有Apple平台上的现有UI框架无缝协作。例如,您可以在SwiftUI视图中放置UIKit视图和视图控制器,反之亦然。
本教程将向您展示如何从主屏幕转换特色地标以包装和的实例。您将使用显示SwiftUI视图的轮播,并使用状态变量和绑定来协调整个用户界面中的数据更新。UIPageViewController
UIPageControl
UIPageViewController
按照步骤构建此项目,或者下载完成的项目以自行探索。
项目文件:https://docs-assets.developer.apple.com/published/113d95da00/InterfacingWithUIKit.zip
一、创建视图以表示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 }
三、在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