我遵循了苹果的SwiftUI教程,在“与UIKIT接口”(https://developer.Apple.com/tutorials/SwiftUI/interfacing-with-uikit)下,我们被要求制作一个UIPageController。
在教程中,他们使用了一个状态属性来更新PageView
中的CurrentPage
变量。 我想尝试一下,决定将PageView
嵌入到不同的视图中,并在父视图中更新CurrentPage
属性。 因此,我很自然地将pageView
中的@state
属性更改为@binding
属性,以便更新父级中的CurrentPage
。
但是,当我这样做时,UIPageController停止工作,并继续从PageViewController(_:ViewControllerBefore:)->返回
。nil
; UIViewController?
这是代码,我会添加注释,让它理解起来不那么繁琐:
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: $0) } // 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
修复了这个问题,但是我需要它--所以删除它不是一个选项。
如果有人能解释如何解决我的问题,我将万分感激。 如果你太懒,不需要提供一个代码示例,简单的解释也可以完成工作。
提前谢谢!
更新text
时,在更新body
期间重新创建pageView
。 避免这种情况的解决方案是使pageview
确认为equatable
。
fix最简单的演示如下(仅修改部分)。 使用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