使用自动布局指定 UICollectionView 中单元格的一维

2024-06-23

在 iOS 8 中UICollectionViewFlowLayout支持根据单元格自身的内容大小自动调整单元格大小。这会根据单元格的内容调整单元格的宽度和高度。

是否可以为所有单元格的宽度(或高度)指定固定值并允许调整其他尺寸的大小?

举一个简单的示例,考虑单元格中的多行标签,并具有将其定位到单元格两侧的约束。多行标签可以以不同的方式调整大小以适应文本。单元格应填充集合视图的宽度并相应地调整其高度。相反,单元格的大小是随意的,当单元格大小大于集合视图的不可滚动尺寸时,它甚至会导致崩溃。


iOS 8介绍方法systemLayoutSizeFittingSize: withHorizontalFittingPriority: verticalFittingPriority:对于集合视图中的每个单元格,布局都会在单元格上调用此方法,并传入估计的大小。对我来说有意义的是在单元格上覆盖此方法,传入给定的大小并将水平约束设置为必需的,并将垂直约束设置为低优先级。这样水平尺寸就固定为布局中设置的值,而垂直尺寸可以灵活调整。

像这样的东西:

- (UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes {
    UICollectionViewLayoutAttributes *attributes = [super preferredLayoutAttributesFittingAttributes:layoutAttributes];
    attributes.size = [self systemLayoutSizeFittingSize:layoutAttributes.size withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityFittingSizeLevel];
    return attributes;
}

然而,通过这种方法返回的尺寸是完全奇怪的。关于此方法的文档对我来说非常不清楚,并提到使用常量UILayoutFittingCompressedSize UILayoutFittingExpandedSize它只代表零大小和相当大的大小。

Is the size这个方法的参数真的只是传递两个常量的方法吗?有没有办法实现我期望的为给定尺寸获得适当高度的行为?


替代解决方案

1)添加将指定单元格特定宽度的约束以实现正确的布局。这是一个糟糕的解决方案,因为该约束应该设置为没有安全引用的单元格集合视图的大小。该约束的值可以在配置单元时传入,但这似乎完全违反直觉。这也很尴尬,因为直接向单元格或其内容视图添加约束会导致许多问题。

2)使用表视图。表格视图以这种方式开箱即用,因为单元格具有固定宽度,但这不能适应其他情况,例如在多列中具有固定宽度单元格的 iPad 布局。


听起来您要求的是一种使用 UICollectionView 生成类似 UITableView 的布局的方法。如果这确实是您想要的,那么正确的方法是使用自定义 UICollectionViewLayout 子类(可能类似于SBTableLayout https://github.com/blommegard/SBTableLayout).

另一方面,如果您真的想知道是否有一种干净的方法可以使用默认的 UICollectionViewFlowLayout 来执行此操作,那么我相信没有办法。即使 iOS8 具有自动调整单元大小的功能,这也不是一件简单的事。正如您所说,根本问题是流布局的机制无法提供固定一个维度并让另一个维度做出响应的方法。 (此外,即使可以,需要两次布局传递来调整多行标签的大小也会带来额外的复杂性。这可能不符合自调整单元格希望通过一次调用 systemLayoutSizeFittingSize 来计算所有大小的方式。)

但是,如果您仍然想创建一个具有流布局的类似 tableview 的布局,其中单元格可以确定自己的大小,并自然地响应集合视图的宽度,当然这是可能的。还是有那种乱七八糟的方式。我已经使用“调整单元格”来完成此操作,即控制器仅保留用于计算单元格大小的未显示的 UICollectionViewCell。

这种方法有两个部分。第一部分是让集合视图委托通过获取集合视图的宽度并使用调整大小的单元格来计算单元格的高度来计算正确的单元格大小。

在 UICollectionViewDelegateFlowLayout 中,您实现如下方法:

func collectionView(collectionView: UICollectionView,
  layout collectionViewLayout: UICollectionViewLayout,
  sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
{
  // NOTE: here is where we say we want cells to use the width of the collection view
   let requiredWidth = collectionView.bounds.size.width

   // NOTE: here is where we ask our sizing cell to compute what height it needs
  let targetSize = CGSize(width: requiredWidth, height: 0)
  /// NOTE: populate the sizing cell's contents so it can compute accurately
  self.sizingCell.label.text = items[indexPath.row]
  let adequateSize = self.sizingCell.preferredLayoutSizeFittingSize(targetSize)
  return adequateSize
}

这将导致集合视图根据封闭的集合视图设置单元格的宽度,然后要求调整大小的单元格计算高度。

第二部分是让尺寸调整单元使用自己的 AL 约束来计算高度。这可能比应有的更难,因为多行 UILabel 实际上需要两阶段布局过程。工作是在方法中完成的preferredLayoutSizeFittingSize,就像这样:

 /*
 Computes the size the cell will need to be to fit within targetSize.

 targetSize should be used to pass in a width.

 the returned size will have the same width, and the height which is
 calculated by Auto Layout so that the contents of the cell (i.e., text in the label)
 can fit within that width.

 */
 func preferredLayoutSizeFittingSize(targetSize:CGSize) -> CGSize {

   // save original frame and preferredMaxLayoutWidth
   let originalFrame = self.frame
   let originalPreferredMaxLayoutWidth = self.label.preferredMaxLayoutWidth

   // assert: targetSize.width has the required width of the cell

   // step1: set the cell.frame to use that width
   var frame = self.frame
   frame.size = targetSize
   self.frame = frame

   // step2: layout the cell
   self.setNeedsLayout()
   self.layoutIfNeeded()
   self.label.preferredMaxLayoutWidth = self.label.bounds.size.width

   // assert: the label's bounds and preferredMaxLayoutWidth are set to the width required by the cell's width

   // step3: compute how tall the cell needs to be

   // this causes the cell to compute the height it needs, which it does by asking the 
   // label what height it needs to wrap within its current bounds (which we just set).
   let computedSize = self.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)

   // assert: computedSize has the needed height for the cell

   // Apple: "Only consider the height for cells, because the contentView isn't anchored correctly sometimes."
   let newSize = CGSize(width:targetSize.width,height:computedSize.height)

   // restore old frame and preferredMaxLayoutWidth
   self.frame = originalFrame
   self.label.preferredMaxLayoutWidth = originalPreferredMaxLayoutWidth

   return newSize
 }

(此代码改编自 WWDC2014 会议上“高级集合视图”的示例代码中的 Apple 示例代码。)

有几点需要注意。它使用layoutIfNeeded()强制整个单元格的布局,以便计算和设置标签的宽度。但这还不够。我相信你还需要设置preferredMaxLayoutWidth这样标签将在自动布局中使用该宽度。只有这样你才能使用systemLayoutSizeFittingSize为了让单元格在考虑标签的同时计算其高度。

我喜欢这种方法吗?不!!感觉太复杂了,而且布局了两次。但只要性能不成为问题,我宁愿在运行时执行两次布局,也不愿在代码中定义两次,这似乎是唯一的其他选择。

我希望最终自调整大小的单元能够以不同的方式工作,这一切都会变得更加简单。

示例项目 https://github.com/algal/UICVVariableHeightiOS8在工作中展示它。

但为什么不直接使用自调整大小的单元格呢?

理论上,iOS8 的“自动调整单元大小”的新功能应该使这种情况变得不必要。如果您使用自动布局(AL)定义了一个单元格,那么集合视图应该足够智能,可以让它自行调整大小并正确布局。在实践中,我还没有看到任何可以使用多行标签的示例。我认为this https://github.com/algal/SelfSizingCellsDemo部分原因是自调整单元大小的机制仍然存在缺陷。

但我敢打赌这主要是因为自动布局和标签通常很棘手,即 UILabels 需要基本上两步的布局过程。我不清楚如何使用自动调整大小的单元格执行这两个步骤。

正如我所说,这确实是一项不同布局的工作。它是流布局本质的一部分,它定位具有大小的事物,而不是固定宽度并让它们选择它们的高度。

那么 PreferredLayoutAttributesFitting Attributes: 又如何呢?

The preferredLayoutAttributesFittingAttributes:我认为,这种方法是一种转移注意力的方法。这只能与新的自调整大小单元机制一起使用。因此,只要该机制不可靠,这就不是答案。

systemlayoutSizeFittingSize: 是怎么回事?

你是对的,文档令人困惑。

文档上systemLayoutSizeFittingSize: and systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority:两者都建议你应该只通过UILayoutFittingCompressedSize and UILayoutFittingExpandedSize as the targetSize。但是,方法签名本身、标头注释和函数的行为表明它们正在响应targetSize范围。

事实上,如果你设置UICollectionViewFlowLayoutDelegate.estimatedItemSize,为了启用新的自调整单元格大小机制,该值似乎作为 targetSize 传入。和UILabel.systemLayoutSizeFittingSize似乎返回与完全相同的值UILabel.sizeThatFits。这是可疑的,因为这个论点systemLayoutSizeFittingSize应该是一个粗略的目标和论据sizeThatFits:应该是最大外接尺寸。

更多资源

虽然令人遗憾的是,这样的例行要求竟然需要“研究资源”,但我认为确实如此。很好的例子和讨论是:

  • http://www.objc.io/issue-3/advanced-auto-layout-toolbox.html http://www.objc.io/issue-3/advanced-auto-layout-toolbox.html
  • http://devetc.org/code/2014/07/07/auto-layout-and-views-that-wrap.html http://devetc.org/code/2014/07/07/auto-layout-and-views-that-wrap.html
  • WWDC2014 会议 232 的代码,“具有集合视图的高级用户界面”
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用自动布局指定 UICollectionView 中单元格的一维 的相关文章

  • iOS 6.0 Quicklook QLPreviewController 错误:“无法找到已加载代理的预览项目”

    我的应用程序一直使用 QLPreviewController 来显示所有类型的文件 在 iOS 5 x 中 它似乎做得很好 现在 在 iOS 6 0 中 我收到一个错误 它显示了控制器 但带有恒定的加载指示器 并且从未实际加载任何内容 日志
  • 在 iOS 中将多种尺寸的文本垂直中心而不是基线与核心文本对齐

    是否可以通过边界中心 每个尺寸 而不是基线来对齐多个尺寸的文本 我尝试了 CTRunDelegate 功能 但它不起作用 这是可能的CTRunDelegateCallbacks getAscent and CTRunDelegateCall
  • 自定义 URL 方案

    如何处理自定义 URL 方案以允许一个应用程序定向到另一个应用程序 例如instagram user username someusername它通过用户名将用户直接引导至用户个人资料 我需要创建类似的东西 我已经结账了applicatio
  • ASIHTTPRequest 登录页面

    我尝试制作一个 iPhone 应用程序 可以登录到使用 https 保护用户信息的 Web 应用程序 现在我陷入了登录页面 我不知道通过我的应用程序登录时如何检查用户网站中的真实帐户 即使我输入错误的帐户 我也只收到 200 的回复 这是我
  • 如何在 AVCaptureVideoPreviewLayer 上添加叠加层?

    我正在使用 Swift 构建一个 iOS 应用程序 它需要二维码扫描仪功能 我已经使用 AVFoundation 实现了 QR 码扫描仪 现在我的捕获屏幕看起来与视频录制屏幕相同 即AVCaptureVideoPreviewLayer显示相
  • SwiftUI |警告:绑定首选项_尝试每帧更新多次。可能的原因?

    自从我按照偏好工作以来 PreferenceKey 我在控制台中收到此消息 Bound preference tried to update multiple times per frame 经过无数次的研究 我还没有找到任何方法可以让它安
  • MobileSafari 的正确触摸按钮行为

    MobileSafari 通常具有不正确的 HTML 按钮行为 不正确的含义 不像 iOS 原生按钮 正确的按钮行为如下 用户触摸按钮 按钮突出显示 用户将手指拖出按钮 按钮变暗 用户将手指拖回按钮 按钮突出显示 用户将手指拖出按钮并释放
  • 有没有办法检查 iOS 设备是否锁定/解锁?

    我在我的应用程序中使用了 GPS 位置更新 我想检测 iOS 设备是否处于睡眠模式 以便我可以关闭 GPS 位置更新并优化电池使用 我已经在iOS 6中尝试过pausesLocationupdates 但它无法按预期工作 我想在设备进入睡眠
  • iPhone IOS5 Storyboard,如何使用自定义的 .xib 文件加载 UIViewController?

    我有一些来自旧 iOS4 项目的 UIViewController 它们使用的是在界面生成器中创建的 xib 我的新项目是为 iOS5 构建的 使用故事板 我正在尝试将 UIViewController 添加到故事板 但让它使用我已经拥有的
  • 创建 iPhone 与外部设备连接的硬件。阿杜伊诺?

    嗨 我需要创建一个简单的连接器 我想我可能需要创建一个arduino板 它将从应用程序中获取数据 然后将它们传输到外部设备 应用程序中有一个滑块可供用户调整 调整滑块时 应用程序只会将值从应用程序发送到我需要建立的连接 该连接将连接到外部设
  • UIStackView - 拖动以重新排序排列的子视图?

    我试图获得一些关于如何实现在 a 上发现的相同类型的 拖动重新排序 行为的建议 UITableView但在一个UIStackView 到目前为止 这就是我的想法 为每个按钮添加一个长按手势识别器arrangedSubviews 长按时 添加
  • 如何使 TextField 右对齐(尾随)

    我正在努力拥有一个价值文本域以尾随对齐方式显示 正如你所看到的价值34 3以前导对齐方式显示 我确信我错过了一些明显的东西 但我不知道是什么 有任何想法吗 State private var endwert 34 3 var numberF
  • YouTube 嵌入 AirPlay UIWebView

    我正在开发一个视频应用程序 其中包含来自不同来源的大量视频 应用程序中的选项之一必须是通过电视上的 Airplay 播放视频 为了在我的应用程序中显示 YouTube 视频 我使用 YTPlayerView 它是 YouTube API 的
  • 设置视图控制器根视图的外观代理

    使用 UIAppearance 时是否可以仅针对视图控制器的根视图 我想从我的应用程序委托中为所有控制器设置背景颜色 但只想定位视图控制器上的直接视图 谢谢 详细来说 每个 UIViewController 子类都有自己的 UIView 对
  • 动态创建的标签被覆盖

    我正在开发一个应用程序 其中我在一个函数中动态添加了 5 个标签 当我回想起相同的函数时 尽管在每次创建时释放了标签 但先前创建的标签上的标签都会被覆盖 for int i 1 i lt array count i CGRect lblfr
  • 以编程方式使后退按钮转到上一个视图

    我有一个 UIBarButtonItem 并且想以编程方式设置转到前一个控制器的操作 在我的例子中 我之前的视图是 UITableViewController 下面是我当前用来制作栏按钮项目的代码 尽管该按钮尚未转到上一个视图 UIBarB
  • 动画完成后 CABasicAnimation 重置为初始值

    我正在旋转 CALayer 并尝试在动画完成后将其停止在最终位置 但动画完成后 它会重置到初始位置 xcode 文档明确指出动画不会更新属性的值 任何如何实现这一目标的建议 这就是答案 它是我的答案和克里希南的答案的结合 cabasican
  • iOS APNS:以字符串格式将设备令牌发送给提供商

    我需要通过调用在我的请求中需要 JSON 数据的服务 将 iOS 应用程序的 APNS 设备令牌发送给我的提供商 我正在阅读苹果的本地和推送通知编程指南 https developer apple com library ios docum
  • 在iOS上,setNeedsDisplay确实不会导致drawRect被调用...除非CALayer的display或drawInContext最终调用了drawRect?

    我不太明白 CALayer 是怎么做的display and drawInContext与 有关drawRect视图中 如果我有一个 NSTimer 来设置 self view setNeedsDisplay 每 1 秒 然后drawRec
  • UpdatedTransactions(transactionState == .restored) 与 paymentQueueRestoreCompletedTransactionsFinished

    正如标题所描述的 实际有什么不同 如果我有这个 func paymentQueue queue SKPaymentQueue updatedTransactions transactions SKPaymentTransaction for

随机推荐

  • package.json 中这些带有下划线前缀的属性有何用途?

    我在 package json 中发现这些属性带有下划线前缀 它们有什么用 为什么要在属性上添加下划线前缀 from email protected cdn cgi l email protection id email protected
  • D3 v4 中的 d3.rebind

    所以我在网上找到了这段代码 它计算 d3 图中节点上的 dijkstra 最短路径算法 问题是 d3 rebind 已被删除 我还没有找到修复这段代码以使其工作的方法 有什么建议么 我也知道这段代码不是我的 所以这就是为什么我在理解如何更改
  • 使用 Nunjucks 模板按整数值循环

    我对 nunjucks 很陌生 从我读到的内容来看 这是不可能的 但我想知道是否有人想出了一种方法来做到这一点 我基本上希望根据值而不是对象的大小在 nunjucks 模板中执行 for 循环 假设您将以下数据传递给模板 假设房间数值是从列
  • pythonlogging.handlers.RotatingFileHandler是否允许创建组可写日志文件?

    我正在使用标准 python 2 5 2 日志模块 特别是RotatingFileHandler 在Linux系统上 我的应用程序同时支持命令行界面和 Web 服务界面 我希望两者都写入同一个日志文件 但是 当日志文件轮换时 新文件已644
  • PropertyUtils.copyProperties 什么时候可以默默地失败?

    我在用着PropertyUtils copyProperties http commons apache org beanutils api org apache commons beanutils PropertyUtils html c
  • Pydub from_mp3 给出 [Errno 2] 没有这样的文件或目录

    我发现自己站在墙前 只是尝试将音频文件加载到 pydub 中进行转换 但不断抛出 Errno 2 没有这样的文件或目录 错误 当然 我花了太多时间来确保路径有效 尝试了相对路径和绝对路径 并确认 python 方法 open 可以在完全相同
  • CSS-hack - 在网站正文中添加 css

    我陷入了只能访问网站正文而不能访问网站头部的情况 我必须使用新的样式表 现在我遇到了在网站正文中添加 CSS 文件的解决方案 当然 这是一个黑客 所以我想知道是否有更好的解决方案 我们有不同的方法来加载 CSS 文件 1 HTML 在页面上
  • iPhone VS ipad开发流程(异同)

    我是 iOS 开发新手 我正在努力弄清楚这些事情 iPhone 和 iPad 上的项目 两者有哪些相同之处 编码 图形或 UI 应用程序的设计模式相同还是不同 等待答复 谢谢 就我个人而言 我发现 iPhone 和 iPad 之间唯一真正的
  • 如何在oracle 11g SQL中计算两个日期之间的差异

    当我尝试使用 datediff 函数计算日期差时 它显示该无效标识符 SELECT DATEDIFF day 2008 08 05 2008 06 05 AS DiffDate from da static trade Error inva
  • 如何使用后期绑定访问 Microsoft Word 现有实例

    我正在用 C 开发一些代码 我将在其中与 Microsoft Word 进行交互 我希望能够选择重新使用现有实例或作为创建新实例的替代方案 请记住 我想使用 LATE BINDING 来完成所有这些 可以肯定地说 我已经弄清楚如何在创建新实
  • 我可以像在 C++ 中那样在 R 中拥有多个独立的随机数生成器吗?

    我有许多独立的随机过程 比如到达过程 需要我生成随机数 我想对每个进程使用通用随机数 以便在控制这些策略时比较不同策略的执行情况 我希望进程 A 由生成器 A 控制 使用种子 A 我希望进程 B 由生成器 B 控制 使用种子 B 等等 这可
  • ASP.NET Core 中的 HttpRequest.Path 和 HttpRequest.PathBase 有什么区别?

    详细信息如下 https learn microsoft com en us dotnet api microsoft aspnetcore http httprequest view aspnetcore 3 0 https learn
  • 如何检测IIS Express版本?

    我发现我们的 ASP NET 应用程序在 IIS Express 中的不同计算机上运行方式不同 全部都有 VS 2012 Net 4 5 和集成模式 但有些有 VS 2012 Update 1 有些则没有 如何找到 IIS Express
  • 查找一个字段大于另一字段的行

    我的 SQL 有点生疏了 我无法找到一种方法来检索一个值大于另一个值的行 例如 我有以下行 ROWID 1 CreatedAt 2013 08 03 10 10 23 344 UpdatedAt 2013 08 03 11 10 23 34
  • 对 ArangoDB 文档集合进行重复数据删除

    我确信有一种简单快捷的方法可以做到这一点 但它却让我无法理解 我有一个包含一些重复记录的大型数据集 我想删除重复项 重复项由一个属性唯一标识 但文档的其余部分也应该相同 我尝试以几种不同的方式创建一个仅具有唯一值的新集合 但它们都非常慢 例
  • 自动接受安装 NPX 包 [重复]

    这个问题在这里已经有答案了 运行 NPM 包时npx第一次会出现提示询问是否要下载包 例如 如果您运行命令npx some npm package 您会收到以下提示 Need to install the following package
  • 在 Chrome 扩展内容脚本中共享内存中对象?

    我对 JavaScript 和 Chrome 开发都很陌生 正在尝试创建一个在某些网页中注入内容 CSS 的扩展 很简单 但问题是这样做需要查看本地存储中的大量数据 根据我到目前为止所读到的内容 正确的方法是 读取所需数据 JSON 序列化
  • 无法使用 Rust 在 VSCode-LLDB 中创建条件断点

    我正在使用 LLDB 在 VS Code 中调试 Rust 程序 The 表达式文档 https github com vadimcn vscode lldb blob master MANUAL md expressions说有一个程序变
  • 联合类型上不存在 Typescript 属性

    这是我遇到过几次的情况 看起来应该相当简单 但我找不到不将类型设置为任何的解决方案 函数采用两个不同对象之一作为参数 检查已接收到哪个对象 并返回相应的字段 这是问题的简化版本 但问题是这两个对象只能通过它们的属性 没有重叠 来区分 并且我
  • 使用自动布局指定 UICollectionView 中单元格的一维

    在 iOS 8 中UICollectionViewFlowLayout支持根据单元格自身的内容大小自动调整单元格大小 这会根据单元格的内容调整单元格的宽度和高度 是否可以为所有单元格的宽度 或高度 指定固定值并允许调整其他尺寸的大小 举一个