From 5a81b74aba2dd54d388c809ce3eae4a107090184 Mon Sep 17 00:00:00 2001 From: Carlos Martinez Medina Date: Tue, 9 Jan 2024 15:25:47 +0100 Subject: [PATCH] feat(List): Add mising functionality to List BREAKING CHANGE: List asset .image has been renamed to .custom. This custom case has as parameter .image(UIImage) or .url(URL) * https://jira.tid.es/browse/IOS-9421: Add support for url and image gesture --------- Co-authored-by: WanaldinoTelefonica --- .../UICatalogListsViewController.swift | 5 +- .../Lists/Internals/CellLeftSectionView.swift | 57 ++++++++++++++++--- .../Lists/ListCellContentView.swift | 16 +++++- Tests/MisticaTests/UI/CarouselTests.swift | 2 +- Tests/MisticaTests/UI/ListsTests.swift | 38 ++++++------- 5 files changed, 89 insertions(+), 29 deletions(-) diff --git a/MisticaCatalog/Source/Catalog/Mistica/Components/UICatalogListsViewController.swift b/MisticaCatalog/Source/Catalog/Mistica/Components/UICatalogListsViewController.swift index d696c92c..04628969 100644 --- a/MisticaCatalog/Source/Catalog/Mistica/Components/UICatalogListsViewController.swift +++ b/MisticaCatalog/Source/Catalog/Mistica/Components/UICatalogListsViewController.swift @@ -63,6 +63,7 @@ class UICatalogListsViewController: UITableViewController { cell.segmentedControl.insertSegment(withTitle: "Large Icon", at: 1, animated: false) cell.segmentedControl.insertSegment(withTitle: "Small Icon", at: 2, animated: false) cell.segmentedControl.insertSegment(withTitle: "Image", at: 3, animated: false) + cell.segmentedControl.insertSegment(withTitle: "URL", at: 4, animated: false) cell.segmentedControl.selectedSegmentIndex = 0 return cell }() @@ -163,7 +164,9 @@ extension UICatalogListsViewController { case 2: sampleVC.assetType = .smallIcon(.imageIcon) case 3: - sampleVC.assetType = .image(.netflixLogo, size: CGSize(width: 140, height: 80)) + sampleVC.assetType = .custom(.image(.netflixLogo), size: CGSize(width: 140, height: 80)) + case 4: + sampleVC.assetType = .custom(.url(URL(string: "https://www.svgrepo.com/show/19461/url-link.svg")!), size: CGSize(width: 64, height: 64)) default: break } diff --git a/Sources/Mistica/Components/Lists/Internals/CellLeftSectionView.swift b/Sources/Mistica/Components/Lists/Internals/CellLeftSectionView.swift index c3a4cd40..67d5701f 100644 --- a/Sources/Mistica/Components/Lists/Internals/CellLeftSectionView.swift +++ b/Sources/Mistica/Components/Lists/Internals/CellLeftSectionView.swift @@ -13,6 +13,10 @@ private enum ImageSize { static let small: CGFloat = 24 } +public protocol ListCellContentAssetDelegate: AnyObject { + func listCellContentDidTapOnAsset() +} + class CellLeftSectionView: UIStackView { private lazy var heightConstraint = containerView.heightAnchor.constraint(equalToConstant: assetType.viewSize.height) private lazy var widthConstraint = containerView.widthAnchor.constraint(equalToConstant: assetType.viewSize.width) @@ -25,16 +29,23 @@ class CellLeftSectionView: UIStackView { private let imageView = IntrinsictImageView() + weak var delegate: ListCellContentAssetDelegate? { + didSet { + imageView.isUserInteractionEnabled = delegate != nil + } + } + var assetType: ListCellContentView.CellAssetType = .none { didSet { heightConstraint.constant = assetType.viewSize.height widthConstraint.constant = assetType.viewSize.width imageView.intrinsicWidth = assetType.assetSize.width imageView.intrinsicHeight = assetType.assetSize.height - imageView.image = assetType.image imageView.contentMode = assetType.contentMode containerView.makeRounded(cornerRadius: assetType.cornerRadius) containerView.backgroundColor = assetType.backgroundColor + + load() } } @@ -58,6 +69,26 @@ class CellLeftSectionView: UIStackView { fatalError("init(coder:) has not been implemented") } + func load() { + switch assetType { + case .none: + imageView.image = nil + case .custom(let asset, _): + load(asset: asset) + case .smallIcon(let image), .largeIcon(let image, _): + imageView.image = image + } + } + + func load(asset: ListCellContentView.CellAssetType.Asset) { + switch asset { + case .image(let image): + imageView.image = image + case .url(let url): + imageView.load(url: url) + } + } + func centerAlignment() { alignment = .center isLayoutMarginsRelativeArrangement = false @@ -80,6 +111,15 @@ private extension CellLeftSectionView { func commonInit() { addArrangedSubview(containerView) NSLayoutConstraint.activate([heightConstraint, widthConstraint]) + + let gesture = UITapGestureRecognizer(target: self, action: #selector(didTapAsset)) + imageView.addGestureRecognizer(gesture) + imageView.isUserInteractionEnabled = delegate != nil + } + + @objc + func didTapAsset() { + delegate?.listCellContentDidTapOnAsset() } } @@ -88,7 +128,7 @@ private extension ListCellContentView.CellAssetType { switch self { case .none: return CGSize.zero - case let .image(_, size): + case let .custom(_, size): if let size = size { return size } return CGSize(width: ImageSize.large, height: ImageSize.large) case .smallIcon, .largeIcon: @@ -100,7 +140,7 @@ private extension ListCellContentView.CellAssetType { switch self { case .none: return CGSize.zero - case let .image(_, size): + case let .custom(_, size): if let size = size { return size } return CGSize(width: ImageSize.large, height: ImageSize.large) @@ -116,7 +156,7 @@ private extension ListCellContentView.CellAssetType { switch self { case .none, .smallIcon: return 0 - case .image(_, let size): + case .custom(_, let size): return (size != nil ? MisticaConfig.currentCornerRadius.container : viewSize.height / 2) case .largeIcon: return viewSize.height / 2 @@ -125,7 +165,10 @@ private extension ListCellContentView.CellAssetType { var image: UIImage? { switch self { - case .smallIcon(let image), .largeIcon(let image, _), .image(let image, _): + case .custom(let asset, _): + guard case let .image(image) = asset else { return nil } + return image + case .smallIcon(let image), .largeIcon(let image, _): return image case .none: return nil @@ -134,7 +177,7 @@ private extension ListCellContentView.CellAssetType { var contentMode: UIView.ContentMode { switch self { - case .image: + case .custom: return .scaleAspectFill case .none, .smallIcon, .largeIcon: return .scaleAspectFit @@ -145,7 +188,7 @@ private extension ListCellContentView.CellAssetType { switch self { case .largeIcon(_, let backgroundColor): return backgroundColor - case .none, .smallIcon, .image: + case .none, .smallIcon, .custom: return .clear } } diff --git a/Sources/Mistica/Components/Lists/ListCellContentView.swift b/Sources/Mistica/Components/Lists/ListCellContentView.swift index 173b7e20..37ad5db9 100644 --- a/Sources/Mistica/Components/Lists/ListCellContentView.swift +++ b/Sources/Mistica/Components/Lists/ListCellContentView.swift @@ -45,8 +45,13 @@ open class ListCellContentView: UIView { @frozen public enum CellAssetType: Equatable { + public enum Asset: Equatable { + case image(UIImage) + case url(URL) + } + case none - case image(UIImage, size: CGSize? = nil) + case custom(Asset, size: CGSize? = nil) case smallIcon(UIImage) case largeIcon(UIImage, backgroundColor: UIColor) } @@ -195,6 +200,15 @@ open class ListCellContentView: UIView { } } + public var assetDelegate: ListCellContentAssetDelegate? { + get { + leftSection.delegate + } + set { + leftSection.delegate = newValue + } + } + public var controlView: UIView? { didSet { oldValue?.removeFromSuperview() diff --git a/Tests/MisticaTests/UI/CarouselTests.swift b/Tests/MisticaTests/UI/CarouselTests.swift index 244f6487..3b8275e2 100644 --- a/Tests/MisticaTests/UI/CarouselTests.swift +++ b/Tests/MisticaTests/UI/CarouselTests.swift @@ -36,7 +36,7 @@ final class CarouselTests: XCTestCase { func testCellWithTitleAndImage() { let carouselTestsViewController = makeCarouselTestsViewController( title: AnyValues.title, - assetType: .image(AnyValues.image) + assetType: .custom(.image(AnyValues.image)) ) assertSnapshot( diff --git a/Tests/MisticaTests/UI/ListsTests.swift b/Tests/MisticaTests/UI/ListsTests.swift index d9054d5d..bcda0843 100644 --- a/Tests/MisticaTests/UI/ListsTests.swift +++ b/Tests/MisticaTests/UI/ListsTests.swift @@ -23,7 +23,7 @@ final class ListsTests: XCTestCase { // MARK: - Default config func testCellDefaultConfigAndImage() { - let listTestsViewController = makeListTestsViewController(assetType: .image(AnyValues.image)) + let listTestsViewController = makeListTestsViewController(assetType: .custom(.image(AnyValues.image))) assertSnapshot( matching: listTestsViewController, @@ -32,7 +32,7 @@ final class ListsTests: XCTestCase { } func testCellDefaultConfigAndImageWithCustomSize() { - let listTestsViewController = makeListTestsViewController(assetType: .image(AnyValues.image, size: CGSize(width: 100, height: 40))) + let listTestsViewController = makeListTestsViewController(assetType: .custom(.image(AnyValues.image), size: CGSize(width: 100, height: 40))) assertSnapshot( matching: listTestsViewController, @@ -84,7 +84,7 @@ final class ListsTests: XCTestCase { func testCellWithTitleAndImage() { let listTestsViewController = makeListTestsViewController( title: AnyValues.title, - assetType: .image(AnyValues.image) + assetType: .custom(.image(AnyValues.image)) ) assertSnapshot( @@ -96,7 +96,7 @@ final class ListsTests: XCTestCase { func testCellWithTitleAndImageWithCustomSize() { let listTestsViewController = makeListTestsViewController( title: AnyValues.title, - assetType: .image(AnyValues.image, size: CGSize(width: 100, height: 40)) + assetType: .custom(.image(AnyValues.image), size: CGSize(width: 100, height: 40)) ) assertSnapshot( @@ -160,7 +160,7 @@ final class ListsTests: XCTestCase { let listTestsViewController = makeListTestsViewController( title: AnyValues.title, subtitle: AnyValues.subtitle, - assetType: .image(AnyValues.image) + assetType: .custom(.image(AnyValues.image)) ) assertSnapshot( @@ -173,7 +173,7 @@ final class ListsTests: XCTestCase { let listTestsViewController = makeListTestsViewController( title: AnyValues.title, subtitle: AnyValues.subtitle, - assetType: .image(AnyValues.image, size: CGSize(width: 100, height: 40)) + assetType: .custom(.image(AnyValues.image), size: CGSize(width: 100, height: 40)) ) assertSnapshot( @@ -240,7 +240,7 @@ final class ListsTests: XCTestCase { let listTestsViewController = makeListTestsViewController( title: AnyValues.title, detailText: AnyValues.detailText, - assetType: .image(AnyValues.image) + assetType: .custom(.image(AnyValues.image)) ) assertSnapshot( @@ -253,7 +253,7 @@ final class ListsTests: XCTestCase { let listTestsViewController = makeListTestsViewController( title: AnyValues.title, detailText: AnyValues.detailText, - assetType: .image(AnyValues.image, size: CGSize(width: 100, height: 40)) + assetType: .custom(.image(AnyValues.image), size: CGSize(width: 100, height: 40)) ) assertSnapshot( @@ -322,7 +322,7 @@ final class ListsTests: XCTestCase { title: AnyValues.title, subtitle: AnyValues.subtitle, detailText: AnyValues.detailText, - assetType: .image(AnyValues.image) + assetType: .custom(.image(AnyValues.image)) ) assertSnapshot( @@ -336,7 +336,7 @@ final class ListsTests: XCTestCase { title: AnyValues.title, subtitle: AnyValues.subtitle, detailText: AnyValues.detailText, - assetType: .image(AnyValues.image, size: CGSize(width: 100, height: 40)) + assetType: .custom(.image(AnyValues.image), size: CGSize(width: 100, height: 40)) ) assertSnapshot( @@ -421,7 +421,7 @@ final class ListsTests: XCTestCase { title: AnyValues.title, subtitle: AnyValues.subtitle, detailText: AnyValues.detailText, - assetType: .image(AnyValues.image), + assetType: .custom(.image(AnyValues.image)), showHeadline: true ) @@ -436,7 +436,7 @@ final class ListsTests: XCTestCase { title: AnyValues.title, subtitle: AnyValues.subtitle, detailText: AnyValues.detailText, - assetType: .image(AnyValues.image, size: CGSize(width: 100, height: 40)), + assetType: .custom(.image(AnyValues.image), size: CGSize(width: 100, height: 40)), showHeadline: true ) @@ -451,7 +451,7 @@ final class ListsTests: XCTestCase { title: AnyValues.title, subtitle: AnyValues.subtitle, detailText: AnyValues.detailText, - assetType: .image(AnyValues.image), + assetType: .custom(.image(AnyValues.image)), customControl: .custom(makeCustomControlView), showHeadline: true ) @@ -467,7 +467,7 @@ final class ListsTests: XCTestCase { title: AnyValues.title, subtitle: AnyValues.subtitle, detailText: AnyValues.detailText, - assetType: .image(AnyValues.image, size: CGSize(width: 60, height: 20)), + assetType: .custom(.image(AnyValues.image), size: CGSize(width: 60, height: 20)), customControl: .custom(makeCustomControlView), showHeadline: true ) @@ -625,7 +625,7 @@ final class ListsTests: XCTestCase { title: AnyValues.titleMultiline, subtitle: AnyValues.subtitleMultiline, detailText: AnyValues.detailTextMultiline, - assetType: .image(AnyValues.image), + assetType: .custom(.image(AnyValues.image)), customControl: .navigation(makeNavigationPresetViewWithoutBagde), showHeadline: true ) @@ -641,7 +641,7 @@ final class ListsTests: XCTestCase { title: AnyValues.title, subtitle: AnyValues.subtitleMultiline, detailText: AnyValues.detailTextMultiline, - assetType: .image(AnyValues.image), + assetType: .custom(.image(AnyValues.image)), customControl: .navigation(makeNavigationPresetViewWithoutBagde), showHeadline: true, numberOfRows: 3 @@ -703,7 +703,7 @@ final class ListsTests: XCTestCase { title: AnyValues.title, subtitle: AnyValues.subtitleMultiline, detailText: AnyValues.detailTextMultiline, - assetType: .image(AnyValues.image), + assetType: .custom(.image(AnyValues.image)), customControl: .navigation(makeNavigationPresetViewWithoutBagde), showHeadline: true, cellLayoutStyle: .boxed, @@ -766,7 +766,7 @@ final class ListsTests: XCTestCase { title: AnyValues.title, subtitle: AnyValues.subtitleMultiline, detailText: AnyValues.detailTextMultiline, - assetType: .image(AnyValues.image), + assetType: .custom(.image(AnyValues.image)), customControl: .navigation(makeNavigationPresetViewWithoutBagde), showHeadline: true, cellLayoutStyle: .boxedInverse, @@ -789,7 +789,7 @@ final class ListsTests: XCTestCase { title: AnyValues.title, subtitle: AnyValues.subtitle, detailText: AnyValues.detailText, - assetType: .image(AnyValues.image), + assetType: .custom(.image(AnyValues.image)), customControl: .custom(makeCustomControlView), showHeadline: true, cellLayoutStyle: .fullWidth