From d01784bd66c9d6d9b196ae085f2ddf46e34bb158 Mon Sep 17 00:00:00 2001 From: Maxime Ollivier Date: Tue, 19 Dec 2023 07:17:30 -0800 Subject: [PATCH] add swipe action to UICollectionViewCompositionalLayout example Summary: Lets add swipe to delete. Differential Revision: D52262785 fbshipit-source-id: b95cdee116374c561967dd585d02110a4d877e16 --- .../project.pbxproj | 8 +++ .../Models/SwipeActionSection.swift | 25 +++++++ ...ipeActionComposabelSectionController.swift | 68 +++++++++++++++++++ .../CompositionLayoutViewController.swift | 3 + 4 files changed, 104 insertions(+) create mode 100644 Examples/Examples-iOS/IGListKitExamples/Models/SwipeActionSection.swift create mode 100644 Examples/Examples-iOS/IGListKitExamples/SectionControllers/With Composable Layout/SwipeActionComposabelSectionController.swift diff --git a/Examples/Examples-iOS/IGListKitExamples.xcodeproj/project.pbxproj b/Examples/Examples-iOS/IGListKitExamples.xcodeproj/project.pbxproj index 1e55c84ad..ccfd156ac 100644 --- a/Examples/Examples-iOS/IGListKitExamples.xcodeproj/project.pbxproj +++ b/Examples/Examples-iOS/IGListKitExamples.xcodeproj/project.pbxproj @@ -74,6 +74,7 @@ 56C05B751E49B33C0026DB39 /* InteractiveCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 56C05B741E49B33C0026DB39 /* InteractiveCell.m */; }; 56C05B781E49B3A50026DB39 /* PhotoCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 56C05B771E49B3A50026DB39 /* PhotoCell.m */; }; 576D20072B2CB50E0012C5B8 /* CompositionLayoutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 576D20062B2CB50E0012C5B8 /* CompositionLayoutViewController.swift */; }; + 576D20092B2CB6C20012C5B8 /* SwipeActionComposabelSectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 576D20082B2CB6C20012C5B8 /* SwipeActionComposabelSectionController.swift */; }; 576D200B2B2CC6DF0012C5B8 /* CompositionLayoutCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 576D200A2B2CC6DF0012C5B8 /* CompositionLayoutCell.swift */; }; 576D200E2B2CE9EC0012C5B8 /* ExpandableComposableSectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 576D200D2B2CE9EC0012C5B8 /* ExpandableComposableSectionController.swift */; }; 576D20102B2CEC4E0012C5B8 /* GridComposableSectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 576D200F2B2CEC4E0012C5B8 /* GridComposableSectionController.swift */; }; @@ -81,6 +82,7 @@ 576D20142B2CF15C0012C5B8 /* GridItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 576D20132B2CF15C0012C5B8 /* GridItem.swift */; }; 576D20162B2CF1A50012C5B8 /* HorizontalCardsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 576D20152B2CF1A50012C5B8 /* HorizontalCardsSection.swift */; }; 576D20182B2CF24F0012C5B8 /* HorizontalComposableSectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 576D20172B2CF24F0012C5B8 /* HorizontalComposableSectionController.swift */; }; + 576D201A2B2CF82C0012C5B8 /* SwipeActionSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 576D20192B2CF82C0012C5B8 /* SwipeActionSection.swift */; }; 821BC4B61DB8B3DC00172ED0 /* StoryboardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 821BC4B51DB8B3DC00172ED0 /* StoryboardViewController.swift */; }; 821BC4B81DB8B48300172ED0 /* StoryboardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 821BC4B71DB8B48300172ED0 /* StoryboardCell.swift */; }; 821BC4BA1DB8B61200172ED0 /* StoryboardLabelSectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 821BC4B91DB8B61200172ED0 /* StoryboardLabelSectionController.swift */; }; @@ -242,6 +244,7 @@ 56C05B761E49B3A50026DB39 /* PhotoCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PhotoCell.h; sourceTree = ""; }; 56C05B771E49B3A50026DB39 /* PhotoCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PhotoCell.m; sourceTree = ""; }; 576D20062B2CB50E0012C5B8 /* CompositionLayoutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositionLayoutViewController.swift; sourceTree = ""; }; + 576D20082B2CB6C20012C5B8 /* SwipeActionComposabelSectionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeActionComposabelSectionController.swift; sourceTree = ""; }; 576D200A2B2CC6DF0012C5B8 /* CompositionLayoutCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositionLayoutCell.swift; sourceTree = ""; }; 576D200D2B2CE9EC0012C5B8 /* ExpandableComposableSectionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpandableComposableSectionController.swift; sourceTree = ""; }; 576D200F2B2CEC4E0012C5B8 /* GridComposableSectionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GridComposableSectionController.swift; sourceTree = ""; }; @@ -249,6 +252,7 @@ 576D20132B2CF15C0012C5B8 /* GridItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GridItem.swift; sourceTree = ""; }; 576D20152B2CF1A50012C5B8 /* HorizontalCardsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HorizontalCardsSection.swift; sourceTree = ""; }; 576D20172B2CF24F0012C5B8 /* HorizontalComposableSectionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HorizontalComposableSectionController.swift; sourceTree = ""; }; + 576D20192B2CF82C0012C5B8 /* SwipeActionSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeActionSection.swift; sourceTree = ""; }; 821BC4B51DB8B3DC00172ED0 /* StoryboardViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardViewController.swift; sourceTree = ""; }; 821BC4B71DB8B48300172ED0 /* StoryboardCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardCell.swift; sourceTree = ""; }; 821BC4B91DB8B61200172ED0 /* StoryboardLabelSectionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardLabelSectionController.swift; sourceTree = ""; }; @@ -505,6 +509,7 @@ 2991F91D1D7BB30C00B0C58F /* User.swift */, 576D20132B2CF15C0012C5B8 /* GridItem.swift */, 576D20152B2CF1A50012C5B8 /* HorizontalCardsSection.swift */, + 576D20192B2CF82C0012C5B8 /* SwipeActionSection.swift */, EB02899E202A11DF007E17D5 /* RemodelGeneratedModels */, 2926585C1E74A0360041B56D /* ViewModels */, ); @@ -526,6 +531,7 @@ 576D200F2B2CEC4E0012C5B8 /* GridComposableSectionController.swift */, 576D20112B2CF04F0012C5B8 /* UserComposableSectionController.swift */, 576D20172B2CF24F0012C5B8 /* HorizontalComposableSectionController.swift */, + 576D20082B2CB6C20012C5B8 /* SwipeActionComposabelSectionController.swift */, ); path = "With Composable Layout"; sourceTree = ""; @@ -874,6 +880,7 @@ 2942FF8F1D9F39E00015D24B /* GridSectionController.swift in Sources */, 821BC4B81DB8B48300172ED0 /* StoryboardCell.swift in Sources */, 56C05B781E49B3A50026DB39 /* PhotoCell.m in Sources */, + 576D201A2B2CF82C0012C5B8 /* SwipeActionSection.swift in Sources */, 2942FF921D9F39E00015D24B /* RemoveSectionController.swift in Sources */, 26271C8E1DAE9D3F0073E116 /* SingleSectionViewController.swift in Sources */, 2961B3AD1D68B0B5001C9451 /* LabelCell.swift in Sources */, @@ -893,6 +900,7 @@ 296DD7571DD2163800206780 /* ManuallySelfSizingCell.swift in Sources */, 292658571E749EDC0041B56D /* CalendarViewController.swift in Sources */, 29C6297F1DCFD9E9004A5BB1 /* FeedItemSectionController.swift in Sources */, + 576D20092B2CB6C20012C5B8 /* SwipeActionComposabelSectionController.swift in Sources */, 2942FF8E1D9F39E00015D24B /* ExpandableSectionController.swift in Sources */, 29F7E2AA1E9283FF00197586 /* AnnouncingDepsViewController.swift in Sources */, 9DB684E1251B10C2002023DD /* UIColor+Extension.swift in Sources */, diff --git a/Examples/Examples-iOS/IGListKitExamples/Models/SwipeActionSection.swift b/Examples/Examples-iOS/IGListKitExamples/Models/SwipeActionSection.swift new file mode 100644 index 000000000..8d74da028 --- /dev/null +++ b/Examples/Examples-iOS/IGListKitExamples/Models/SwipeActionSection.swift @@ -0,0 +1,25 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import IGListKit +import Foundation + +final class SwipeActionSection: NSObject { + +} + +extension SwipeActionSection: ListDiffable { + + func diffIdentifier() -> NSObjectProtocol { + return self + } + + func isEqual(toDiffableObject object: ListDiffable?) -> Bool { + return self === object ? true : self.isEqual(object) + } + +} diff --git a/Examples/Examples-iOS/IGListKitExamples/SectionControllers/With Composable Layout/SwipeActionComposabelSectionController.swift b/Examples/Examples-iOS/IGListKitExamples/SectionControllers/With Composable Layout/SwipeActionComposabelSectionController.swift new file mode 100644 index 000000000..b2fee4f8b --- /dev/null +++ b/Examples/Examples-iOS/IGListKitExamples/SectionControllers/With Composable Layout/SwipeActionComposabelSectionController.swift @@ -0,0 +1,68 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import IGListKit +import UIKit + +final class SwipeActionComposabelSectionController: ListSectionController, CompositionLayoutCapable { + + private var object:SwipeActionSection? + + private var items = ["1. Swipe to delete me", "2. Swipe to delete me", "3. Swipe to delete me"] + + override func numberOfItems() -> Int { + return items.count + } + + override func sizeForItem(at index: Int) -> CGSize { + // Compositional layout doesn't request sizes per NSIndexPath + return CGSizeZero + } + + override func cellForItem(at index: Int) -> UICollectionViewCell { + let cell: CompositionLayoutCell = collectionContext.dequeueReusableCell(for: self, at: index) + cell.text = items[index] + return cell + } + + override func didUpdate(to object: Any) { + self.object = object as? SwipeActionSection + } + + func collectionViewSectionLayout(layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? { + var config = UICollectionLayoutListConfiguration(appearance: .plain) + + config.trailingSwipeActionsConfigurationProvider = {[weak self] indexPath in + // Sections should match, but just in case + guard let self = self, indexPath.section == self.section else { + return nil + } + return self.swipeActionFor(index: indexPath.item) + } + + return NSCollectionLayoutSection.list(using: config, layoutEnvironment: layoutEnvironment) + } + + // MARK: CompositionLayoutCapable + + private func swipeActionFor(index:Int) -> UISwipeActionsConfiguration? { + let action = UIContextualAction(style: .destructive, title: "Delete") {[weak self] action, view, block in + self?.deleteItem(index: index, block: block) + } + return UISwipeActionsConfiguration(actions: [action]) + } + + private func deleteItem(index:Int, block: @escaping (Bool) -> ()) { + self.collectionContext.performBatch(animated: true) {updates in + self.items.remove(at: index) + updates.delete(in: self, at: IndexSet(integer: index)) + } completion: { completed in + block(completed) + } + } + +} diff --git a/Examples/Examples-iOS/IGListKitExamples/ViewControllers/CompositionLayoutViewController.swift b/Examples/Examples-iOS/IGListKitExamples/ViewControllers/CompositionLayoutViewController.swift index a515ac756..13a0a0f14 100644 --- a/Examples/Examples-iOS/IGListKitExamples/ViewControllers/CompositionLayoutViewController.swift +++ b/Examples/Examples-iOS/IGListKitExamples/ViewControllers/CompositionLayoutViewController.swift @@ -28,6 +28,7 @@ final class CompositionLayoutViewController: UIViewController, ListAdapterDataSo GridItem(color: UIColor(red: 237 / 255.0, green: 73 / 255.0, blue: 86 / 255.0, alpha: 1), itemCount: 6), User(pk: 2, name: "Ryan Olson", handle: "ryanolsonk"), HorizontalCardsSection(cardCount: 10), + SwipeActionSection(), "Praesent commodo cursus magna, vel scelerisque nisl consectetur et.", User(pk: 4, name: "Oliver Rickard", handle: "ocrickard"), HorizontalCardsSection(cardCount: 2), @@ -84,6 +85,8 @@ final class CompositionLayoutViewController: UIViewController, ListAdapterDataSo return UserComposableSectionController() case is HorizontalCardsSection: return HorizontalComposableSectionController() + case is SwipeActionSection: + return SwipeActionComposabelSectionController() default: return ListSectionController() }