Skip to content

Commit

Permalink
Clean up early rendering flag (#155)
Browse files Browse the repository at this point in the history
  • Loading branch information
brynbodayle authored Oct 17, 2023
1 parent bb59fcc commit eeb182b
Show file tree
Hide file tree
Showing 6 changed files with 8 additions and 68 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added `forcesEarlySwiftUIRendering` flag to `CollectionViewConfiguration` to test a SwiftUI layout
approach to resolve an issue that could cause collection view cells to layout with
unexpected dimensions
- Made new layout-based SwiftUI cell rendering option the default.

## [0.10.0](https://github.com/airbnb/epoxy-ios/compare/0.9.0...0.10.0) - 2023-06-29

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,6 @@ open class CollectionView: UICollectionView {
}

cell.itemPath = itemPath
cell.forcesEarlySwiftUIRendering = configuration.forcesEarlySwiftUIRendering

let metadata = ItemCellMetadata(
traitCollection: traitCollection,
Expand All @@ -470,7 +469,6 @@ open class CollectionView: UICollectionView {
animated: Bool)
{
supplementaryView.itemPath = itemPath
supplementaryView.forcesEarlySwiftUIRendering = configuration.forcesEarlySwiftUIRendering
model.configure(
reusableView: supplementaryView,
traitCollection: traitCollection,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,11 @@ public struct CollectionViewConfiguration {
public init(
usesBatchUpdatesForAllReloads: Bool = true,
usesCellPrefetching: Bool = true,
usesAccurateScrollToItem: Bool = true,
forcesEarlySwiftUIRendering: Bool = true)
usesAccurateScrollToItem: Bool = true)
{
self.usesBatchUpdatesForAllReloads = usesBatchUpdatesForAllReloads
self.usesCellPrefetching = usesCellPrefetching
self.usesAccurateScrollToItem = usesAccurateScrollToItem
self.forcesEarlySwiftUIRendering = forcesEarlySwiftUIRendering
}

// MARK: Public
Expand Down Expand Up @@ -68,10 +66,4 @@ public struct CollectionViewConfiguration {
///
/// - SeeAlso: `CollectionViewScrollToItemHelper`
public var usesAccurateScrollToItem: Bool

/// Flag to use a semi-private API to force an early render of a SwiftUI view embedded in a `UIHostingController`. This is used
/// to synchronously resize after updating the `rootView`. When disabled, layout is forced using standard UIKit functions, a newer
/// approach which is being tested for viability and to resolve issues where SwiftUI views in collection view cells undergo a layout pass
/// with unexpected dimensions.
public var forcesEarlySwiftUIRendering: Bool
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import UIKit
// MARK: - CollectionViewCell

/// An internal cell class for use in a `CollectionView`.
public final class CollectionViewCell: UICollectionViewCell, ItemCellView, SwiftUIRenderingConfigurable {
public final class CollectionViewCell: UICollectionViewCell, ItemCellView {

// MARK: Lifecycle

Expand All @@ -27,13 +27,6 @@ public final class CollectionViewCell: UICollectionViewCell, ItemCellView, Swift

public var selectedBackgroundColor: UIColor?

/// See `CollectionViewConfiguration.forcesEarlySwiftUIRendering` for an explanation of this behavior.
public var forcesEarlySwiftUIRendering = true {
didSet {
updateForcesEarlySwiftUIRendering()
}
}

override public var isSelected: Bool {
didSet {
updateVisualHighlightState(isSelected)
Expand Down Expand Up @@ -66,8 +59,6 @@ public final class CollectionViewCell: UICollectionViewCell, ItemCellView, Swift
view.topAnchor.constraint(equalTo: contentView.topAnchor),
view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
])

updateForcesEarlySwiftUIRendering()
}

override public func preferredLayoutAttributesFitting(
Expand Down Expand Up @@ -161,12 +152,6 @@ public final class CollectionViewCell: UICollectionViewCell, ItemCellView, Swift
}
}

private func updateForcesEarlySwiftUIRendering() {
if let configurableView = view as? SwiftUIRenderingConfigurable {
configurableView.forcesEarlySwiftUIRendering = forcesEarlySwiftUIRendering
}
}

}

// MARK: EphemeralCachedStateView
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import EpoxyCore
import UIKit

/// An internal collection reusable view class for use in a `CollectionView`.
public final class CollectionViewReusableView: UICollectionReusableView, SwiftUIRenderingConfigurable {
public final class CollectionViewReusableView: UICollectionReusableView {

// MARK: Lifecycle

Expand All @@ -23,13 +23,6 @@ public final class CollectionViewReusableView: UICollectionReusableView, SwiftUI

public private(set) var view: UIView?

/// See `CollectionViewConfiguration.forcesEarlySwiftUIRendering` for an explanation of this behavior.
public var forcesEarlySwiftUIRendering = false {
didSet {
updateForcesEarlySwiftUIRendering()
}
}

/// Pass a view for this view's element kind and reuseID that the view will pin to its edges.
public func setViewIfNeeded(view: UIView) {
if self.view != nil {
Expand All @@ -47,8 +40,6 @@ public final class CollectionViewReusableView: UICollectionReusableView, SwiftUI
view.bottomAnchor.constraint(equalTo: bottomAnchor),
])
self.view = view

updateForcesEarlySwiftUIRendering()
}

override public func preferredLayoutAttributesFitting(
Expand Down Expand Up @@ -86,12 +77,4 @@ public final class CollectionViewReusableView: UICollectionReusableView, SwiftUI
/// post-update data.
var itemPath: SupplementaryItemPath?

// MARK: Private

private func updateForcesEarlySwiftUIRendering() {
if let configurableView = view as? SwiftUIRenderingConfigurable {
configurableView.forcesEarlySwiftUIRendering = forcesEarlySwiftUIRendering
}
}

}
27 changes: 4 additions & 23 deletions Sources/EpoxyCore/SwiftUI/EpoxySwiftUIHostingView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ extension CallbackContextEpoxyModeled
/// the API is private and 3) the `_UIHostingView` doesn't not accept setting a new `View` instance.
///
/// - SeeAlso: `EpoxySwiftUIHostingController`
public final class EpoxySwiftUIHostingView<RootView: View>: UIView, EpoxyableView, SwiftUIRenderingConfigurable {
public final class EpoxySwiftUIHostingView<RootView: View>: UIView, EpoxyableView {

// MARK: Lifecycle

Expand Down Expand Up @@ -130,9 +130,6 @@ public final class EpoxySwiftUIHostingView<RootView: View>: UIView, EpoxyableVie
}
}

/// See `CollectionViewConfiguration.forcesEarlySwiftUIRendering` for an explanation of this behavior.
public var forcesEarlySwiftUIRendering = true

public override func didMoveToWindow() {
super.didMoveToWindow()

Expand Down Expand Up @@ -186,18 +183,9 @@ public final class EpoxySwiftUIHostingView<RootView: View>: UIView, EpoxyableVie
// The view controller must be added to the view controller hierarchy to measure its content.
addViewControllerIfNeededAndReady()

if forcesEarlySwiftUIRendering {
// As of iOS 15.2, `UIHostingController` now renders updated content asynchronously, and as such
// this view will get sized incorrectly with the previous content when reused unless we invoke
// this semi-private API. We couldn't find any other method to get the view to resize
// synchronously after updating `rootView`, but hopefully this will become a public API soon so
// we can remove this call.
viewController._render(seconds: 0)
} else {
// We need to layout the view to ensure it gets resized properly when cells are re-used
viewController.view.setNeedsLayout()
viewController.view.layoutIfNeeded()
}
// We need to layout the view to ensure it gets resized properly when cells are re-used
viewController.view.setNeedsLayout()
viewController.view.layoutIfNeeded()

// This is required to ensure that views with new content are properly resized.
viewController.view.invalidateIntrinsicContentSize()
Expand Down Expand Up @@ -437,10 +425,3 @@ struct EpoxyHostingWrapper<Content: View>: View {
}

#endif

// MARK: - SwiftUIRenderingConfigurable

public protocol SwiftUIRenderingConfigurable: AnyObject {
/// See `CollectionViewConfiguration.forcesEarlySwiftUIRendering` for an explanation of this behavior.
var forcesEarlySwiftUIRendering: Bool { get set }
}

0 comments on commit eeb182b

Please sign in to comment.