标签栏下方的 UISheetPresentationController

Jak*_*san 8 modal-dialog uitabbarcontroller ios swift uisheetpresentationcontroller

我试图UIViewController在 a 中呈现一个位于我下方的UISheetPresentationController永久模式,就像苹果在“查找我的”应用程序中展示的那样:UITabBarController

标签栏下方的 UISheetPresentationController

参考代码:

let navigationController = UINavigationController(rootViewController: UIViewController());

navigationController.modalPresentationStyle = .formSheet;
if let sheet = navigationController.sheetPresentationController {
    sheet.detents = [.medium(), .large()];
    sheet.prefersGrabberVisible = true;
    sheet.largestUndimmedDetentIdentifier = .medium;
    sheet.prefersScrollingExpandsWhenScrolledToEdge = false;
}

present(navigationController, animated: true);
Run Code Online (Sandbox Code Playgroud)

这篇文章:UISheetPresentationController with a tabBar提出了类似的问题,但没有任何答案。

Han*_*ash 0

这个问题及其解决方案有几个方面。

首先,我们需要一个自定义UISheetPresentationController来防止呈现的视图控制器覆盖选项卡栏。可以使用以下类:

class TabSheetPresentationController : UISheetPresentationController {
    override func presentationTransitionWillBegin() {
        super.presentationTransitionWillBegin()

        // Update the container frame if there is a tab bar
        if let tc = presentingViewController as? UITabBarController, let cv = containerView {
            cv.clipsToBounds = true // ensure tab bar isn't covered
            var frame = cv.frame
            frame.size.height -= tc.tabBar.frame.height
            cv.frame = frame
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的类缩短了容器视图的高度,因此标签栏被暴露,允许用户与标签栏交互,即使呈现的工作表在视图中也是如此。它还确保如果用户拉下呈现视图,底部不会覆盖选项卡栏。

为了使用自定义的呈现控制器,我们不能再使用sheetPresentationController要呈现的视图控制器的标准属性。相反,我们需要提供自定义模式演示。

首先,创建和呈现要在工作表中显示的视图控制器的代码如下所示:

let vc = UIViewController()
let nc = UINavigationController(rootViewController: vc)
nc.modalPresentationStyle = .custom
nc.transitioningDelegate = self
nc.isModalInPresentation = true // don't let it be dismissed by dragging to the bottom

present(nc, animated: false)
Run Code Online (Sandbox Code Playgroud)

注意该行nc.transitioningDelegate = self。这就要求 所代表的类self要符合UIViewControllerTransitioningDelegate协议。

假设self是一个SomeTabViewController代表视图控制器中的选项卡之一的类。然后我们可以添加:

extension SomeTabViewController: UIViewControllerTransitioningDelegate {
    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        let sc = TabSheetPresentationController(presentedViewController: presented, presenting: source)
        sc.detents = [
            .mySmall(),
            .medium(),
            .myLarge(),
        ]
        sc.largestUndimmedDetentIdentifier = .myLarge
        sc.prefersGrabberVisible = true
        sc.prefersScrollingExpandsWhenScrolledToEdge = false
        sc.widthFollowsPreferredContentSizeWhenEdgeAttached = true
        sc.selectedDetentIdentifier = .medium

        return sc
    }
}
Run Code Online (Sandbox Code Playgroud)

该代码基本上替换了与所呈现的视图控制器的属性一起使用的常用设置代码sheetPresentationController

在示例代码中,我使用了两个自定义制动器。这些均提供以下内容:

extension UISheetPresentationController.Detent.Identifier {
    static let mySmall = UISheetPresentationController.Detent.Identifier("mySmall")
    static let myLarge = UISheetPresentationController.Detent.Identifier("myLarge")
}

extension UISheetPresentationController.Detent {
    class func mySmall() -> UISheetPresentationController.Detent {
        return UISheetPresentationController.Detent.custom(identifier: .mySmall) { context in
            return 60
        }
    }

    class func myLarge() -> UISheetPresentationController.Detent {
        return UISheetPresentationController.Detent.custom(identifier: .myLarge) { context in
            return context.maximumDetentValue - 0.1
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

小凹痕可以让用户最小化工作表,而自定义的大凹口是一个技巧,可以让工作表几乎全屏显示,而不会像通常使用标准凹口那样产生底层视图控制器收缩的副作用.large()

请注意,自定义工作表演示仅在 iPhone 上以纵向方式进行了测试。可能需要进一步的工作才能完全支持横向的 iPhone,并且可能需要在 iPad 上提供不同的布局。我把它留给读者作为练习。

上面的代码基本上回答了在工作表演示中显示呈现的视图控制器的问题,同时仍然允许选项卡栏可见和活动。

然而,这带来了下一个大问题。当您从选项卡栏控制器的视图控制器之一呈现视图控制器时,呈现的视图控制器实际上是从选项卡栏控制器呈现的,而不是原始视图控制器。这意味着选项卡栏控制器中只有一个视图控制器(选项卡)可以在任何时候呈现一张工作表。使用我上面提供的代码,当您切换选项卡时,工作表将保持在视图中。如果不需要这样做,则需要添加逻辑以在选择不同选项卡时关闭工作表。

“查找我的”应用程序在所有四个选项卡上显示一个工作表。我坚信标签栏控制器只显示一张表。其内容根据当前选择的选项卡进行更新。

鉴于此,根据您自己的要求,您可能需要稍微更改我的解决方案,以便工作表的视图控制器直接由选项卡栏控制器呈现,而不是从选项卡视图控制器之一呈现。根据所选选项卡处理内容更新超出了原始范围,我将这些详细信息留给读者作为练习。