在我的SwiftUI应用中,我目前有一个使用UIKit实现的PageViewController。它遵循传统的SwiftUI-Apple的SwiftUI UIKit集成tutorials中概述的UIKit实现。
我有一个数据,该数据填充在一个由@Environment
变量提供的,传递给PageViewController的controllers数组中的UIViewController。在应用程序的另一个屏幕中,您可以发出导致环境对象更新的操作,从而触发驻留在PageViewController中的ViewController的重新渲染。
但是,此重新渲染会导致问题,因为用新标识符重新构造了ViewController,因此在类协调器pageViewController函数的parent.controller
数组中找不到viewController的索引。这将导致索引默认为nil并禁用对更新的viewController的任何滑动。我仍然能够使用页面控制点在viewControllers之间导航,但我想确定如何更新parent.controller数组以包括新的ViewController并丢弃旧的ViewController。
经过数小时的搜索,调试和修改后,我无法找到一种方法来重置父控制器数组,新的ViewController替换了已丢弃的旧视图。下面是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.view.backgroundColor = UIColor.clear
pageViewController.dataSource = context.coordinator
pageViewController.delegate = context.coordinator
return pageViewController
}
func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
pageViewController.setViewControllers(
[controllers[currentPage]], direction: .forward, animated: false)
}
class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var parent: PageViewController
init(_ pageViewController: PageViewController) {
self.parent = pageViewController
}
func pageViewController(
_ pageViewController: UIPageViewController,
viewControllerBefore viewController: UIViewController) -> UIViewController? {
print(parent.controllers)
print(viewController)
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
}
}
}
}
更新环境状态后,该状态将填充给定ViewController的数据,从而导致重新渲染,coordinator类可以在以下带注释的代码中打印包含旧ViewController和新ViewController的控制器数组,但我没有还能够找到一种可靠的方法来确保新的ViewController有效地替代旧的ViewController。
struct PageViewController: UIViewControllerRepresentable {
...
class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
...
func pageViewController(
_ pageViewController: UIPageViewController,
viewControllerBefore viewController: UIViewController) -> UIViewController? {
print(parent.controllers)
// prints out array of ViewControllers including old ViewController that has now been
// discarded
// [<_TtGC7SwiftUI19UIHostingControllerVS_7AnyView_: 0x7fd595c93de0>,
// <_TtGC7SwiftUI19UIHostingControllerVS_7AnyView_: 0x7fd595c94be0>,
// <_TtGC7SwiftUI19UIHostingControllerVS_7AnyView_: 0x7fd595c96830>,
// <_TtGC7SwiftUI19UIHostingControllerVS_7AnyView_: 0x7fd595c976b0>]
print(viewController)
// prints out new ViewController that does not exist within the parent.controllers array
// and hence nil is returned from the guard
// <_TtGC7SwiftUI19UIHostingControllerVS_7AnyView_: 0x7fd593721710>
guard let index = parent.controllers.firstIndex(of: viewController) else {
return nil
}
if index == 0 {
return parent.controllers.last
}
return parent.controllers[index - 1]
}
...
}
对此问题的任何帮助或指导,将不胜感激!
不可避免地在将问题发布到StackOverflow的几分钟内,我已经找到了解决问题的方法。
移动将viewControllers从updateUIViewController()
设置为makeUIViewController()
的逻辑可确保在创建新的ViewController时调用该方法,而不是在updateUIViewController()
内仅调用一次该方法
通过执行以下更改,现在可以在更新时恢复刷卡功能,因为可以找到新创建的ViewController的索引。
以下是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.view.backgroundColor = UIColor.clear
pageViewController.dataSource = context.coordinator
pageViewController.delegate = context.coordinator
pageViewController.setViewControllers(
[controllers[currentPage]], direction: .forward, animated: false)
return pageViewController
}
func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
}
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? {
print(pageViewController)
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
}
}
}
}