【问题标题】:UITableView with side section header images带有侧部分标题图像的 UITableView
【发布时间】:2010-08-04 22:07:55
【问题描述】:

iPad 上的音乐应用有很多有趣的自定义 UI 功能,其中之一是查看 albums of a particular artist 时显示的表格视图。

它在每个部分的侧面都有图像,几乎以与部分标题视图非常相似的方式起作用。如果我向下滚动,顶部图像会一直停留在那里,直到下一张图像出现将其关闭,反之亦然。

最初,如果不重新实现 UITableView 似乎是不可能的,但我认为只要调用layoutSubviews(即每当它滚动时)。这有一些小问题:因为它在 UITableView 之外绘制,所以任何自动调整大小都可能会导致整个布局关闭。而且我真的只是不确定这一切的可行性。

有什么想法吗?


编辑:我刚刚意识到我的第一个想法是行不通的,因为图像侧本身的任何触摸都不会被 tableview 跟踪,所以如果你触摸那里就无法滚动。我认为这里最大的问题是弄清楚如何传播触摸,以便在整个视图中跟踪触摸。

【问题讨论】:

    标签: iphone uitableview ipad uikit sectionheader


    【解决方案1】:

    您可以以某种方式检查您在 tableview(或最顶层等)中的哪个部分,然后只需有一个 UIView 在该部分更改时更新,我的想法有点难以解释,但它不会要完全重做 UITableView,只需在表格视图旁边有另一个视图来处理图像,而不是尝试嵌入该功能。

    执行此操作的另一种方法可能有点棘手,但您可以尝试缩进单元格,这样它们就不会从表格视图的最左侧开始,使表格视图非常宽,并在部分标题中有自定义视图包含在其左侧的图像,并希望能正确地做到这一点?很好的问题和一个有趣的问题。

    【讨论】:

    • 实际上,两者的结合听起来很可行。在位于单元格旁边的表格视图中添加一个子视图,同时“缩进”单元格以使表格和侧视图不重叠。
    • 是的。无论如何,我相信这是一个有趣的 UI 实验,我有兴趣看看你是否能得到想要的结果 :)
    • 嗯,显然这是可能的,就像我用我的原型做的那样。但它也需要数据源/委托方的大量帮助来“缩进”单元格和节标题。无法弄清楚如何在 tableview 本身中使所有这些独立,但我想我不能太挑剔。
    • 我找到了一个更好的解决方案,作为我的原型的演变,发布在下面,但是你的帖子给了我很多帮助来解决这个问题。非常感谢。
    【解决方案2】:

    半夜胡乱想了想,终于想出了一个真正的解决办法。

    将 UITableview 嵌入到 UIScrollView 中,当用户滚动视图时,触摸会通过父滚动视图,而不是 tableview 本身。您需要做的几乎所有事情都在 layoutSubviews 方法中;由于每当用户滚动视图时都会调用滚动视图的 layoutSubviews,只需更改 tableview 的框架以保持固定在屏幕上,并更改 tableview 的 contentOffset 以匹配滚动视图。

    示例代码:

    - (void) layoutSubviews{
        tableView.frame = CGRectMake(self.contentOffset.x, self.contentOffset.y, tableView.frame.size.width, tableView.frame.size.height);
        tableView.contentOffset = self.contentOffset;   
    }
    

    此时,您可以将 tableview 调整到一侧,并在另一侧做任何您想做的事情。

    【讨论】:

      【解决方案3】:

      好的,我已经做了类似于音乐应用和聚光灯搜索所做的事情。

      我没有继承 UITableView,我只是通过它的 scrollViewDidScroll 方法跟踪了各个部分,并将标题视图添加到 tableView 的左侧(因此您必须将 tableview 放在 viewController 视图的右侧,这意味着你不能使用 UITableViewController)。

      这个方法应该在scrollViewDidScroll、ViewDidLoad、didRotateFromInterfaceOrientation中调用:(如果支持旋转)

      请记住,在您支持的任何界面方向上,您必须在左侧留出与标题大小相等的空间。

      -(void)updateHeadersLocation
      {
      for (int sectionNumber = 0; sectionNumber != [self numberOfSectionsInTableView:self.tableView]; sectionNumber++)
      {
          // get the rect of the section from the tableview, and convert it to it's superview's coordinates
          CGRect rect = [self.tableView convertRect:[self.tableView rectForSection:sectionNumber] toView:[self.tableView superview]];
      
          // get the intersection between the section's rect and the view's rect, this will help in knowing what portion of the section is showing
          CGRect intersection = CGRectIntersection(rect, self.tableView.frame);
      
          CGRect viewFrame = CGRectZero; // we will start off with zero
      
          viewFrame.size = [self headerSize]; // let's set the size
      
          viewFrame.origin.x = [self headerXOrigin];
      
          /*
      
           three cases:
      
           1. the section's origin is still showing -> header view will follow the origin
           2. the section's origin isn't showing but some part of the section still shows -> header view will stick to the top
           3. the part of the section that's showing is not sufficient for the view's height -> will move the header view up
      
           */
      
          if (rect.origin.y >= self.tableView.frame.origin.y)
          {
              // case 1
              viewFrame.origin.y = rect.origin.y;
          }
          else
          {
              if (intersection.size.height >= viewFrame.size.height)
              {
                  // case 2
                  viewFrame.origin.y = self.tableView.frame.origin.y;
              }
              else
              {
                  // case 3
                  viewFrame.origin.y = self.tableView.frame.origin.y + intersection.size.height - viewFrame.size.height;
              }
          }
      
          UIView* view = [self.headerViewsDictionary objectForKey:[NSString stringWithFormat:@"%i", sectionNumber]];
      
          // check if the header view is needed
          if (intersection.size.height == 0)
          {
              // not needed, remove it
              if (view)
              {
                  [view removeFromSuperview];
                  [self.headerViewsDictionary removeObjectForKey:[NSString stringWithFormat:@"%i", sectionNumber]];
                  view = nil;
              }
          }
          else if(!view)
          {
              // needed, but not available, create it and add it as a subview
      
              view = [self headerViewForSection:sectionNumber];
      
              if (!self.headerViewsDictionary && view)
                  self.headerViewsDictionary = [NSMutableDictionary dictionary];
      
              if (view)
              {
                  [self.headerViewsDictionary setValue:view forKey:[NSString stringWithFormat:@"%i", sectionNumber]];
      
                  [self.view addSubview:view];
              }
          }
      
      
          [view setFrame:viewFrame];
      }
      }
      

      我们还需要声明一个保持可见视图的属性:

      @property (nonatomic, strong) NSMutableDictionary* headerViewsDictionary;
      

      这些方法返回标题视图的大小和 X 轴偏移量:

      -(CGSize)headerSize
      {
      return CGSizeMake(44.0f, 44.0f);
      }
      
      -(CGFloat)headerXOrigin
      {
      return 10.0f;
      } 
      

      我已经构建了代码,以便删除任何不需要的标题视图,因此我们需要一个可以在需要时返回视图的方法:

      -(UIView*)headerViewForSection:(NSInteger)index
      {
      UIImageView* view = [[UIImageView alloc] init];
      
      if (index % 2)
      {
          [view setImage:[UIImage imageNamed:@"call"]];
      }
      else
      {
          [view setImage:[UIImage imageNamed:@"mail"]];
      }
      
      return view;
      }
      

      这是它的外观:

      它在 lanscape 中的外观,我使用约束在 tableView 的左侧给出 44px

      希望这会有所帮助:)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-07-09
        • 2014-01-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-28
        • 2023-03-06
        相关资源
        最近更新 更多