UIColViewView里面的UIColViewView - 动态高度?

Sha*_*man 104 uitableview ios uicollectionview

我们的一个应用程序屏幕要求我们放置UICollectionView一个UITableViewCell.这UICollectionView将具有动态数量的项目,从而产生必须动态计算的高度.但是,我在尝试计算嵌入式高度时遇到了问题UICollectionView.

我们的总体设计UIViewController是在故事板中创建的,并且确实利用了自动布局.但是,我不知道如何动态增加UITableViewCell基于高度的高度UICollectionView.

任何人都可以提供一些如何实现这一目标的建议或建议吗?

Pab*_*meu 111

正确的答案是YES,你CAN做到这一点.

我几周前遇到过这个问题.它实际上比你想象的要容易.将您的单元格放入NIB(或故事板)并固定它们,让自动布局完成所有工作

鉴于以下结构:

的TableView

TableViewCell

的CollectionView

CollectionViewCell

CollectionViewCell

CollectionViewCell

[...可变数量的细胞或不同的细胞大小]

解决方案是告诉自动布局首先计算collectionViewCell大小,然后集合视图contentSize,并将其用作单元格的大小.这是"做神奇" 的UIView方法:

-(void)systemLayoutSizeFittingSize:(CGSize)targetSize 
     withHorizontalFittingPriority:(UILayoutPriority)horizontalFittingPriority 
           verticalFittingPriority:(UILayoutPriority)verticalFittingPriority
Run Code Online (Sandbox Code Playgroud)

你必须在这里设置TableViewCell的大小,在你的情况下是CollectionView的contentSize.

CollectionViewCell

CollectionViewCell中,每次更改模型时都必须告诉单元格布局(例如:您使用文本设置UILabel,然后单元格必须再次布局).

- (void)bindWithModel:(id)model {
    // Do whatever you may need to bind with your data and 
    // tell the collection view cell's contentView to resize
    [self.contentView setNeedsLayout];
}
// Other stuff here...
Run Code Online (Sandbox Code Playgroud)

TableViewCell

TableViewCell确实神奇.它有一个出口,以您的的CollectionView,能够使用的CollectionView细胞自动布局estimatedItemSize的的UICollectionViewFlowLayout.

然后,诀窍是在systemLayoutSizeFittingSize ...方法中设置tableView单元格的大小.(注意:iOS8或更高版本)

注:我试图用的tableView的代表单元的高度方法-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath.但为时已晚的自动布局系统来计算的CollectionView contentSize,有时你可能会发现错误调整细胞.

@implementation TableCell

- (void)awakeFromNib {
    [super awakeFromNib];
    UICollectionViewFlowLayout *flow = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;

    // Configure the collectionView
    flow.minimumInteritemSpacing = ...;

    // This enables the magic of auto layout. 
    // Setting estimatedItemSize different to CGSizeZero 
    // on flow Layout enables auto layout for collectionView cells.
    // https://developer.apple.com/videos/play/wwdc2014-226/
    flow.estimatedItemSize = CGSizeMake(1, 1);

    // Disable the scroll on your collection view
    // to avoid running into multiple scroll issues.
    [self.collectionView setScrollEnabled:NO];
}

- (void)bindWithModel:(id)model {
    // Do your stuff here to configure the tableViewCell
    // Tell the cell to redraw its contentView        
    [self.contentView layoutIfNeeded];
}

// THIS IS THE MOST IMPORTANT METHOD
//
// This method tells the auto layout
// You cannot calculate the collectionView content size in any other place, 
// because you run into race condition issues.
// NOTE: Works for iOS 8 or later
- (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize withHorizontalFittingPriority:(UILayoutPriority)horizontalFittingPriority verticalFittingPriority:(UILayoutPriority)verticalFittingPriority {

    // With autolayout enabled on collection view's cells we need to force a collection view relayout with the shown size (width)
    self.collectionView.frame = CGRectMake(0, 0, targetSize.width, MAXFLOAT);
    [self.collectionView layoutIfNeeded];

    // If the cell's size has to be exactly the content 
    // Size of the collection View, just return the
    // collectionViewLayout's collectionViewContentSize.

    return [self.collectionView.collectionViewLayout collectionViewContentSize];
}

// Other stuff here...

@end
Run Code Online (Sandbox Code Playgroud)

TableViewController

请记住在TableViewController上为tableView单元格启用自动布局系统:

- (void)viewDidLoad {
    [super viewDidLoad];

    // Enable automatic row auto layout calculations        
    self.tableView.rowHeight = UITableViewAutomaticDimension;
    // Set the estimatedRowHeight to a non-0 value to enable auto layout.
    self.tableView.estimatedRowHeight = 10;

}
Run Code Online (Sandbox Code Playgroud)

信用:@rbarbera帮助解决了这个问题

  • 当在`systemLayoutSizeFittingSize`中将帧高设置为最大值时,我在重新加载单元格时遇到swift问题,应用程序似乎停留在`self.collectionView.layoutIfNeeded()`之后.我通过将框架高度设置为1而不是最大高度来修复它.如果有人偶然发现这个问题,希望它有所帮助. (15认同)
  • 以下是为我修复的问题 - 在`systemLayoutSizeFitting`中,反转当前答案中显示的顺序,以便首先运行`collectionView.layoutIfNeeded()`,然后`collectionView.frame = CGRect.init(原点:.zero, size:CGSize.init(width:targetSize.width,height:1))` (5认同)
  • 最后这对我有用,但我不得不做另一个技巧:`cell.bounds = CGRectMake(0,0,CGRectGetWidth(tableView.bounds),10000);`这将"模拟"具有长collectionView的长单元格,所以collectionView将计算其所有单元格的大小.否则collectionView仅计算可见单元格大小,并错误地设置talbeViewCell高度. (4认同)
  • 是啊.这有效.这应该是公认的答案. (3认同)

Igo*_*gor 84

我认为,我的方式更简单,然后由@PabloRomeu提出.

步骤1.创建出口UICollectionViewUITableViewCell subclass,其中UICollectionView被放置.让,它的名字将是collectionView

步骤2.在IB中添加UICollectionView高度约束并创建出口UITableViewCell subclass.让,它的名字将是collectionViewHeight.

第3步.tableView:cellForRowAtIndexPath:添加代码:

// deque a cell
cell.frame = tableView.bounds;
[cell layoutIfNeeded];
[cell.collectionView reloadData];
cell.collectionViewHeight.constant = cell.collectionView.collectionViewLayout.collectionViewContentSize.height;
Run Code Online (Sandbox Code Playgroud)

  • 这个像冠军一样......简单而有效......谢谢.如果对某些人来说它不起作用那么原因可能是,我们需要将集合视图的底部绑定到表视图单元格的底部.然后它将开始工作.快乐的编码.. :) (3认同)
  • 我有它的工作。我在UITableViewCell的所有侧面添加了collectionView的约束,并设置了tableView.estimatedRowHeight = 44;和tableView.rowHeight = UITableViewAutomaticDimension;。 (2认同)
  • 如果通过旋转iPad调整屏幕大小,这是否可行?这可能会改变集合视图的高度,从而改变单元格的高度,但是如果在屏幕上,单元格可能不一定会被表视图重新加载,因此从不调用cellForRowAtIndexPath:方法而不给你机会更改约束常量.也许可以强制reloadData. (2认同)
  • 您是否尝试过不同的 collectionView 单元格的大小?那是我的问题。如果您有不同的 collectionView 单元格大小,则它不起作用... :( (2认同)

Riv*_*era 21

表视图和集合视图都是UIScrollView子类,因此不希望嵌入到另一个滚动视图中,因为它们尝试计算内容大小,重用单元格等.

我建议您仅使用集合视图来实现所有目的.

您可以将其划分为多个部分,并将某些部分的布局"视为"视图,将其他部分视为集合视图.毕竟,使用表视图的集合视图无法实现任何功能.

如果您的集合视图"部件"具有基本网格布局,则还可以使用常规表格单元格来处理它们.如果你不需要iOS 5支持,你应该更好地使用集合视图.

  • 您的答案假设您从头开始,并且还没有您需要适应的大型复杂tableview.这个问题专门询问了如何将一个集合视图放在tableview中.IMO你的回答不是"答案" (3认同)
  • @ Rivera,你的答案是最合理的,听起来也是正确的.我在很多地方都读过这个建议.您是否也可以分享一些关于如何实现或至少一些指针的示例或tute的链接.TIA (2认同)
  • 这是一个不好的答案。在表视图中包含集合视图是一件很常见的事情,几乎每个应用程序都可以做到。 (2认同)

小智 9

我通读了所有的答案。这似乎适用于所有情况。

override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
        collectionView.layoutIfNeeded()
        collectionView.frame = CGRect(x: 0, y: 0, width: targetSize.width , height: 1)
        return collectionView.collectionViewLayout.collectionViewContentSize
}
Run Code Online (Sandbox Code Playgroud)


小智 7

帕勃罗·罗梅(Pablo Romeu)的上述回答(/sf/answers/2335486471/)对我的问题有极大帮助。但是,我必须做一些不同的事情才能使它解决我的问题。首先,我不必layoutIfNeeded()经常打电话。我只需要collectionViewsystemLayoutSizeFitting函数中调用它。

其次,我在表格视图单元格中的集合视图上具有自动布局约束,以对其进行填充。因此,targetSize.width在设置collectionView.frame的宽度时,我不得不从减去前导和尾随页边距。我还必须将顶部和底部边距添加到返回值的CGSize高度。

为了获得这些约束常量,我可以选择为约束创建出口,对它们的常量进行硬编码,或者通过标识符查找它们。我决定选择第三个选项,以使自定义表格视图单元格类易于重用。最后,这就是我需要使其工作的一切:

class CollectionTableViewCell: UITableViewCell {

    // MARK: -
    // MARK: Properties
    @IBOutlet weak var collectionView: UICollectionView! {
        didSet {
            collectionViewLayout?.estimatedItemSize = CGSize(width: 1, height: 1)
            selectionStyle = .none
        }
    }

    var collectionViewLayout: UICollectionViewFlowLayout? {
        return collectionView.collectionViewLayout as? UICollectionViewFlowLayout
    }

    // MARK: -
    // MARK: UIView functions
    override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
        collectionView.layoutIfNeeded()

        let topConstraintConstant = contentView.constraint(byIdentifier: "topAnchor")?.constant ?? 0
        let bottomConstraintConstant = contentView.constraint(byIdentifier: "bottomAnchor")?.constant ?? 0
        let trailingConstraintConstant = contentView.constraint(byIdentifier: "trailingAnchor")?.constant ?? 0
        let leadingConstraintConstant = contentView.constraint(byIdentifier: "leadingAnchor")?.constant ?? 0

        collectionView.frame = CGRect(x: 0, y: 0, width: targetSize.width - trailingConstraintConstant - leadingConstraintConstant, height: 1)

        let size = collectionView.collectionViewLayout.collectionViewContentSize
        let newSize = CGSize(width: size.width, height: size.height + topConstraintConstant + bottomConstraintConstant)
        return newSize
    }
}
Run Code Online (Sandbox Code Playgroud)

作为通过标识符检索约束的帮助函数,我添加了以下扩展名:

extension UIView {
    func constraint(byIdentifier identifier: String) -> NSLayoutConstraint? {
        return constraints.first(where: { $0.identifier == identifier })
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:您将需要在情节提要中或在创建约束的任何位置设置标识符。除非它们的常数为0,否则没关系。同样,如Pablo的回复一样,您将需要将其UICollectionViewFlowLayout用作收藏视图的布局。最后,确保将collectionViewIBOutlet 链接到情节提要。

通过上面的自定义表格视图单元格,我现在可以在需要收集视图并使其实现UICollectionViewDelegateFlowLayoutUICollectionViewDataSource协议的任何其他表格视图单元格中将其子类化。希望这对其他人有帮助!


Ink*_*lem 2

我会在集合视图类上放置一个静态方法,该方法将根据其所拥有的内容返回一个大小。然后使用该方法返回heightForRowAtIndexPath正确的大小。

另请注意,当您嵌入这些类型的 viewController 时,您可能会遇到一些奇怪的行为。我做过一次,但遇到了一些奇怪的记忆问题,但从未解决过。