【问题标题】:UIPageViewController swipe ignores SwiftUI's navigationBarHidden(true)UIPageViewController 滑动忽略 SwiftUI 的 navigationBarHidden(true)
【发布时间】:2021-11-08 12:56:55
【问题描述】:

我正在创建一个 SwiftUI 应用,它需要一个包含数百页的滑块。由于没有适合我需要的第一方解决方案,我已经适应了UIPageViewController。主要用例:滑块应允许点击其内容,这将(取消)隐藏导航栏。单独隐藏效果很好,但是当我在导航栏隐藏时滑动时,它会重新出现。

如何根据navigationBarHidden(hidden)隐藏导航栏?

一步一步:

  1. 点击隐藏(导航栏现已隐藏)
  2. 滑动(仍然隐藏)
  3. 滑动(出现)

在大多数情况下,需要两次滑动才能取消隐藏栏,但有时只需滑动一次即可取消隐藏。

这是应用程序的 SwiftUI 部分:

struct ApplicationView: View {

    var body: some View {
        NavigationView {
            ContentView()
                .navigationBarTitle(Text("Test"), displayMode: .inline)
        }
    }

}

struct ContentView: View {

    @State private var hidden = false
    @State private var currentPage = 0

    var body: some View {
        PagerView(0..<100, currentPage: $currentPage) {
            Text("\($0)")
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .onTapGesture { hidden.toggle() }
        }
        .navigationBarHidden(hidden)
        .ignoresSafeArea()
        .background(Color.red.ignoresSafeArea())
    }

}

以及改编后的UIPageViewController

struct PagerView<Data: RandomAccessCollection, Page: View>: UIViewControllerRepresentable {

    private let data: Data
    @Binding var currentPage: Data.Index
    private let interPageSpacing: CGFloat
    private let content: (Data.Element) -> Page

    init(
        _ data: Data,
        currentPage: Binding<Data.Index>,
        interPageSpacing: CGFloat = 0,
        @ViewBuilder content: @escaping (Data.Element) -> Page
    ) {
        self.data = data
        self._currentPage = currentPage
        self.interPageSpacing = interPageSpacing
        self.content = content
    }

    func makeUIViewController(context: Context) -> UIPageViewController {
        let pageViewController = UIPageViewController(
            transitionStyle: .scroll,
            navigationOrientation: .horizontal,
            options: [.interPageSpacing: interPageSpacing]
        )
        pageViewController.delegate = context.coordinator
        pageViewController.dataSource = context.coordinator
        return pageViewController
    }

    func updateUIViewController(_ uiViewController: UIPageViewController, context: Context) {
        let direction: UIPageViewController.NavigationDirection

        if let previousViewController = uiViewController.viewControllers?.first as? PageViewController<Page> {
            guard previousViewController.index != currentPage else { return }
            direction = previousViewController.index < currentPage ? .forward : .reverse
        } else {
            direction = .forward
        }

        let page = context.coordinator.page(for: currentPage)
        uiViewController.setViewControllers([page], direction: direction, animated: true)
    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(self)
    }

    class Coordinator: NSObject, UIPageViewControllerDelegate, UIPageViewControllerDataSource {

        private var parent: PagerView

        init(_ parent: PagerView) {
            self.parent = parent
        }

        func page(for index: Data.Index) -> PageViewController<Page> {
            return PageViewController(rootView: parent.content(parent.data[index]), index: index)
        }

        func pageViewController(
            _ pageViewController: UIPageViewController,
            viewControllerBefore viewController: UIViewController
        ) -> UIViewController? {
            guard parent.currentPage > parent.data.startIndex else { return nil }
            return page(for: parent.data.index(parent.currentPage, offsetBy: -1))
        }

        func pageViewController(
            _ pageViewController: UIPageViewController,
            viewControllerAfter viewController: UIViewController
        ) -> UIViewController? {
            guard parent.currentPage < parent.data.index(parent.data.endIndex, offsetBy: -1) else { return nil }
            return page(for: parent.data.index(parent.currentPage, offsetBy: 1))
        }

        func pageViewController(
            _ pageViewController: UIPageViewController,
            didFinishAnimating finished: Bool,
            previousViewControllers: [UIViewController],
            transitionCompleted completed: Bool
        ) {
            if completed, let viewController = pageViewController.viewControllers?.first as? PageViewController<Page> {
                parent.currentPage = viewController.index
            }
        }

    }

    class PageViewController<Content: View>: UIHostingController<Content> {

        var index: Data.Index

        init(rootView: Content, index: Data.Index) {
            self.index = index
            super.init(rootView: rootView)
        }

        @MainActor @objc required dynamic init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }

        override func viewDidLoad() {
            super.viewDidLoad()
            view.backgroundColor = .clear
        }

    }

}

【问题讨论】:

    标签: ios swift swiftui


    【解决方案1】:

    i was a quick solution,but i'm not very sure :)

    更新解决方案

      //your code 
      var body: some View {
            PagerView(0..<100, currentPage: $currentPage) {
                Text("\($0)")
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
    
                    // page contain evey text in UIHostingController      
                    .navigationBarHidden(true)
                    .onTapGesture { hidden.toggle() }
            }
            .navigationBarHidden(hidden)
            .ignoresSafeArea()
            .background(Color.red.ignoresSafeArea())
        }
    

    像:a -> b

    一个:

    NavigationView{
        let b = controller
        b.navigationBarHidden(true)
        link:b
    }
    

    b:
    i use a navigationView{} to contain my UI code in b

    //wrong!! can not a <- b
    NavigationView {
        ZStack{
           custom navigationBar
           pageController
           ...
        }
    
    }
    
    //can a <- b
    ZStack{
           custom navigationBar
           //just page
           NavigationView {
              pageController
           }
          
           ...
    }
    
    

    然后,它解决了 a -> b 临时问题 只是个把戏,我没花时间去挖掘

    another solution,它对我有用(2/16/2022)

            let a = AnyView(TestRedView()).navigationBarHidden(true)
            let b = AnyView(TestBlueView()).navigationBarHidden(true)
            
            return PaginationView(pages: [a, b])
                .navigationBarTitle(Text("Test"), displayMode: .inline)
                .navigationBarHidden(true)
                .edgesIgnoringSafeArea(.all)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-04
      • 1970-01-01
      • 1970-01-01
      • 2016-12-07
      • 1970-01-01
      相关资源
      最近更新 更多