From 6d37ac73960b45dfad6ef1e75631f063e20f77ea Mon Sep 17 00:00:00 2001 From: Dylan Nagler Date: Mon, 19 Aug 2024 11:26:59 -0400 Subject: [PATCH] Breaking content: UIView retain cycle in UIViewLazyList's LazyListContainerCell (#2250) The content view is a Swift object, so it needs to be manually nil'd out by its Kotlin retainer --- CHANGELOG.md | 2 +- .../lazylayout/uiview/UIViewLazyList.kt | 29 ++++++++++--------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08b7554027..4ef9ace396 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ Changed: - Change `UiConfiguration.viewportSize` to be nullable. A null `viewportSize` indicates the viewport's size has not been resolved yet. Fixed: -- Nothing yet! +- Breaking `content: UIView` retain cycle in `UIViewLazyList`'s `LazyListContainerCell`. ## [0.13.0] - 2024-07-25 diff --git a/redwood-lazylayout-uiview/src/commonMain/kotlin/app/cash/redwood/lazylayout/uiview/UIViewLazyList.kt b/redwood-lazylayout-uiview/src/commonMain/kotlin/app/cash/redwood/lazylayout/uiview/UIViewLazyList.kt index 202b432cd1..29183c73a4 100644 --- a/redwood-lazylayout-uiview/src/commonMain/kotlin/app/cash/redwood/lazylayout/uiview/UIViewLazyList.kt +++ b/redwood-lazylayout-uiview/src/commonMain/kotlin/app/cash/redwood/lazylayout/uiview/UIViewLazyList.kt @@ -119,7 +119,7 @@ internal open class UIViewLazyList : } override fun setContent(view: LazyListContainerCell, content: UIView?, modifier: Modifier) { - view.content = content + view.setContent(content) } } @@ -269,16 +269,6 @@ internal class LazyListContainerCell( reuseIdentifier: String?, ) : UITableViewCell(style, reuseIdentifier) { internal var binding: Binding? = null - internal var content: UIView? = null - set(value) { - field = value - - removeAllSubviews() - if (value != null) { - contentView.addSubview(value) - } - setNeedsLayout() - } override fun initWithStyle( style: UITableViewCellStyle, @@ -320,13 +310,26 @@ internal class LazyListContainerCell( override fun layoutSubviews() { super.layoutSubviews() - val content = this.content ?: return + if (contentView.subviews.isEmpty()) return + + val content = contentView.subviews.first() as UIView ?: return content.setFrame(bounds) contentView.setFrame(bounds) } override fun sizeThatFits(size: CValue): CValue { - return content?.sizeThatFits(size) ?: return super.sizeThatFits(size) + if (contentView.subviews.isEmpty()) return super.sizeThatFits(size) + + val content = contentView.subviews.first() as UIView ?: return super.sizeThatFits(size) + return content.sizeThatFits(size) + } + + internal fun setContent(content: UIView?) { + removeAllSubviews() + if (content != null) { + contentView.addSubview(content) + } + setNeedsLayout() } private fun removeAllSubviews() {