带有大标题的 UINavigationBar - 如何在 iOS 11 中找到额外的高度

UINavigationBar with Large Titles - how to find extra height in iOS 11

iOS 11 中为 UINavigationControllerUINavigationBar 使用 prefersLargeTitles 时,导航栏会增加高度。在我检查过的 iPhone 上,增加是从 44 到 96,但我认为这些数字可以根据设备改变(或者至少我们需要编码,好像它们可以)。

我想以编程方式找到 'extra' 高度 - 显示大标题时添加到传统 UINavigationBar 下方的大标题区域的高度。我可以很容易地找到显示大标题的栏的整个高度,但是有没有办法以编程方式单独找到栏的大标题部分的高度(没有任何硬编码)?

我需要这个的原因是有时我想以编程方式滚动到 UITableView 的顶部,下拉大标题(在 "regular height" 导航栏下向上滚动)以便它正在显示,我需要的内容偏移量是导航栏的额外高度。我可以使用导航栏的总高度,但这会将 UITableView 拉得太低。现在要做到这一点,我需要硬编码如下:

[self.tableView setContentOffset:CGPointMake(0, -52) animated:NO];

您可以简单地滚动到 table 视图的顶部 而无需 知道导航栏的大小。 UITableView 附带一个 API。

选项 1

// Get the first index path. You might want to check here whether data source item exists.
NSIndexPath *firstIndexPath = [NSIndexPath indexPathForRow:0
                                                 inSection:0];
// Scroll to it. You can play with scroll position to get what you want
[tableView scrollToRowAtIndexPath:firstIndexPath
                 atScrollPosition:UITableViewScrollPositionTop
                         animated:YES];

选项 2

CGPoint offset = CGPointMake(0.0, -tableView.contentInset.top);
[tableView setContentOffset:offset animated:YES];

我刚遇到同样的问题。

 -(void)layoutSubviews{
            [super layoutSubviews];
            CGRect rectStatusBar = [[UIApplication sharedApplication] statusBarFrame];
            if (rectStatusBar.size.height==44.f) {
               //statusBar shows
            }else{
                if (@available(iOS 11.0, *)) {
                    for ( UIView*view in self.subviews) {
                        if ([NSStringFromClass(view.classForCoder) isEqualToString:@"_UINavigationBarContentView"]) {
                            view.frame = CGRectMake( 0,20,view.frame.size.width,44);
                        }
                        else if ([NSStringFromClass(view.classForCoder) isEqualToString:@"_UIBarBackground"]) {
                            view.frame = CGRectMake(0,0,view.frame.size.width, 64);
                        }
                    }
                }
            }
        }

据我了解,您的 tableView 原点位于导航栏下方,在您的 UIViewController 视图中的 y = 0 处。

您的 tableView 应该将其顶部绑定修复到顶部布局指南,或者使用新的安全区域。这样您就不必以编程方式计算导航栏的大小。

如果您从未使用过它,请查看自动布局。

我认为您找不到明确解决问题的方法。您所要求的是不向用户公开的导航栏内部结构的一部分。不过,我想我可以为您提供解决方法。

为了理解这一点,让我们看一下视图调试器中的导航栏(启用了大标题)。正如您在下图中看到的,有一个额外的视图,其中包含特定视图控制器的大标题。当您不支持大标题时,此视图不存在。

基本上,您想知道的是那个视图的高度。

这是我的提议solution/workaround

extension UINavigationBar
{
    var largeTitleHeight: CGFloat {
        let maxSize = self.subviews
            .filter { [=10=].frame.origin.y > 0 }
            .max { [=10=].frame.origin.y < .frame.origin.y }
            .map { [=10=].frame.size }
        return maxSize?.height ?? 0
    }
}

它的作用

  1. 过滤掉从导航栏顶部开始的子视图。它们很可能是背景或导航区域,我们不需要它们。
  2. 在导航栏中找到最低的子视图。
  3. 将结果子视图框架映射到框架大小。
  4. Returns 找到的子视图的高度,如果找到 none,则为 0。

这种方法的明显缺点是它与导航栏的结构紧密相关,而导航栏的结构将来可能会发生变化。但是,除了一个或另一个肮脏的把戏之外,您不太可能得到更多东西。

执行结果应该如下所示。

print("Extra height: \(navigationController!.navigationBar.lagreTitleHeight)")
Extra height: 52.0

似乎调用 navigationBar.sizeToFit() 会强制 navigationBar 调整其大小,因为它显示了大标题。因此,您可以轻松计算出顶部安全区域。接下来是我们想出的解决方案:

self.navigationController?.navigationBar.sizeToFit()
let navigationBarOffset = navigationController?.navigationBar.frame.origin.y ?? 0
let navigationBarHeight = navigationController?.navigationBar.frame.height ?? 0
let offset = -navigationBarOffset - navigationBarHeight
if collectionView.contentOffset.y > offset {
let contentOffset = CGPoint(x: 0, y: offset)
   collectionView.setContentOffset(contentOffset, animated: true)
}