自定义UITableViewCell的布局完成后?

问题描述 投票:6回答:4

我在故事板文件中定义了一个自定义表格视图单元格。

此单元格包含滚动视图,其中包含少量内容页面和页面指示符。滚动视图,内容视图和单元格本身都使用自动布局定义。 Page1 / 2/3视图绑定的宽度与滚动视图相同,容器绑定的宽度为滚动视图的3倍。这样,我不确定内容的大小是否与显示三页信息无关,无论屏幕大小如何。

我在自定义单元格类中有一个代码,当滚动视图滚动时更新页面指示器 - 这很好。

我还在单元类中公开了方法来从外部指定页面,在这个setter中我正在做类似的事情:

- (void)setPage:(NSInteger)page animated:(BOOL)animated
{
    CGFloat pageWidth = self.scrollView.frame.size.width;
    self.pageControl.currentPage = page;
    [self.scrollView setContentOffset:CGPointMake(page*pageWidth, 0) animated:animated];
}

我正面临的问题是滚动视图框架,当我调用此setter时,内容大小未正确定义,因此页面指示器将被设置,但滚动视图不会更改内容偏移。

我在哪里可以看到我的单元格自动布局完成的时刻,我可以保证setContentOffset可以工作?我试图从setPage调用cellForIndexPath - 这显然不起作用,然后我试图从willDisplayCell调用它 - 同样的故事 - 单元格本身已经正确布局,但滚动视图仍然有600px(来自故事板),它失败了。

ios objective-c cocoa-touch uitableview autolayout
4个回答
2
投票

您似乎已经设置了定制实现,因此这可能有用。

在表视图设置其子视图之前调用-setPage:animated:方法,让单元格有机会执行相同操作,或者自动布局启动将无效,除非您在单元格中维护某些状态(可能只是页码)并做出反应以下一个或多个机会:

  1. 等到你的UITableViewController的-viewDidLayoutSubviews方法触发,然后调用-cellForRowAtIndexPath:来获取单元格。然后,该单元应具有有效帧。
  2. 在自定义UITableViewCell中覆盖-didMoveToSuperview。到调用此帧时,帧也应该有效。

在这些点中的任何一个之后,您可以检查自定义单元格的状态(应包含要显示的页码)并滚动到相应的点。重构你的方法以设置状态(单元格上的整数属性)并且有一个单独的私有方法可能是有意义的,-setPage:animated:和上面两个覆盖中的一个可以调用来计算偏移并进行滚动。


建议的替代方案:

你可以通过在单元格的contentView中嵌入一个UICollectionView来解决这个问题。当前正在实现UITableViewDataSource的UIViewController也可以实现UICollectionViewDataSource并为其提供单元格的集合视图。

假设您的UITableView单元格是屏幕的宽度和高度,您可以将嵌入式集合视图配置为基本水平分页(因为UICollectionView是UIScrollView的子类)。

设置集合视图时,使用UICollectionViewFlowLayout作为布局对象并进行配置,如下所示:

// Ensure the collection view scrolls horizontally.
let flowLayout = UICollectionViewFlowLayout();
flowLayout.scrollDirection = .Horizontal;

let collectionView = UICollectionView(frame:tableCellFrame, layout:flowLayout);

// These are actually UIScrollView properties.
collectionView.pagingEnabled = true;
collectionView.bounces = false;

然后,将每页内容放在自定义UICollectionViewCell中,集合视图将为您分页。这还有一个额外的好处,即免费为您提供分页滚动API。


18
投票

我最后做了以下事情:

在自定义单元子类中添加了layoutSubview实现

- (void)layoutSubviews
{
    [super layoutSubviews];

    [self.contentView layoutSubviews];

     // set proper content offset
    [self.scrollView setContentOffset:CGPointMake(self.frame.size.width*self.pageControl.currentPage, 0) animated:NO];
}

没有我直接在layoutSubview上调用contentView滚动视图即使在[super layoutSubview]完成后也没有正确的框架。

我没有必要将当前页码存储在内部变量中,因为pageControl已经拥有它并且在配置原始单元格信息时设置它。

有趣的是 - 最初我在该方法中添加了[self.scrollView layoutSubview],它在iOS8中运行良好,但在iOS7中没有做任何好事。我不得不上升到contentView一级,以确保它在7和8都有效。


0
投票

我发现UITableViewCell的layoutSubviews的覆盖不能正常工作。在第一次调用layoutSubviews期间,我得到了contentView的不正确边界。所以我找到了另一种解决方案:

// implementation of the UITableViewDelegate protocol
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    DispatchQueue.main.async {
        // the layout of the cell is ready
    }
}

-1
投票

由于layoutSubviews()经常被多次调用,因此很难知道布局是否是最终的。因此,我实现了对UIView的扩展,并使用它来了解要对哪个layoutSubviews()调用进行操作。

Swift 4.2

extension UIView {

  /** A Boolean value that determines whether the view or any of its subviews need a layout update.

   This can be called from -layoutSubviews to determine if the layout of all the subviews are up-to-date.
   */
  open var needsLayout: Bool {
    get {
      return needsLayout(view: self)
    }
  }

  func needsLayout(view: UIView) -> Bool {
    var result = view.needsUpdateConstraints()
    for subview in view.subviews {
      result = result || needsLayout(view: subview)
    }
    return result
  }
}

然后可以在UITableViewCell子类中简单地使用它:

class MyCustomCell: UITableViewCell {

  override func layoutSubviews() {
    super.layoutSubviews()
    if !needsLayout {
      // The layout is now complete, do what you must!
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.