diff --git a/EssentialApp/EssentialAppTests/FeedAcceptanceTests.swift b/EssentialApp/EssentialAppTests/FeedAcceptanceTests.swift index 691d7a2..1b16026 100644 --- a/EssentialApp/EssentialAppTests/FeedAcceptanceTests.swift +++ b/EssentialApp/EssentialAppTests/FeedAcceptanceTests.swift @@ -60,7 +60,7 @@ class FeedAcceptanceTests: XCTestCase { store: InMemoryFeedStore = .empty ) -> ListViewController { let sut = SceneDelegate(httpClient: httpClient, store: store) - sut.window = UIWindow() + sut.window = UIWindow(frame: CGRect(x: 0, y: 0, width: 390, height: 1)) sut.configureWindow() let nav = sut.window?.rootViewController as? UINavigationController diff --git a/EssentialFeed/EssentialFeediOS/Feed UI/Controllers/FeedImageCellController.swift b/EssentialFeed/EssentialFeediOS/Feed UI/Controllers/FeedImageCellController.swift index c206649..0be718a 100644 --- a/EssentialFeed/EssentialFeediOS/Feed UI/Controllers/FeedImageCellController.swift +++ b/EssentialFeed/EssentialFeediOS/Feed UI/Controllers/FeedImageCellController.swift @@ -29,9 +29,6 @@ extension FeedImageCellController: UITableViewDataSource, UITableViewDelegate, U public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { cell = tableView.dequeueReusableCell() - cell?.onReuse = { [weak self] in - self?.releaseCellForReuse() - } cell?.locationContainer.isHidden = !viewModel.hasLocation cell?.locationLabel.text = viewModel.location cell?.descriptionLabel.text = viewModel.description @@ -39,6 +36,9 @@ extension FeedImageCellController: UITableViewDataSource, UITableViewDelegate, U cell?.onRetry = { [weak self] in self?.delegate.didRequestImage() } + cell?.onReuse = { [weak self] in + self?.releaseCellForReuse() + } delegate.didRequestImage() return cell! } diff --git a/EssentialFeed/EssentialFeediOS/Shared UI/Controllers/ListViewController.swift b/EssentialFeed/EssentialFeediOS/Shared UI/Controllers/ListViewController.swift index 274594e..b654e8f 100644 --- a/EssentialFeed/EssentialFeediOS/Shared UI/Controllers/ListViewController.swift +++ b/EssentialFeed/EssentialFeediOS/Shared UI/Controllers/ListViewController.swift @@ -23,6 +23,7 @@ public final class ListViewController: UITableViewController, UITableViewDataSou super.viewDidLoad() configureTableView() + configureTraitCollectionObservers() // Note: Using `onViewIsAppearing` to defer `beginRefreshing()` until the view is fully visible. // This ensures the spinner appears correctly, addressing a change in behavior introduced in iOS 17. @@ -44,9 +45,11 @@ public final class ListViewController: UITableViewController, UITableViewDataSou } } - public override func traitCollectionDidChange(_ previous: UITraitCollection?) { - if previous?.preferredContentSizeCategory != traitCollection.preferredContentSizeCategory { - tableView.reloadData() + private func configureTraitCollectionObservers() { + registerForTraitChanges( + [UITraitPreferredContentSizeCategory.self] + ) { (self: Self, previous: UITraitCollection) in + self.tableView.reloadData() } } diff --git a/EssentialFeed/EssentialFeediOS/Shared UI/Views/ErrorView.swift b/EssentialFeed/EssentialFeediOS/Shared UI/Views/ErrorView.swift index 36eeda6..3d23712 100644 --- a/EssentialFeed/EssentialFeediOS/Shared UI/Views/ErrorView.swift +++ b/EssentialFeed/EssentialFeediOS/Shared UI/Views/ErrorView.swift @@ -26,10 +26,10 @@ public final class ErrorView: UIButton { let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.alignment = NSTextAlignment.center - var attributes = AttributeContainer() - attributes.paragraphStyle = paragraphStyle - attributes.font = UIFont.preferredFont(forTextStyle: .body) - return attributes + return AttributeContainer([ + .paragraphStyle: paragraphStyle, + .font: UIFont.preferredFont(forTextStyle: .body) + ]) } private func configure() { diff --git a/EssentialFeed/EssentialFeediOSTests/Feed UI/FeedSnapshotTests.swift b/EssentialFeed/EssentialFeediOSTests/Feed UI/FeedSnapshotTests.swift index abb5ede..bdd2221 100644 --- a/EssentialFeed/EssentialFeediOSTests/Feed UI/FeedSnapshotTests.swift +++ b/EssentialFeed/EssentialFeediOSTests/Feed UI/FeedSnapshotTests.swift @@ -14,9 +14,9 @@ class FeedSnapshotTests: XCTestCase { sut.display(feedWithContent()) - assert(snapshot: sut.snapshot(for: .iPhone8(style: .light)), named: "FEED_WITH_CONTENT_light") - assert(snapshot: sut.snapshot(for: .iPhone8(style: .dark)), named: "FEED_WITH_CONTENT_dark") - assert(snapshot: sut.snapshot(for: .iPhone8(style: .light, contentSize: .extraExtraExtraLarge)), named: "FEED_WITH_CONTENT_light_extraExtraExtraLarge") + assert(snapshot: sut.snapshot(for: .iPhone(style: .light)), named: "FEED_WITH_CONTENT_light") + assert(snapshot: sut.snapshot(for: .iPhone(style: .dark)), named: "FEED_WITH_CONTENT_dark") + assert(snapshot: sut.snapshot(for: .iPhone(style: .light, contentSize: .extraExtraExtraLarge)), named: "FEED_WITH_CONTENT_light_extraExtraExtraLarge") } func test_feedWithFailedImageLoading() { @@ -24,8 +24,8 @@ class FeedSnapshotTests: XCTestCase { sut.display(feedWithFailedImageLoading()) - assert(snapshot: sut.snapshot(for: .iPhone8(style: .light)), named: "FEED_WITH_FAILED_IMAGE_LOADING_light") - assert(snapshot: sut.snapshot(for: .iPhone8(style: .dark)), named: "FEED_WITH_FAILED_IMAGE_LOADING_dark") + assert(snapshot: sut.snapshot(for: .iPhone(style: .light)), named: "FEED_WITH_FAILED_IMAGE_LOADING_light") + assert(snapshot: sut.snapshot(for: .iPhone(style: .dark)), named: "FEED_WITH_FAILED_IMAGE_LOADING_dark") } // MARK: - Helpers diff --git a/EssentialFeed/EssentialFeediOSTests/Feed UI/snapshots/FEED_WITH_CONTENT_dark.png b/EssentialFeed/EssentialFeediOSTests/Feed UI/snapshots/FEED_WITH_CONTENT_dark.png index 89694b6..1afb813 100644 Binary files a/EssentialFeed/EssentialFeediOSTests/Feed UI/snapshots/FEED_WITH_CONTENT_dark.png and b/EssentialFeed/EssentialFeediOSTests/Feed UI/snapshots/FEED_WITH_CONTENT_dark.png differ diff --git a/EssentialFeed/EssentialFeediOSTests/Feed UI/snapshots/FEED_WITH_CONTENT_light.png b/EssentialFeed/EssentialFeediOSTests/Feed UI/snapshots/FEED_WITH_CONTENT_light.png index b5c86fd..139abc4 100644 Binary files a/EssentialFeed/EssentialFeediOSTests/Feed UI/snapshots/FEED_WITH_CONTENT_light.png and b/EssentialFeed/EssentialFeediOSTests/Feed UI/snapshots/FEED_WITH_CONTENT_light.png differ diff --git a/EssentialFeed/EssentialFeediOSTests/Feed UI/snapshots/FEED_WITH_CONTENT_light_extraExtraExtraLarge.png b/EssentialFeed/EssentialFeediOSTests/Feed UI/snapshots/FEED_WITH_CONTENT_light_extraExtraExtraLarge.png index 2ca74ee..9718b25 100644 Binary files a/EssentialFeed/EssentialFeediOSTests/Feed UI/snapshots/FEED_WITH_CONTENT_light_extraExtraExtraLarge.png and b/EssentialFeed/EssentialFeediOSTests/Feed UI/snapshots/FEED_WITH_CONTENT_light_extraExtraExtraLarge.png differ diff --git a/EssentialFeed/EssentialFeediOSTests/Feed UI/snapshots/FEED_WITH_FAILED_IMAGE_LOADING_dark.png b/EssentialFeed/EssentialFeediOSTests/Feed UI/snapshots/FEED_WITH_FAILED_IMAGE_LOADING_dark.png index fe0f66e..3e1606e 100644 Binary files a/EssentialFeed/EssentialFeediOSTests/Feed UI/snapshots/FEED_WITH_FAILED_IMAGE_LOADING_dark.png and b/EssentialFeed/EssentialFeediOSTests/Feed UI/snapshots/FEED_WITH_FAILED_IMAGE_LOADING_dark.png differ diff --git a/EssentialFeed/EssentialFeediOSTests/Feed UI/snapshots/FEED_WITH_FAILED_IMAGE_LOADING_light.png b/EssentialFeed/EssentialFeediOSTests/Feed UI/snapshots/FEED_WITH_FAILED_IMAGE_LOADING_light.png index d523eb4..f1db79a 100644 Binary files a/EssentialFeed/EssentialFeediOSTests/Feed UI/snapshots/FEED_WITH_FAILED_IMAGE_LOADING_light.png and b/EssentialFeed/EssentialFeediOSTests/Feed UI/snapshots/FEED_WITH_FAILED_IMAGE_LOADING_light.png differ diff --git a/EssentialFeed/EssentialFeediOSTests/Helpers/UIViewController+Snapshot.swift b/EssentialFeed/EssentialFeediOSTests/Helpers/UIViewController+Snapshot.swift index 6af02e9..443512a 100644 --- a/EssentialFeed/EssentialFeediOSTests/Helpers/UIViewController+Snapshot.swift +++ b/EssentialFeed/EssentialFeediOSTests/Helpers/UIViewController+Snapshot.swift @@ -17,28 +17,29 @@ struct SnapshotConfiguration { let layoutMargins: UIEdgeInsets let traitCollection: UITraitCollection - static func iPhone8(style: UIUserInterfaceStyle, contentSize: UIContentSizeCategory = .medium) -> SnapshotConfiguration { + static func iPhone(style: UIUserInterfaceStyle, contentSize: UIContentSizeCategory = .medium) -> SnapshotConfiguration { return SnapshotConfiguration( - size: CGSize(width: 375, height: 667), - safeAreaInsets: UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0), - layoutMargins: UIEdgeInsets(top: 20, left: 16, bottom: 0, right: 16), - traitCollection: UITraitCollection { - $0.forceTouchCapability = .available - $0.layoutDirection = .leftToRight - $0.preferredContentSizeCategory = contentSize - $0.userInterfaceIdiom = .phone - $0.horizontalSizeClass = .compact - $0.verticalSizeClass = .regular - $0.displayScale = 2 - $0.displayGamut = .P3 - $0.userInterfaceStyle = style - } + size: CGSize(width: 390, height: 844), + safeAreaInsets: UIEdgeInsets(top: 47, left: 0, bottom: 34, right: 0), + layoutMargins: UIEdgeInsets(top: 55, left: 8, bottom: 42, right: 8), + traitCollection: UITraitCollection(mutations: { traits in + traits.forceTouchCapability = .unavailable + traits.layoutDirection = .leftToRight + traits.preferredContentSizeCategory = contentSize + traits.userInterfaceIdiom = .phone + traits.horizontalSizeClass = .compact + traits.verticalSizeClass = .regular + traits.displayScale = 3 + traits.accessibilityContrast = .normal + traits.displayGamut = .P3 + traits.userInterfaceStyle = style + }) ) } } private final class SnapshotWindow: UIWindow { - private var configuration: SnapshotConfiguration = .iPhone8(style: .light) + private var configuration: SnapshotConfiguration = .iPhone(style: .light) convenience init(configuration: SnapshotConfiguration, root: UIViewController) { self.init(frame: CGRect(origin: .zero, size: configuration.size)) @@ -50,21 +51,11 @@ private final class SnapshotWindow: UIWindow { } override var safeAreaInsets: UIEdgeInsets { - return configuration.safeAreaInsets + configuration.safeAreaInsets } override var traitCollection: UITraitCollection { - return super.traitCollection.modifyingTraits { traits in - traits.forceTouchCapability = configuration.traitCollection.forceTouchCapability - traits.layoutDirection = configuration.traitCollection.layoutDirection - traits.preferredContentSizeCategory = configuration.traitCollection.preferredContentSizeCategory - traits.userInterfaceIdiom = configuration.traitCollection.userInterfaceIdiom - traits.horizontalSizeClass = configuration.traitCollection.horizontalSizeClass - traits.verticalSizeClass = configuration.traitCollection.verticalSizeClass - traits.displayScale = configuration.traitCollection.displayScale - traits.displayGamut = configuration.traitCollection.displayGamut - traits.userInterfaceStyle = configuration.traitCollection.userInterfaceStyle - } + configuration.traitCollection } func snapshot() -> UIImage { diff --git a/EssentialFeed/EssentialFeediOSTests/Image Comments UI/ImageCommentsSnapshotTests.swift b/EssentialFeed/EssentialFeediOSTests/Image Comments UI/ImageCommentsSnapshotTests.swift index 2fce172..0505827 100644 --- a/EssentialFeed/EssentialFeediOSTests/Image Comments UI/ImageCommentsSnapshotTests.swift +++ b/EssentialFeed/EssentialFeediOSTests/Image Comments UI/ImageCommentsSnapshotTests.swift @@ -14,9 +14,9 @@ class ImageCommentsSnapshotTests: XCTestCase { sut.display(comments()) - assert(snapshot: sut.snapshot(for: .iPhone8(style: .light)), named: "IMAGE_COMMENTS_light") - assert(snapshot: sut.snapshot(for: .iPhone8(style: .dark)), named: "IMAGE_COMMENTS_dark") - assert(snapshot: sut.snapshot(for: .iPhone8(style: .light, contentSize: .extraExtraExtraLarge)), named: "IMAGE_COMMENTS_light_extraExtraExtraLarge") + assert(snapshot: sut.snapshot(for: .iPhone(style: .light)), named: "IMAGE_COMMENTS_light") + assert(snapshot: sut.snapshot(for: .iPhone(style: .dark)), named: "IMAGE_COMMENTS_dark") + assert(snapshot: sut.snapshot(for: .iPhone(style: .light, contentSize: .extraExtraExtraLarge)), named: "IMAGE_COMMENTS_light_extraExtraExtraLarge") } // MARK: - Helpers diff --git a/EssentialFeed/EssentialFeediOSTests/Image Comments UI/snapshots/IMAGE_COMMENTS_dark.png b/EssentialFeed/EssentialFeediOSTests/Image Comments UI/snapshots/IMAGE_COMMENTS_dark.png index 37353bc..c3b30ec 100644 Binary files a/EssentialFeed/EssentialFeediOSTests/Image Comments UI/snapshots/IMAGE_COMMENTS_dark.png and b/EssentialFeed/EssentialFeediOSTests/Image Comments UI/snapshots/IMAGE_COMMENTS_dark.png differ diff --git a/EssentialFeed/EssentialFeediOSTests/Image Comments UI/snapshots/IMAGE_COMMENTS_light.png b/EssentialFeed/EssentialFeediOSTests/Image Comments UI/snapshots/IMAGE_COMMENTS_light.png index caa8c3f..8b06820 100644 Binary files a/EssentialFeed/EssentialFeediOSTests/Image Comments UI/snapshots/IMAGE_COMMENTS_light.png and b/EssentialFeed/EssentialFeediOSTests/Image Comments UI/snapshots/IMAGE_COMMENTS_light.png differ diff --git a/EssentialFeed/EssentialFeediOSTests/Image Comments UI/snapshots/IMAGE_COMMENTS_light_extraExtraExtraLarge.png b/EssentialFeed/EssentialFeediOSTests/Image Comments UI/snapshots/IMAGE_COMMENTS_light_extraExtraExtraLarge.png index 860e90e..2a59459 100644 Binary files a/EssentialFeed/EssentialFeediOSTests/Image Comments UI/snapshots/IMAGE_COMMENTS_light_extraExtraExtraLarge.png and b/EssentialFeed/EssentialFeediOSTests/Image Comments UI/snapshots/IMAGE_COMMENTS_light_extraExtraExtraLarge.png differ diff --git a/EssentialFeed/EssentialFeediOSTests/Shared UI/ListSnapshotTests.swift b/EssentialFeed/EssentialFeediOSTests/Shared UI/ListSnapshotTests.swift index 8fed510..8dfe7cb 100644 --- a/EssentialFeed/EssentialFeediOSTests/Shared UI/ListSnapshotTests.swift +++ b/EssentialFeed/EssentialFeediOSTests/Shared UI/ListSnapshotTests.swift @@ -14,8 +14,8 @@ class ListSnapshotTests: XCTestCase { sut.display(emptyList()) - assert(snapshot: sut.snapshot(for: .iPhone8(style: .light)), named: "EMPTY_LIST_light") - assert(snapshot: sut.snapshot(for: .iPhone8(style: .dark)), named: "EMPTY_LIST_dark") + assert(snapshot: sut.snapshot(for: .iPhone(style: .light)), named: "EMPTY_LIST_light") + assert(snapshot: sut.snapshot(for: .iPhone(style: .dark)), named: "EMPTY_LIST_dark") } func test_listWithErrorMessage() { @@ -23,9 +23,9 @@ class ListSnapshotTests: XCTestCase { sut.display(.error(message: "This is a\nmulti-line\nerror message")) - assert(snapshot: sut.snapshot(for: .iPhone8(style: .light)), named: "LIST_WITH_ERROR_MESSAGE_light") - assert(snapshot: sut.snapshot(for: .iPhone8(style: .dark)), named: "LIST_WITH_ERROR_MESSAGE_dark") - assert(snapshot: sut.snapshot(for: .iPhone8(style: .light, contentSize: .extraExtraExtraLarge)), named: "LIST_WITH_ERROR_MESSAGE_light_extraExtraExtraLarge") + assert(snapshot: sut.snapshot(for: .iPhone(style: .light)), named: "LIST_WITH_ERROR_MESSAGE_light") + assert(snapshot: sut.snapshot(for: .iPhone(style: .dark)), named: "LIST_WITH_ERROR_MESSAGE_dark") + assert(snapshot: sut.snapshot(for: .iPhone(style: .light, contentSize: .extraExtraExtraLarge)), named: "LIST_WITH_ERROR_MESSAGE_light_extraExtraExtraLarge") } // MARK: - Helpers diff --git a/EssentialFeed/EssentialFeediOSTests/Shared UI/snapshots/EMPTY_LIST_dark.png b/EssentialFeed/EssentialFeediOSTests/Shared UI/snapshots/EMPTY_LIST_dark.png index a5e9491..774a62f 100644 Binary files a/EssentialFeed/EssentialFeediOSTests/Shared UI/snapshots/EMPTY_LIST_dark.png and b/EssentialFeed/EssentialFeediOSTests/Shared UI/snapshots/EMPTY_LIST_dark.png differ diff --git a/EssentialFeed/EssentialFeediOSTests/Shared UI/snapshots/EMPTY_LIST_light.png b/EssentialFeed/EssentialFeediOSTests/Shared UI/snapshots/EMPTY_LIST_light.png index 64636f8..896b491 100644 Binary files a/EssentialFeed/EssentialFeediOSTests/Shared UI/snapshots/EMPTY_LIST_light.png and b/EssentialFeed/EssentialFeediOSTests/Shared UI/snapshots/EMPTY_LIST_light.png differ diff --git a/EssentialFeed/EssentialFeediOSTests/Shared UI/snapshots/LIST_WITH_ERROR_MESSAGE_dark.png b/EssentialFeed/EssentialFeediOSTests/Shared UI/snapshots/LIST_WITH_ERROR_MESSAGE_dark.png index fea7299..2860dfe 100644 Binary files a/EssentialFeed/EssentialFeediOSTests/Shared UI/snapshots/LIST_WITH_ERROR_MESSAGE_dark.png and b/EssentialFeed/EssentialFeediOSTests/Shared UI/snapshots/LIST_WITH_ERROR_MESSAGE_dark.png differ diff --git a/EssentialFeed/EssentialFeediOSTests/Shared UI/snapshots/LIST_WITH_ERROR_MESSAGE_light.png b/EssentialFeed/EssentialFeediOSTests/Shared UI/snapshots/LIST_WITH_ERROR_MESSAGE_light.png index 10a5104..80f87f4 100644 Binary files a/EssentialFeed/EssentialFeediOSTests/Shared UI/snapshots/LIST_WITH_ERROR_MESSAGE_light.png and b/EssentialFeed/EssentialFeediOSTests/Shared UI/snapshots/LIST_WITH_ERROR_MESSAGE_light.png differ diff --git a/EssentialFeed/EssentialFeediOSTests/Shared UI/snapshots/LIST_WITH_ERROR_MESSAGE_light_extraExtraExtraLarge.png b/EssentialFeed/EssentialFeediOSTests/Shared UI/snapshots/LIST_WITH_ERROR_MESSAGE_light_extraExtraExtraLarge.png index b7495a2..794be1e 100644 Binary files a/EssentialFeed/EssentialFeediOSTests/Shared UI/snapshots/LIST_WITH_ERROR_MESSAGE_light_extraExtraExtraLarge.png and b/EssentialFeed/EssentialFeediOSTests/Shared UI/snapshots/LIST_WITH_ERROR_MESSAGE_light_extraExtraExtraLarge.png differ