SwiftUI: How to connect currentPage of PageView with firing view's passing index

邮差的信 提交于 2021-02-11 12:11:16

问题


I follow Apple's example interfacing-with-uikit to use PageView in SwiftUI. And I want to implement it in my app. The scenario is that in MovieView, when the user taps an image of image list then it triggers call to PageView with views and selected index as parameters.

But when I click on any item of image list, it always show the page view with the 1st element. I try to change it in init() of PageView, however it is not working.

In console, I try to print something, and I see the currentPage is not changed.

ReceivedIdx: 0
Current page: 1
ReceivedIdx: 3
Current page: 1
ReceivedIdx: 5
Current page: 1

Currently, currentPage is connected between PageView & PageViewCollection, but how to connect selectedIdx with them? Because it is the trigger point for which image is selected.

MovieView

...
private struct ImageList: View {
    var images: [ImageViewModel]
    @State private var showSheet = false
    @State private var selectedIdx = 0

    var body: some View {

        VStack(alignment: .leading) {
            Text("Images")
                .font(.headline)
            ScrollView(.horizontal) {
                HStack(alignment: .top, spacing: 6) {
                    ForEach(0..<images.count, id: \.self) { i in
                        KFImage(source: .network(self.images[i].fileURL))
                            .resizable()
                            .frame(width: 200)
                            .aspectRatio(1.77, contentMode: .fit)
                            .onTapGesture {
                                self.selectedIdx = i
                                self.showSheet.toggle()
                        }
                    }
                }.sheet(isPresented: $showSheet) {
                    PageView(self.images.map { PresentedImageView(image: $0) }, selectedIdx: self.selectedIdx)
                }
            }.frame(height: 120)
        }
        .padding(.horizontal).padding(.bottom)
    }
}

private struct PresentedImageView: View {
    var image: ImageViewModel

    var body: some View {
        KFImage(source: .network(image.fileURL))
        .resizable()
        //.frame(width: gr.size.width - 6, alignment: .center)
        .aspectRatio(1.77, contentMode: .fit)
    }
}
...

PageView

import SwiftUI

struct PageView<Page: View>: View {

    var viewControllers: [UIHostingController<Page>]
    var selectedIdx = 0
    @State var currentPage = 1

    init(_ views: [Page], selectedIdx: Int) {
        self.viewControllers = views.map { UIHostingController(rootView: $0) }
        self.currentPage = selectedIdx
        print("ReceivedIdx: \(selectedIdx)")
        print("Current page: \(currentPage)")
    }

    var body: some View {
        PageViewController(controllers: viewControllers, currentPage: $currentPage)
    }
}

//struct PageView_Previews: PreviewProvider {
//    static var previews: some View {
//        PageView(features.map { FeatureCard(landmark: $0) })
//            .aspectRatio(3/2, contentMode: .fit)
//    }
//}

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.dataSource = context.coordinator
        pageViewController.delegate = context.coordinator
        return pageViewController
    }

    func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
        pageViewController.setViewControllers([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? {
            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
            }
        }
    }
}

回答1:


Finally, I found the answer From this post in stackoverflow. this morning!

In one word:

SwiftUI doesn't allow you to change @State in the initializer but you can initialize it.

So in my case, remove the initial value on @State currentPage and set it directly using State(initialValue:) in your init().

Remember to use _ before currentPage.

import SwiftUI

struct PageView<Page: View>: View {

    var viewControllers: [UIHostingController<Page>]
    @State var currentPage: Int

    init(_ views: [Page], selectedIdx: Int) {
        self.viewControllers = views.map { UIHostingController(rootView: $0) }
        _currentPage = State(initialValue: selectedIdx)
    }

    var body: some View {
        PageViewController(controllers: viewControllers, currentPage: $currentPage)
    }
}


来源:https://stackoverflow.com/questions/61328279/swiftui-how-to-connect-currentpage-of-pageview-with-firing-views-passing-index

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!