分页是通过SwiftUI中的UIPageView实现的。我遇到了一个问题,在页面滑动时,同一个页面连续显示两次。导致这种现象的条件是翻页的速度。这个问题在缓慢或非常快的翻页时不会出现,但在中速翻页时会出现。Here is a video of this phenomenon.
复制的完整代码附在下面。您的帮助将不胜感激。提前感谢您。
struct ContentView: View {
@State var currentPage: Int = 2
var body: some View {
VStack {
Text(currentPage.description)
.font(.largeTitle)
PageView(
pages: Array(0...100).map({ num in
Text("Page of \(num)")
.font(.largeTitle)
.padding(100)
.background(Color(.systemOrange))
.cornerRadius(10)
}),
currentPage: $currentPage)
}
}
}
struct PageView<Page: View>: UIViewControllerRepresentable {
var pages: [Page]
@Binding var currentPage: Int
func makeUIViewController(context: Context) -> UIPageViewController {
let pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: [UIPageViewController.OptionsKey.interPageSpacing: 30])
pageViewController.dataSource = context.coordinator
pageViewController.delegate = context.coordinator
return pageViewController
}
func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
context.coordinator.controllers = pages.map({
let hostingController = UIHostingController(rootView: $0)
hostingController.view.backgroundColor = .clear
return hostingController
})
let currentViewController = context.coordinator.controllers[currentPage]
pageViewController.setViewControllers([currentViewController], direction: .forward, animated: false)
}
func makeCoordinator() -> Coordinator {
return Coordinator(parent: self, pages: pages)
}
class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var parent: PageView
var controllers: [UIViewController]
init(parent: PageView, pages: [Page]) {
self.parent = parent
self.controllers = pages.map({
let hostingController = UIHostingController(rootView: $0)
hostingController.view.backgroundColor = .clear
return hostingController
})
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let index = controllers.firstIndex(of: viewController) else {
return controllers[parent.currentPage - 1]
}
if index == 0 {
return nil
}
return controllers[index - 1]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let index = controllers.firstIndex(of: viewController) else {
return controllers[parent.currentPage + 1]
}
if index + 1 == controllers.count {
return nil
}
return controllers[index + 1]
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed,
let currentViewController = pageViewController.viewControllers?.first,
let currentIndex = controllers.firstIndex(of: currentViewController)
{
parent.currentPage = currentIndex
}
}
}
}
字符串
我们也尝试过从TabView、ScrollView等SwiftUI中实现分页,但我想显示的屏幕非常重,使用UIKit的UIPageView的方法是唯一一个几乎没有屏幕蚕食的方法。
1条答案
按热度按时间jm2pwxwz1#
在
didFinishAnimating
中,只有在controllers
中找到当前视图控制器时才更新currentPage
:字符串
考虑一下当你设置
parent.currentPage
-updateUIViewController
将被调用时会发生什么,你给予一个全新的VC数组给协调器。如果此时didFinishAnimating
被再次调用,currentViewController
仍然包含旧的示例。它不会在controllers
中找到currentViewController
,并且无法设置currentPage
。请注意,
setViewControllers
仅在动画完成后更改pageViewController.viewControllers
。因此,要解决这个问题,只需在每次视图更新时不给予新的
UIHostingController
s即可:型
如果你有一个恒定的页面集,这就可以工作。否则,这意味着当
pages
改变时,你将无法更新视图控制器。更好的解决方案是使用面向数据的方法,就像所有其他SwiftUI视图一样。
这个想法是
PageView
应该接受一个 data 的列表,而不是View
s的列表。它还需要一个将数据转换为View
s的函数。协调器还使用 data。在数据数组上使用
firstIndex(of:)
,并且仅在必要时创建VC。下面是一个粗略的草图。它可以工作,但它可能可以通过使用
Identifiable
和Dictionary
来优化。如果数据没有改变,makeVC
制作的VC也可以重用。型
示例用法:
型