我试图在
UIViewController
中呈现一个UISheetPresentationController
,以拥有一个位于我的UITabBarController
下方的永久模式,就像Apple在“查找我的”应用程序中展示的那样:参考代码:
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);
这篇文章:UISheetPresentationController with a tabBar提出了类似的问题,但没有任何答案。
首先,我们需要一个自定义的
UISheetPresentationController
来防止呈现的视图控制器覆盖选项卡栏。可以使用以下类:
class TabSheetPresentationController : UISheetPresentationController {
override func presentationTransitionWillBegin() {
super.presentationTransitionWillBegin()
if let tc = presentingViewController as? UITabBarController, let cv = containerView {
var frame = cv.frame
frame.size.height -= tc.tabBar.frame.height
cv.frame = frame
}
}
}
上面的类缩短了容器视图的高度,因此标签栏被暴露出来,允许用户与标签栏交互,即使呈现的工作表处于视图中。为了使用自定义呈现控制器,我们不能再使用要呈现的视图控制器的标准
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)
注意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
}
}
该代码基本上替换了与所呈现的视图控制器的 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
}
}
}
小凹痕可以让用户最小化工作表,而自定义的大凹痕是一种技巧,可以让工作表几乎全屏显示,而不会像通常使用标准 .large()
凹痕那样产生底层视图控制器收缩的副作用。请注意,自定义工作表演示仅在 iPhone 上以纵向方式进行了测试。可能需要进一步的工作才能完全支持横向的 iPhone,并且可能需要在 iPad 上提供不同的布局。我把它留给读者作为练习。
上面的代码基本上回答了在工作表演示中显示呈现的视图控制器的问题,同时仍然允许选项卡栏可见和活动。
然而,这带来了下一个大问题。当您从选项卡栏控制器的视图控制器之一呈现视图控制器时,呈现的视图控制器实际上是从选项卡栏控制器呈现的,而不是原始视图控制器。这意味着选项卡栏控制器中只有一个视图控制器(选项卡)可以在任何时候呈现一张工作表。使用我上面提供的代码,当您切换选项卡时,工作表将保持在视图中。如果不需要这样做,则需要添加逻辑以在选择不同选项卡时关闭工作表。
“查找我的”应用程序在所有四个选项卡上显示一个工作表。我坚信标签栏控制器只显示一张表。其内容根据当前选择的选项卡进行更新。
鉴于此,根据您自己的要求,您可能需要稍微更改我的解决方案,以便工作表的视图控制器直接由选项卡栏控制器呈现,而不是从选项卡视图控制器之一呈现。根据所选选项卡处理内容更新超出了原始范围,我将这些详细信息留给读者作为练习。