【问题标题】:iOS 7 - Layout adviceiOS 7 - 布局建议
【发布时间】:2015-08-23 18:21:14
【问题描述】:

我的视图控制器中有以下布局。我希望能够垂直滚动,标题滚动出视图并且 UISegmentedControl 粘在视图的顶部,除此之外,剩余的滚动应该由集合视图处理。

但是,对于实现此布局的最佳方法是什么,我有点困惑。

我尝试了一些结果好坏参半的实现:

  1. 以 UICollectionView 作为子视图的 UIScrollView: UIScrollView 作为父视图,以标题、分段控件和集合视图作为子控件。这种方法的问题是嵌套滚动似乎无法正常工作。为了能够滚动 UIScrollView,点击需要在 CollectionView 区域之外,否则只有 CollectionView 滚动并且标题和分段控件不会移动。

  2. 标题单元格中的标题和分段控件:我尝试了另一种方法,即使用单个 CollectionView。我将标题和分段控件添加为集合视图的单个标题单元格的子视图。当分段控件值发生变化时,我切换CollectionView的数据源属性,实现collection view需要的3个view。视觉上一切都很完美。这里唯一的问题是在第一个、第二个和第三个选项卡之间快速切换时的竞争条件。我从 Web 服务加载数据,如果 Web 服务需要时间并且仍在加载数据并且我快速切换选项卡然后我遇到错误,其中返回的数据与当前选择的集合视图不同,很多无序同步问题。

  3. 更新自动布局约束的常量值: 我尝试的另一种方法是更改​​应用于“标题”视图的自动布局约束的常量值。然后我在视图控制器的视图中添加了一个手势来跟踪滚动,当用户垂直滚动时,我调整了自动布局约束的常量,以便“标题”单元格弹出视图。再次,这似乎并不顺利,但我想我可以调整它,但它似乎有点像黑客。

有没有更好的方法来实现这个布局?

【问题讨论】:

  • 您需要使用 AutoLayout 吗?
  • 是的,需要自动布局
  • 第二种方法中遇到的问题可能的解决方案(其实我认为这个问题不是你选择的布局问题)可以在第一部分“在iOS上构建并发用户界面”中找到" WWDC 2012 会话。

标签: ios objective-c uiscrollview autolayout uicollectionview


【解决方案1】:

#2 似乎是一个很好的解决方案——滚动手势将最符合用户的期望,因为它都是一个单一的滚动视图。 (我同意 #3 听起来像是一个 hack。)您可以使用一些 custom layout attributes 使标题“粘性”。

这里唯一的问题是在第一个、第二个和第三个选项卡之间快速切换时的竞争条件。

这是切换视图时异步加载的常见问题(尤其是当您将数据加载到单个单元格中时,这些单元格在您滚动时会被重用)。重要的是,在收到数据后,您始终检查接收方是否仍在期待它;即,您应该在更改支持数据源之前检查分段控制值。您还可以:

  • 为不同的段使用单独的数据源对象,让每个对象管理自己的数据提取,以免混淆。
  • 在快速切换选项卡时,尽可能取消未完成的请求,以避免不必要的网络请求。
  • 缓存数据以避免每次切换标签时重新获取。

【讨论】:

    【解决方案2】:

    我认为您希望获得与 pinterest 个人资料页面相同的功能。要轻松实现此类功能,您需要执行以下操作。

    Step 1:将UIView添加为tableHeaderView那些在向上滚动时炫耀的。

    self.tableHeaderView = yourView
    

    第 2 步:在部分标题视图中添加 UISegmentControl

     - (UITableViewHeaderFooterView *)headerViewForSection:(NSInteger)section{
      return your_segmentcontrolView;
    
    }
    

    第 3 步:将UICollectionView 添加到第一部分的第一行。

    通过实现以下方式,您可以获得所需的功能。

    希望对您有所帮助。

    【讨论】:

    • 这听起来可能,我不确定我是否会在滚动混乱中遇到同样奇怪的滚动,基本上它与我的解决方案#1 相同,而不是你建议我可以使用的滚动视图本身使用滚动视图的 TableView。您过去尝试过这种方法吗?
    • @newbie,是的,我已经在可能的应用程序之一中实现了。有人建议我UITableView,因为部分视图的标题会自动粘在屏幕顶部,直到新部分到达顶部。所以你不需要玩偏移量。如果你想玩 UIScrollview 那么事件你没有得到 UItableView 提供的流畅行为。
    • @newbie,如果我的回答对你有帮助,那么你能奖励赏金吗?
    • 我不喜欢用户要求 OP 将赏金奖励给他们,因为虽然您的回答 可能 有所帮助,但问题可能会有更好的答案.从我个人的角度来看,我认为这并不能完全回答这个问题,如果是我,我会说它需要更多的描述才能获得 200 名代表。诸如 1jbandes 之类的描述将是我希望在获得赏金之前看到的描述类型。
    • @None 这里有答案,虽然比你的要好得多,因为它们带有解释。但无论哪种方式,您都不应该要求他们奖励您所需的赏金,让他们自己做出决定。如果这意味着他们会奖励您,那么如果不是,那就太好了,那么请继续下一个问题并帮助下一个人。
    【解决方案3】:

    您可以考虑的另一种方法:

    使用 UITableView 来包含您的 UI

    • 创建一个UITableView,并将你的header设置为UITableView的headerView。
    • 使用 sectionHeader 来包含 segmentedControl。
    • 将您的collectionView 放在一个UITableViewCell 中。或者,您也可以使用 UITableView 的 footerView 来包含 gridView。

    通过使用 sectionHeader,这应该允许标题滚动到视图之外,但是 sectionHeader 将粘在导航栏下方或 contentView 的顶部,直到另一个部分进入视图(在您的情况下,您将只有一个部分.)

    【讨论】:

      【解决方案4】:
      • 将标题视图、正文视图(保持段视图和集合视图)添加到滚动视图中。
      • 最初将集合视图的 userInteractionEnabled 属性设置为“NO”。
      • 始终跟踪滚动视图的昆虫。
      • 如果滚动的昆虫的 y 坐标大于标题视图的高度,则将 userInteractionEnabled 属性设置为“YES”,以便之后可以滚动集合视图。
      • 如果用户在滚动视图之外滚动并试图将标题视图拉下,即滚动视图y坐标昆虫小于标题视图的高度,则立即更改集合视图的用户迭代模式并允许用户滚动滚动视图直到顶部。

      【讨论】:

        【解决方案5】:

        您可以使用库/cocoapod 为您进行设置,而不是手动实现。这个看起来很合适:https://github.com/iosengineer/BMFloatingHeaderCollectionViewLayout

        另外,代码是开源的,所以您可以随时根据需要进行修改。

        【讨论】:

        • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会失效。
        • 感谢@MayankJain 的输入。该链接指向一个执行问题所要求的库(而不是教程或其他内容),因此我将更新我的答案以明确这一点。
        【解决方案6】:

        我只能说你需要继承 UIView 并使其成为 UIGestureRecognizerDelegate 和 UICollectionViewDelegate 的代表,然后在你的 UIView 子类中,执行以下操作,因为代码虽然拥有,但我无法提供更多信息就我自己而言,它是专有的,可能会激怒我使用它的很多组织,所以这里是秘诀:

        CGPoint contentOffset = [scrollView contentOffset];
        CGFloat newHeight = [_headerView maxHeight] - contentOffset.y;
        CGRect frame = [_headerView frame];
        
        if (newHeight > [_headerView maxHeight]) {
            frame.origin.y = contentOffset.y;
            frame.size.height = [_headerView maxHeight];
            [_headerView setFrame:frame];
        } else if (newHeight < [_headerView minHeight]) {
        
            frame.origin.y = contentOffset.y;
            frame.size.height = [_headerView minHeight];
            [_headerView setFrame:frame];
        } else {
            frame.origin.y = contentOffset.y;
            frame.size.height = newHeight;
            [_headerView setFrame:frame];
        }
        if ([_delegate respondsToSelector:@selector(scrollViewDidScroll:)]) {
            return [_delegate scrollViewDidScroll:scrollView];
        }
        

        您必须将另一个定义为此自定义 UiCollectionView 标头的 UIView 子类化。然后,您必须在 UIView/UICollectionView 委托的自定义子视图中声明 UIView 自定义标题视图,然后在 UICollctionViewdelegate 中设置该自定义子视图的标题。然后,您应该将 UIView/UIcollectionView 的复合子类拉入您的 UIViewController。哦,是的,并且在您的 layoutSubViews 中,确保您执行通过双层子类传递的高度计算。因此,您将拥有以下文件:

        1. UIVew 这是 UICollectionView 的代表,也是我之前提到的

        2. UIView 这是一个 UISCrollViewDelegate 这是标题视图

        3. 在编号 1 中拉入子类 UIView 的 UIViewController

        4. 1 号的 UIView 子类拉入 2 号并将其设置为标题

        在第 4 部分,请确保您执行以下操作:

        - (CGFloat)maxHeight
        {
            if (SCREEN_WIDTH == 414)
            {
                return 260;
        
            }else if (SCREEN_WIDTH == 375)
            {
                return 325;
        
            }else
            {
                return 290;
            }
        }
        
        - (CGFloat)minHeight
        {
            if (SCREEN_WIDTH == 414)
            {
        
                return 90;
            }else if (SCREEN_WIDTH == 375)
            {
                return 325;
            }else
            {
                return 290;
            }
        }
        

        这将传递给 UIView 子类,它是我已经解释过的复合子类。这个想法是在这个header UIView的子类(上面的第2个)中捕获你的header的maxHeight,然后将它传递给在scrollViewDidScroll中截取这些值的主UIView子类。

        最后一点信息,确保你在所有方法中设置你的 layoutSubviews 来拦截滚动事件。例如上面的数字 1,layoutsubviews 方法是这样的:

        - (void)layoutSubviews
        {
            CGRect frame = [_headerView frame];
            frame.size.width = [self frame].size.width;
            [_headerView setFrame:frame];
            [super layoutSubviews];
        }
        

        这就是我能给你的全部了,我希望我能发布更多,但这应该让你了解它是如何在生产环境中为你在野外看到的大型应用程序完成的。

        还有一点需要注意。当你开始走上这样密集的实现之路时,不要惊讶地发现,例如,应用程序中的单个视图控制器可以使用我解释过的方法,将有 30-40 个自定义子类它们本身就是子类,或者是复合子类或我自己的子类或我自己的子类的子类。我告诉你这个是为了让你知道需要多少代码才能做到这一点,不是吓唬你,而是让你知道可能需要一段时间才能做到这一点,并且不要自责如果需要一段时间才能工作。祝你好运!!

        【讨论】:

          猜你喜欢
          • 2020-06-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-04-03
          • 2014-06-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多