UIPageViewController 在 SwiftUI 中滑动 1 次后停止工作
UIPageViewController stops working after 1 swipe in SwiftUI
我正在关注 SwiftUI 的 Apple 教程,在 'Interfacing with UIKit' (https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit) 下,我们被要求制作一个 UIPageController。
在教程中,他们使用状态 属性 来更新 PageView
中的 currentPage
变量。我想尝试一下,然后决定将 PageView
嵌入到不同的视图中,并在该父视图中进行 currentPage
属性 更新。因此,很自然地,我将 @State
属性 更改为 PageView
中的 @Binding
属性 以便更新父级中的 currentPage
。
但是,当我这样做时,UIPageController 停止工作并在第二次滑动时继续从 pageViewController(_:viewControllerBefore:) -> UIViewController?
返回 nil
。
这是代码,我将添加注释以使其更易于理解:
struct ContentView: View {
@State var currentPage = 0 // This is the new @State variable which should get updated.
var body: some View {
VStack {
PageView([Text("First page"), Text("Second Page"), Text("Third Page")],
currentPage: self.$currentPage) // Passed a binding to the State variable since I want that to be updated when the page changes.
Text("Page number: \(currentPage)") // Removing this fixes the problem, but I need it here.
}
}
}
struct PageView<Page: View>: View {
var viewControllers: [UIHostingController<Page>]
@Binding var currentPage: Int // This used to be an @State property, but I changed it to a binding since the currentPage variable is now being updated elsewhere (In the view above).
init(_ views: [Page], currentPage: Binding<Int>) {
self.viewControllers = views.map { UIHostingController(rootView: [=11=]) } // Simply wrapping all the views inside ViewControllers for UIPageViewController to use.
self._currentPage = currentPage // Assigning the binding.
}
var body: some View {
PageViewController(controllers: self.viewControllers, currentPage: self.$currentPage)
}
}
struct PageViewController: UIViewControllerRepresentable {
var controllers: [UIViewController]
@Binding var currentPage: Int
func makeCoordinator() -> Coordinator { // Makes the coordinator, irrelevant to the problem
Coordinator(self)
}
func makeUIViewController(context: Context) -> UIPageViewController {
// I don't think the problem lies here.
let pageViewController = UIPageViewController(
transitionStyle: .scroll,
navigationOrientation: .horizontal)
pageViewController.dataSource = context.coordinator
pageViewController.delegate = context.coordinator
return pageViewController
}
func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
// I don't think the problem lies here.
pageViewController.setViewControllers(
[self.controllers[currentPage]], direction: .forward, animated: true)
}
class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var parent: PageViewController
init(_ pageViewController: PageViewController) {
self.parent = pageViewController
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
// This is where I think the problem lies. When the line below is evaluated, nil is returned on the second swipe when it shouldn't be. It works on the first swipe but on the second swipe nil is returned and therefore nothing is presented.
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 == parent.controllers.count - 1 {
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
}
}
}
}
另外,从 ContentView
中删除 Text
解决了这个问题,但我需要它 - 所以删除它不是一个选项。
如果有人能解释如何解决我的问题,我将不胜感激。 懒得提供代码示例,简单的说明也能搞定。
提前致谢!
PageView
在 body
的更新期间重新创建 Text
时刷新。避免这种情况的解决方案是让 PageView
确认为 Equatable
。
最简单的fix demo如下(仅修改部分)。使用 Xcode 11.4 / iOS 13.4
测试
注意:使用相同的PageView,因为没有提供PageView2
// ...
var body: some View {
VStack {
PageView([Text("First page"), Text("Second Page"), Text("Third Page")],
currentPage: self.$currentPage)
.equatable() // << here !!
Text("Page number: \(currentPage)")
}
}
struct PageView<Page: View>: View, Equatable { // << confirm !!
static func == (lhs: PageView<Page>, rhs: PageView<Page>) -> Bool {
return true // just for demo, make it meaningful on some property
}
// .. other code goes here
我正在关注 SwiftUI 的 Apple 教程,在 'Interfacing with UIKit' (https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit) 下,我们被要求制作一个 UIPageController。
在教程中,他们使用状态 属性 来更新 PageView
中的 currentPage
变量。我想尝试一下,然后决定将 PageView
嵌入到不同的视图中,并在该父视图中进行 currentPage
属性 更新。因此,很自然地,我将 @State
属性 更改为 PageView
中的 @Binding
属性 以便更新父级中的 currentPage
。
但是,当我这样做时,UIPageController 停止工作并在第二次滑动时继续从 pageViewController(_:viewControllerBefore:) -> UIViewController?
返回 nil
。
这是代码,我将添加注释以使其更易于理解:
struct ContentView: View {
@State var currentPage = 0 // This is the new @State variable which should get updated.
var body: some View {
VStack {
PageView([Text("First page"), Text("Second Page"), Text("Third Page")],
currentPage: self.$currentPage) // Passed a binding to the State variable since I want that to be updated when the page changes.
Text("Page number: \(currentPage)") // Removing this fixes the problem, but I need it here.
}
}
}
struct PageView<Page: View>: View {
var viewControllers: [UIHostingController<Page>]
@Binding var currentPage: Int // This used to be an @State property, but I changed it to a binding since the currentPage variable is now being updated elsewhere (In the view above).
init(_ views: [Page], currentPage: Binding<Int>) {
self.viewControllers = views.map { UIHostingController(rootView: [=11=]) } // Simply wrapping all the views inside ViewControllers for UIPageViewController to use.
self._currentPage = currentPage // Assigning the binding.
}
var body: some View {
PageViewController(controllers: self.viewControllers, currentPage: self.$currentPage)
}
}
struct PageViewController: UIViewControllerRepresentable {
var controllers: [UIViewController]
@Binding var currentPage: Int
func makeCoordinator() -> Coordinator { // Makes the coordinator, irrelevant to the problem
Coordinator(self)
}
func makeUIViewController(context: Context) -> UIPageViewController {
// I don't think the problem lies here.
let pageViewController = UIPageViewController(
transitionStyle: .scroll,
navigationOrientation: .horizontal)
pageViewController.dataSource = context.coordinator
pageViewController.delegate = context.coordinator
return pageViewController
}
func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
// I don't think the problem lies here.
pageViewController.setViewControllers(
[self.controllers[currentPage]], direction: .forward, animated: true)
}
class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var parent: PageViewController
init(_ pageViewController: PageViewController) {
self.parent = pageViewController
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
// This is where I think the problem lies. When the line below is evaluated, nil is returned on the second swipe when it shouldn't be. It works on the first swipe but on the second swipe nil is returned and therefore nothing is presented.
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 == parent.controllers.count - 1 {
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
}
}
}
}
另外,从 ContentView
中删除 Text
解决了这个问题,但我需要它 - 所以删除它不是一个选项。
如果有人能解释如何解决我的问题,我将不胜感激。 懒得提供代码示例,简单的说明也能搞定。
提前致谢!
PageView
在 body
的更新期间重新创建 Text
时刷新。避免这种情况的解决方案是让 PageView
确认为 Equatable
。
最简单的fix demo如下(仅修改部分)。使用 Xcode 11.4 / iOS 13.4
测试注意:使用相同的PageView,因为没有提供PageView2
// ...
var body: some View {
VStack {
PageView([Text("First page"), Text("Second Page"), Text("Third Page")],
currentPage: self.$currentPage)
.equatable() // << here !!
Text("Page number: \(currentPage)")
}
}
struct PageView<Page: View>: View, Equatable { // << confirm !!
static func == (lhs: PageView<Page>, rhs: PageView<Page>) -> Bool {
return true // just for demo, make it meaningful on some property
}
// .. other code goes here