【问题标题】:10.11 NSCollectionView - determining cell size dynamically10.11 NSCollectionView - 动态确定单元格大小
【发布时间】:2016-02-01 07:38:35
【问题描述】:

AppKit Release Notes for OS X v10.11 建议可以根据每个项目调整集合视图项目的大小:

可以为 CollectionView 的所有项目全局确定项目大小(通过设置 NSCollectionViewFlowLayout 的“itemSize”属性),或者可以从一个项目更改为下一个项目(通过在 CollectionView 的委托上实现 -collectionView:layout:sizeForItemAtIndexPath: )。

在我的例子中,我的 CollectionViewItem 由一个标签组成,其中包含一个不同长度的字符串。我使用 NSCollectionView 来显示字符串数组,因为 NSStackViews 不支持数组绑定,并且不流向新行。字符串数组通过数组控制器绑定到 NSCollectionView 的内容。

我的项目的 nib 文件设置正确,根视图和标签的 Content HuggingContent Compression Resistance 优先级均为 1000,并且边缘通过以下方式对齐自动布局。

现在,NSCollectionViewLayout 的委托方法有这个签名:

func collectionView(collectionView: NSCollectionView, 
                    layout collectionViewLayout: NSCollectionViewLayout, 
                    sizeForItemAtIndexPath indexPath: NSIndexPath) -> NSSize

我现在的想法是获取项目本身,对其运行布局传递,然后返回新的项目大小。

let item = collectionView.itemAtIndexPath(indexPath)!
item.view.layout()
return item.view.bounds.size

这种方法的问题是 itemAtIndexPath 返回 nil。如果我在 nil 情况下返回默认大小,则该默认大小将用于所有单元格。

如何设置 NSCollectionView 以尊重我的项目的 AutoLayout 约束并为每个单元格动态使用计算的大小?

【问题讨论】:

  • 我的快速想法是不会使用itemAtIndexPath 获取该项目。而是从模型中获取内容并计算高度。
  • 该项目尚未在集合视图中,这就是您返回nil 的原因。集合视图在将其添加到视图之前会询问您的大小。
  • 很好的解释,但是您将如何处理执行 AutoLayout 约束的实际问题?
  • 我也对此感兴趣,但还没有正确的答案。但我认为 AutoLayout 总是自上而下工作,并且框架由集合视图设置,独立于任何 AL。所有的拥抱等只会影响项目框架的视图。项目框架本身由 CollectionViewLayout(或您正在使用的委托方法)单独定义。也许人们可以仅仅为了调整大小而分配一个自己的额外项目,应用模型(设置表示的对象),获取大小并返回它?

标签: macos cocoa autolayout nscollectionview nscollectionviewitem


【解决方案1】:

我回答了这个问题的副本,但这可能是应该针对的问题,因为它较旧。

Juul 的评论是正确的 - 项目不存在。被调用的sizeForItemAt 是一个集合,它向委托询问该数据条目的任何特定大小,它将用于帮助创建其最终视图控制器NSCollectionViewItem。因此,当您要求集合以用于帮助获取项目的方法中获取项目时,您会创建一个循环。

我们遇到的问题是,我们希望根据该数据的外观调整大小:具有正确格式的文本标签的长度,而不仅仅是字符串长度。所以我们遇到了先有鸡还是先有蛋的问题。

我想到的唯一可能更漂亮的解决方案如下:

准备

  • 子类 NSCollectionViewItem 并确保您的集合视图具有返回正确子类项的数据源。
  • 在您的 XIB 中完全使用约束。
  • 您的子类应该有一个方法,该方法可以加载要表示的数据对象——当然还有您的数据源协议方法。
  • 在第一次sizeForItemAt 调用之前的某个时间点,或者在第一个调用开始时(如果那时你还没有),手动创建一个NSCollectionViewItem 子类的实例,并使用NSNIb 的instantiate(withOwner:topLevelObjects:) 进行实例化它的 XIB 与您的子类作为所有者。将该引用存储为一种“大小调整模板”,因此您只需执行一次。代表对我来说是最容易的地方。

^注意:我的第一条路线是通过集合的makeItemWithIdentifier 尝试此操作,但它更脆弱,因为它要求集合在创建尺寸模板时具有项目。在初始sizeForItemAt 期间也无法完成(在重新加载崩溃期间访问/制作项目)。而且我担心因为它是用集合制作的,所以它可能会被重复使用,下面的方法不起作用或开始编辑可见项目。 YMMV。

sizeForItemAt

  • 直接从数据源中获取所表示的数据对象。使用我之前提到的方法,让您的尺寸模板对象代表该数据对象。
  • 访问尺寸模板的View.FittingSize,可以为项目指定其约束/优先级的最小尺寸,然后返回该值。

砰!没有经过压力测试或其他任何事情,但我没有问题,它没有做布局传递或任何事情,只是打电话给FittingSize。我还没有在网上任何地方看到这一点,所以我想写出完整的解释。

我在 Xamarin.Mac 中执行此操作,因此我的代码不会是 1:1,我不想写乱码 swift 并搞砸任何事情。

TLDR:手动实例化您将存储的 NSCollectionViewItem 子类及其 xib,不属于该集合。在 sizeForItem 填充您存储为尺寸参考的项目,并返回集合项目视图的FittingSize

【讨论】:

  • 不幸的是,这种技术被证明是极其缓慢的。至少对我来说。特别是在调整集合视图的宽度时。我的集合视图位于拆分视图内,因此用户可以拖动拆分视图分隔线来更改宽度。当我这样做时,它非常不稳定。即使在我的 M1/Max MacBook Pro 上。如果 NSCollectionView 能像 NSTableView 那样处理动态行高,那就更好了。
猜你喜欢
  • 2014-07-08
  • 1970-01-01
  • 2016-06-13
  • 2015-03-20
  • 2021-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多