diff --git a/PocketKit/Sources/PocketKit/Collections/CollectionViewController.swift b/PocketKit/Sources/PocketKit/Collections/CollectionViewController.swift index a7c43a132..320473631 100644 --- a/PocketKit/Sources/PocketKit/Collections/CollectionViewController.swift +++ b/PocketKit/Sources/PocketKit/Collections/CollectionViewController.swift @@ -309,7 +309,7 @@ private extension CollectionViewController { return cell case .error: let cell: EmptyStateCollectionViewCell = collectionView.dequeueCell(for: indexPath) - cell.configure(parent: self, model.errorEmptyState) + cell.configure(viewModel: model.errorEmptyState) return cell } } diff --git a/PocketKit/Sources/PocketKit/Home/HomeViewController.swift b/PocketKit/Sources/PocketKit/Home/HomeViewController.swift index 686a7e6c6..75d896afd 100644 --- a/PocketKit/Sources/PocketKit/Home/HomeViewController.swift +++ b/PocketKit/Sources/PocketKit/Home/HomeViewController.swift @@ -19,7 +19,7 @@ struct HomeViewControllerSwiftUI: UIViewControllerRepresentable { func makeUIViewController(context: UIViewControllerRepresentableContext) -> UINavigationController { let homeViewController = HomeViewController(model: model) - homeViewController.updateHeroCardCount() + homeViewController.updateLayout() let navigationController = UINavigationController(rootViewController: homeViewController) navigationController.navigationBar.prefersLargeTitles = true navigationController.navigationBar.barTintColor = UIColor(.ui.white1) @@ -154,6 +154,7 @@ class HomeViewController: UIViewController { return } dataSource.apply(snapshot) + Log.breadcrumb(category: "home", level: .debug, message: "➡️ Applying snapshot - #sections: \(snapshot.numberOfSections), #items: \(snapshot.numberOfItems)") }.store(in: &subscriptions) } @@ -216,14 +217,14 @@ class HomeViewController: UIViewController { } override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - updateHeroCardCount() + updateLayout() } - func updateHeroCardCount() { + fileprivate func updateLayout() { if traitCollection.shouldUseWideLayout() { - self.model.numberOfHeroItems = 2 + model.useWideLayout() } else { - self.model.numberOfHeroItems = 1 + model.useCompactLayout() } } } diff --git a/PocketKit/Sources/PocketKit/Home/HomeViewModel.swift b/PocketKit/Sources/PocketKit/Home/HomeViewModel.swift index 06e7001de..b2f706eff 100644 --- a/PocketKit/Sources/PocketKit/Home/HomeViewModel.swift +++ b/PocketKit/Sources/PocketKit/Home/HomeViewModel.swift @@ -124,11 +124,7 @@ class HomeViewModel: NSObject { @Published var tappedSeeAll: SeeAll? - var numberOfHeroItems: Int = 1 { - didSet { - self.snapshot = buildSnapshot() - } - } + private var numberOfHeroItems: Int = 1 private let source: Source let tracker: Tracker @@ -311,6 +307,27 @@ extension HomeViewModel { } return snapshot } + + /// Updates the collection view layout for compact or wide layout, if this changed. + /// Wide layout has two columns and two hero items per recommendation section. + /// - Parameter heroItems: the number of hero items to use. + private func updateLayout(_ heroItems: Int) { + guard heroItems != numberOfHeroItems else { + return + } + numberOfHeroItems = heroItems + snapshot = buildSnapshot() + } + + /// Updates the layout to wide, if the previous layout was compact. + func useWideLayout() { + updateLayout(2) + } + + /// Updates the layout to compact, if the previpus layout was wide. + func useCompactLayout() { + updateLayout(1) + } } // MARK: - Cell Selection @@ -618,10 +635,11 @@ extension HomeViewModel { // MARK: - Loading Section extension HomeViewModel { - static func loadingSnapshot() -> Snapshot { + private static func loadingSnapshot() -> Snapshot { var snapshot = Snapshot() snapshot.appendSections([.loading]) snapshot.appendItems([.loading], toSection: .loading) + Log.breadcrumb(category: "home", level: .debug, message: "➡️ Sending loading snapshot.") return snapshot } } diff --git a/PocketKit/Sources/PocketKit/MyList/EmptyStates/EmptyStateView.swift b/PocketKit/Sources/PocketKit/MyList/EmptyStates/EmptyStateView.swift index aa3d1b0a4..f6c8298d6 100644 --- a/PocketKit/Sources/PocketKit/MyList/EmptyStates/EmptyStateView.swift +++ b/PocketKit/Sources/PocketKit/MyList/EmptyStates/EmptyStateView.swift @@ -6,37 +6,33 @@ import SwiftUI import Textile open class SwiftUICollectionViewCell: UICollectionViewCell where T: View { - private(set) var hosting: UIHostingController? - - func embed(in parent: UIViewController, withView content: T) { - if let hosting = self.hosting { - hosting.rootView = content - hosting.view.layoutIfNeeded() - } else { - let hosting = UIHostingController(rootView: content) - parent.addChild(hosting) - hosting.didMove(toParent: parent) - self.contentView.addSubview(hosting.view) - self.hosting = hosting - } - } - - deinit { - Task { @MainActor in - hosting?.willMove(toParent: nil) - hosting?.view.removeFromSuperview() - hosting?.removeFromParent() - hosting = nil - } + /// Conveets a `SwiftUI View` in a `UIKit UIView` + /// - Parameters: + /// - content: the `SwiftUI View` + /// - Returns: the `UIKit View` that embeds the original `SwiftUI View` + func uiView(from content: T) -> UIView { + let controller = UIHostingController(rootView: content) + controller.view.translatesAutoresizingMaskIntoConstraints = false + controller.view.backgroundColor = .clear + return controller.view } } class EmptyStateCollectionViewCell: SwiftUICollectionViewCell> { - func configure(parent: UIViewController, _ viewModel: EmptyStateViewModel) { - embed(in: parent, withView: EmptyStateView(viewModel: viewModel)) - hosting?.view.frame = self.contentView.bounds - hosting?.view.backgroundColor = .clear - hosting?.view.accessibilityIdentifier = viewModel.accessibilityIdentifier + func configure(viewModel: EmptyStateViewModel) { + let view = uiView(from: EmptyStateView(viewModel: viewModel)) + contentView.addSubview(view) + contentView.pinSubviewToAllEdges(view) + view.accessibilityIdentifier = viewModel.accessibilityIdentifier + } + + override func prepareForReuse() { + // default implementation does nothing, adding it here just in case it changes in the future + super.prepareForReuse() + // clear up any existing content from the view before adding one + contentView.subviews.forEach { + $0.removeFromSuperview() + } } } diff --git a/PocketKit/Sources/PocketKit/MyList/ItemsList/UIKit/ItemsListViewController.swift b/PocketKit/Sources/PocketKit/MyList/ItemsList/UIKit/ItemsListViewController.swift index 62ff47f79..b317c37b1 100644 --- a/PocketKit/Sources/PocketKit/MyList/ItemsList/UIKit/ItemsListViewController.swift +++ b/PocketKit/Sources/PocketKit/MyList/ItemsList/UIKit/ItemsListViewController.swift @@ -379,7 +379,7 @@ class ItemsListViewController: UIViewController, guard let viewModel = model.emptyState else { return } - cell.configure(parent: self, viewModel) + cell.configure(viewModel: viewModel) } private func handle(savesEvent event: ItemsListEvent) {