【问题标题】:UINavigationBar with Large Titles - how to find extra height in iOS 11带有大标题的 UINavigationBar - 如何在 iOS 11 中找到额外的高度
【发布时间】:2017-10-19 01:35:19
【问题描述】:

当在iOS 11 中为UINavigationControllerUINavigationBar 使用prefersLargeTitles 时,导航栏会增加高度。我检查过的 iPhone 从 44 增加到 96,但我认为这些数字可能会因设备而异(或者至少我们需要像可以编码一样进行编码)。

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

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

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

【问题讨论】:

  • 你不需要知道当前的布局,只要让第一个单元格可见,如果你想滚动到顶部(scrollToRowAtIndexPath:atScrollPosition:animated:)就足够了。
  • 谢谢,我试过了。但是,我的用例要求我滚动到显示大导航标题的位置,而这只滚动到显示顶行的位置。要显示大标题,我需要导航栏的“额外高度” - 显示大标题的区域。我试图想出另一种方法来做到这一点,但在这一点上,我确实相信我需要那个高度。
  • 这里有一条黄金法则:如果您的问题的答案是跟踪系统生成的布局边界以调整您自己的布局——您可能提出了错误的问题
  • @holex 我明白了,这就是为什么我对这个问题感到惊讶。我希望发现这是我当前问题的情况。但是,在这种特殊情况下,到目前为止,我还没有找到任何其他方式来滚动到标题被拉下的确切位置,而不是超过它的顶部 - 这就是我需要发生的事情案例,我不认为这是不合理的。由于更改容器中的视图,我正在滚动以显示大标题。
  • @holex 如何滚动表格视图以显示大标题的顶部?只是好奇,因为我认为我们都同意我错过了一些东西。

标签: ios uinavigationcontroller uinavigationbar ios11 large-title


【解决方案1】:

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

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

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

这是我提出的解决方案/解决方法

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

它的作用

  1. 过滤掉从导航栏顶部开始的子视图。它们很可能是背景或导航区域,我们不需要它们。
  2. 在导航栏中查找最低的子视图。
  3. 将生成的子视图帧映射到帧大小。
  4. 返回找到的子视图的高度,如果没有找到,则返回 0。

这种方法的明显缺点是它与导航栏的结构紧密耦合,未来可能会发生变化。但是,您不太可能得到不止一个或另一个肮脏的把戏。

执行结果应该如下所示。

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

【讨论】:

  • 是的!谢谢!并且作为侧边栏说明,您想要计算视图控制器首次显示时的额外高度,然后再滚动导航栏的额外高度部分。谢谢!
  • PS 如果你想试一试,我也只是在这个问题上提供了赏金。你显然知道你在做什么! stackoverflow.com/questions/46779985/…
【解决方案2】:

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

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

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

【讨论】:

  • 我经常使用自动布局。我已切换到 iOS 11、安全区域和大型导航标题。我想如果你重读我的问题,你会发现我并没有试图计算导航栏的大小——我的导航栏是通过自动布局完美放置的。我正在尝试在某些条件下计算滚动视图的偏移量。
  • 据我了解,您希望滚动视图的偏移量是为了不隐藏滚动视图内容,对吧?
  • 我需要这个的原因是有时我想以编程方式滚动到 UITableView 的顶部,拉下大标题以便它显示,我需要的内容偏移量是导航栏的额外高度。我可以使用导航栏的总高度,但这会将 UITableView 拉得太低。
  • 我没有尝试过,但我认为使用带有顶部布局指南或安全区域的自动布局会将您的视图放在整个栏的下方,包括大标题。这就是我从 wwdc 视频和文档中了解到的。
  • 确实如此。然而,这不是我的问题。当滚动 tableView 以在底部显示更多行时,大标题会在“常规高度”导航栏下滚动。我需要(以编程方式)滚动到 tableView 的顶部,然后滚动一些,以便大标题会回来。
【解决方案3】:

您可以简单地滚动到表格视图的顶部不知道导航栏的大小。 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];

【讨论】:

  • 谢谢,我试过了。但是,我的用例要求我滚动到显示大导航标题的位置,而这只滚动到显示顶行的位置。要显示大标题,我需要导航栏的“额外高度” - 显示大标题的区域。我试图想出另一种方法来做到这一点,但在这一点上,我确实相信我需要那个高度。
  • 如果您在scrollToRowAtIndexPath 中使用UITableViewScrollPositionBottom 会怎样?
  • 我确实尝试过,但不幸的是它不起作用。不过谢谢你的建议。
【解决方案4】:

我刚刚遇到了同样的问题。

 -(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);
                        }
                    }
                }
            }
        }

【讨论】:

  • 我不确定您的问题到底是什么,但我想避免使用硬编码值,所以很遗憾这不是解决方案。
【解决方案5】:

似乎调用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)
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多