diff --git a/Examples/Examples-iOS/IGListKitExamples/SectionControllers/MonthSectionController.swift b/Examples/Examples-iOS/IGListKitExamples/SectionControllers/MonthSectionController.swift index ebbe76c4c..58c9867f9 100644 --- a/Examples/Examples-iOS/IGListKitExamples/SectionControllers/MonthSectionController.swift +++ b/Examples/Examples-iOS/IGListKitExamples/SectionControllers/MonthSectionController.swift @@ -92,4 +92,26 @@ final class MonthSectionController: ListBindingSectionController, func sectionController(_ sectionController: ListBindingSectionController, didUnhighlightItemAt index: Int, viewModel: Any) {} + @available(iOS 13.0, *) + func sectionController(_ sectionController: ListBindingSectionController, contextMenuConfigurationForItemAt index: Int, point: CGPoint, viewModel: Any) -> UIContextMenuConfiguration? { + return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { _ in + // Create an action for sharing + let share = UIAction(title: "Share", image: UIImage(systemName: "square.and.arrow.up")) { _ in + // Show share sheet + } + + // Create an action for copy + let rename = UIAction(title: "Copy", image: UIImage(systemName: "doc.on.doc")) { _ in + // Perform copy + } + + // Create an action for delete with destructive attributes (highligh in red) + let delete = UIAction(title: "Delete", image: UIImage(systemName: "trash"), attributes: .destructive) { _ in + // Perform delete + } + + // Create a UIMenu with all the actions as children + return UIMenu(title: "", children: [share, rename, delete]) + } + } } diff --git a/Examples/Examples-iOS/Pods/Local Podspecs/IGListSwiftKit.podspec.json b/Examples/Examples-iOS/Pods/Local Podspecs/IGListSwiftKit.podspec.json new file mode 100644 index 000000000..decb3c8a6 --- /dev/null +++ b/Examples/Examples-iOS/Pods/Local Podspecs/IGListSwiftKit.podspec.json @@ -0,0 +1,46 @@ +{ + "name": "IGListSwiftKit", + "version": "4.1.0", + "summary": "A data-driven UICollectionView framework.", + "homepage": "https://github.com/Instagram/IGListKit", + "documentation_url": "https://instagram.github.io/IGListKit", + "description": "A data-driven UICollectionView framework for building fast and flexible lists.", + "license": { + "type": "MIT" + }, + "authors": "Instagram", + "social_media_url": "https://twitter.com/fbOpenSource", + "source": { + "git": "https://github.com/Instagram/IGListKit.git", + "tag": "4.1.0", + "branch": "stable" + }, + "dependencies": { + "IGListKit": [ + "= 4.1.0" + ] + }, + "ios": { + "source_files": [ + "Source/IGListSwiftKit/**/*.{swift}" + ], + "frameworks": "UIKit" + }, + "tvos": { + "source_files": [ + "Source/IGListSwiftKit/**/*.{swift}" + ], + "frameworks": "UIKit" + }, + "requires_arc": true, + "swift_versions": [ + "4.0", + "5.0", + "5.1" + ], + "platforms": { + "ios": "9.0", + "tvos": "9.0" + }, + "swift_version": "5.1" +} diff --git a/Examples/Examples-iOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-Info.plist b/Examples/Examples-iOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-Info.plist new file mode 100644 index 000000000..c26f36f07 --- /dev/null +++ b/Examples/Examples-iOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 4.1.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Examples/Examples-iOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-dummy.m b/Examples/Examples-iOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-dummy.m new file mode 100644 index 000000000..f37ab4805 --- /dev/null +++ b/Examples/Examples-iOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_IGListSwiftKit : NSObject +@end +@implementation PodsDummy_IGListSwiftKit +@end diff --git a/Examples/Examples-iOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-prefix.pch b/Examples/Examples-iOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-prefix.pch new file mode 100644 index 000000000..beb2a2441 --- /dev/null +++ b/Examples/Examples-iOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Examples/Examples-iOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-umbrella.h b/Examples/Examples-iOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-umbrella.h new file mode 100644 index 000000000..cb0e82082 --- /dev/null +++ b/Examples/Examples-iOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double IGListSwiftKitVersionNumber; +FOUNDATION_EXPORT const unsigned char IGListSwiftKitVersionString[]; + diff --git a/Examples/Examples-iOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit.modulemap b/Examples/Examples-iOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit.modulemap new file mode 100644 index 000000000..639b89aa9 --- /dev/null +++ b/Examples/Examples-iOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit.modulemap @@ -0,0 +1,6 @@ +framework module IGListSwiftKit { + umbrella header "IGListSwiftKit-umbrella.h" + + export * + module * { export * } +} diff --git a/Examples/Examples-iOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit.xcconfig b/Examples/Examples-iOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit.xcconfig new file mode 100644 index 000000000..629be5cf1 --- /dev/null +++ b/Examples/Examples-iOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit.xcconfig @@ -0,0 +1,13 @@ +APPLICATION_EXTENSION_API_ONLY = YES +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/IGListSwiftKit +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/IGListDiffKit" "${PODS_CONFIGURATION_BUILD_DIR}/IGListKit" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_LDFLAGS = $(inherited) -framework "UIKit" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/../../.. +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Examples/Examples-tvOS/Pods/Local Podspecs/IGListSwiftKit.podspec.json b/Examples/Examples-tvOS/Pods/Local Podspecs/IGListSwiftKit.podspec.json new file mode 100644 index 000000000..decb3c8a6 --- /dev/null +++ b/Examples/Examples-tvOS/Pods/Local Podspecs/IGListSwiftKit.podspec.json @@ -0,0 +1,46 @@ +{ + "name": "IGListSwiftKit", + "version": "4.1.0", + "summary": "A data-driven UICollectionView framework.", + "homepage": "https://github.com/Instagram/IGListKit", + "documentation_url": "https://instagram.github.io/IGListKit", + "description": "A data-driven UICollectionView framework for building fast and flexible lists.", + "license": { + "type": "MIT" + }, + "authors": "Instagram", + "social_media_url": "https://twitter.com/fbOpenSource", + "source": { + "git": "https://github.com/Instagram/IGListKit.git", + "tag": "4.1.0", + "branch": "stable" + }, + "dependencies": { + "IGListKit": [ + "= 4.1.0" + ] + }, + "ios": { + "source_files": [ + "Source/IGListSwiftKit/**/*.{swift}" + ], + "frameworks": "UIKit" + }, + "tvos": { + "source_files": [ + "Source/IGListSwiftKit/**/*.{swift}" + ], + "frameworks": "UIKit" + }, + "requires_arc": true, + "swift_versions": [ + "4.0", + "5.0", + "5.1" + ], + "platforms": { + "ios": "9.0", + "tvos": "9.0" + }, + "swift_version": "5.1" +} diff --git a/Examples/Examples-tvOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-Info.plist b/Examples/Examples-tvOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-Info.plist new file mode 100644 index 000000000..c26f36f07 --- /dev/null +++ b/Examples/Examples-tvOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 4.1.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Examples/Examples-tvOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-dummy.m b/Examples/Examples-tvOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-dummy.m new file mode 100644 index 000000000..f37ab4805 --- /dev/null +++ b/Examples/Examples-tvOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_IGListSwiftKit : NSObject +@end +@implementation PodsDummy_IGListSwiftKit +@end diff --git a/Examples/Examples-tvOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-prefix.pch b/Examples/Examples-tvOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-prefix.pch new file mode 100644 index 000000000..beb2a2441 --- /dev/null +++ b/Examples/Examples-tvOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Examples/Examples-tvOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-umbrella.h b/Examples/Examples-tvOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-umbrella.h new file mode 100644 index 000000000..cb0e82082 --- /dev/null +++ b/Examples/Examples-tvOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double IGListSwiftKitVersionNumber; +FOUNDATION_EXPORT const unsigned char IGListSwiftKitVersionString[]; + diff --git a/Examples/Examples-tvOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit.modulemap b/Examples/Examples-tvOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit.modulemap new file mode 100644 index 000000000..639b89aa9 --- /dev/null +++ b/Examples/Examples-tvOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit.modulemap @@ -0,0 +1,6 @@ +framework module IGListSwiftKit { + umbrella header "IGListSwiftKit-umbrella.h" + + export * + module * { export * } +} diff --git a/Examples/Examples-tvOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit.xcconfig b/Examples/Examples-tvOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit.xcconfig new file mode 100644 index 000000000..ac4b675a6 --- /dev/null +++ b/Examples/Examples-tvOS/Pods/Target Support Files/IGListSwiftKit/IGListSwiftKit.xcconfig @@ -0,0 +1,12 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/IGListSwiftKit +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/IGListDiffKit" "${PODS_CONFIGURATION_BUILD_DIR}/IGListKit" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_LDFLAGS = $(inherited) -framework "UIKit" +OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/../../.. +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Source/IGListKit/IGListBindingSectionController.m b/Source/IGListKit/IGListBindingSectionController.m index 6a10181b8..7e5ec7a95 100644 --- a/Source/IGListKit/IGListBindingSectionController.m +++ b/Source/IGListKit/IGListBindingSectionController.m @@ -154,4 +154,10 @@ - (void)didUnhighlightItemAtIndex:(NSInteger)index { [self.selectionDelegate sectionController:self didUnhighlightItemAtIndex:index viewModel:self.viewModels[index]]; } +#if !TARGET_OS_TV +- (nullable UIContextMenuConfiguration *)contextMenuConfigurationForItemAtIndex:(NSInteger)index point:(CGPoint)point { + return [self.selectionDelegate sectionController:self contextMenuConfigurationForItemAtIndex:index point:point viewModel:self.viewModels[index]]; +} +#endif + @end diff --git a/Source/IGListKit/IGListBindingSectionControllerSelectionDelegate.h b/Source/IGListKit/IGListBindingSectionControllerSelectionDelegate.h index 6d611af1f..b9ab25bbf 100644 --- a/Source/IGListKit/IGListBindingSectionControllerSelectionDelegate.h +++ b/Source/IGListKit/IGListBindingSectionControllerSelectionDelegate.h @@ -61,6 +61,21 @@ NS_SWIFT_NAME(ListBindingSectionControllerSelectionDelegate) didUnhighlightItemAtIndex:(NSInteger)index viewModel:(id)viewModel; +/** + Tells the delegate that a cell has requested a menu configuration. + + @param sectionController The section controller the request of a menu configuration occurred in. + @param index The index of the cell that is being longed tap. + @param point The point of the tap on the cell. + @param viewModel The view model that was bound to the cell. + + @return An object that conforms to `UIContextMenuConfiguration`. + */ +- (nullable UIContextMenuConfiguration *)sectionController:(IGListBindingSectionController *)sectionController + contextMenuConfigurationForItemAtIndex:(NSInteger)index + point:(CGPoint)point + viewModel:(id)viewModel API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos); + @end NS_ASSUME_NONNULL_END diff --git a/Source/IGListKit/IGListSectionController.h b/Source/IGListKit/IGListSectionController.h index 4a625d698..edf05d68d 100644 --- a/Source/IGListKit/IGListSectionController.h +++ b/Source/IGListKit/IGListSectionController.h @@ -129,6 +129,18 @@ NS_SWIFT_NAME(ListSectionController) */ - (void)didUnhighlightItemAtIndex:(NSInteger)index; +/** + Tells the section controller that the cell has requested a menu configuration. + + @param index The index of the cell that requested the menu. + @param point The point of the tap on the cell. + + @return An object that conforms to `UIContextMenuConfiguration` + + @note The default implementation does nothing. **Calling super is not required.** + */ +- (nullable UIContextMenuConfiguration *)contextMenuConfigurationForItemAtIndex:(NSInteger)index point:(CGPoint)point API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos); + /** Identifies whether an object can be moved through interactive reordering. diff --git a/Source/IGListKit/IGListSectionController.m b/Source/IGListKit/IGListSectionController.m index b54e40032..05dab42e8 100644 --- a/Source/IGListKit/IGListSectionController.m +++ b/Source/IGListKit/IGListSectionController.m @@ -101,6 +101,10 @@ - (void)didHighlightItemAtIndex:(NSInteger)index {} - (void)didUnhighlightItemAtIndex:(NSInteger)index {} +- (nullable UIContextMenuConfiguration *)contextMenuConfigurationForItemAtIndex:(NSInteger)index point:(CGPoint)point { + return nil; +} + - (BOOL)canMoveItemAtIndex:(NSInteger)index { return NO; } diff --git a/Source/IGListKit/Internal/IGListAdapter+UICollectionView.m b/Source/IGListKit/Internal/IGListAdapter+UICollectionView.m index 041cc02e2..9510f026f 100644 --- a/Source/IGListKit/Internal/IGListAdapter+UICollectionView.m +++ b/Source/IGListKit/Internal/IGListAdapter+UICollectionView.m @@ -279,6 +279,19 @@ - (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIn [sectionController didUnhighlightItemAtIndex:indexPath.item]; } +#if !TARGET_OS_TV +- (UIContextMenuConfiguration *)collectionView:(UICollectionView *)collectionView contextMenuConfigurationForItemAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point API_AVAILABLE(ios(13.0)) { + // forward this method to the delegate b/c this implementation will steal the message from the proxy + id collectionViewDelegate = self.collectionViewDelegate; + if ([collectionViewDelegate respondsToSelector:@selector(collectionView:contextMenuConfigurationForItemAtIndexPath:point:)]) { + [collectionViewDelegate collectionView:collectionView contextMenuConfigurationForItemAtIndexPath:indexPath point:point]; + } + + IGListSectionController * sectionController = [self sectionControllerForSection:indexPath.section]; + return [sectionController contextMenuConfigurationForItemAtIndex:indexPath.item point:point]; +} +#endif + #pragma mark - UICollectionViewDelegateFlowLayout - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { diff --git a/Source/IGListKit/Internal/IGListAdapterProxy.m b/Source/IGListKit/Internal/IGListAdapterProxy.m index b2cd15dcb..ae18cca32 100644 --- a/Source/IGListKit/Internal/IGListAdapterProxy.m +++ b/Source/IGListKit/Internal/IGListAdapterProxy.m @@ -35,6 +35,7 @@ static BOOL isInterceptedSelector(SEL sel) { sel == @selector(collectionView:didDeselectItemAtIndexPath:) || sel == @selector(collectionView:didHighlightItemAtIndexPath:) || sel == @selector(collectionView:didUnhighlightItemAtIndexPath:) || + sel == @selector(collectionView:contextMenuConfigurationForItemAtIndexPath:point:) || // UICollectionViewDelegateFlowLayout sel == @selector(collectionView:layout:sizeForItemAtIndexPath:) || sel == @selector(collectionView:layout:insetForSectionAtIndex:) || diff --git a/Tests/IGListAdapterTests.m b/Tests/IGListAdapterTests.m index 46bd3d656..9e39a9115 100644 --- a/Tests/IGListAdapterTests.m +++ b/Tests/IGListAdapterTests.m @@ -1674,7 +1674,7 @@ - (void)test_whenUnhighlightingCell_thatCollectionViewDelegateReceivesMethod { [mockDelegate verify]; } -- (void)test_whenUnlighlightingCell_thatSectionControllerReceivesMethod { +- (void)test_whenUnhighlightingCell_thatSectionControllerReceivesMethod { self.dataSource.objects = @[@0, @1, @2]; [self.adapter reloadDataWithCompletion:nil]; @@ -1692,6 +1692,40 @@ - (void)test_whenUnlighlightingCell_thatSectionControllerReceivesMethod { XCTAssertFalse(s2.wasUnhighlighted); } +- (void)test_whenContextMenuAskedCell_thatCollectionViewDelegateReceivesMethod API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos) { + self.dataSource.objects = @[@0, @1, @2]; + [self.adapter reloadDataWithCompletion:nil]; + + id mockDelegate = [OCMockObject mockForProtocol:@protocol(UICollectionViewDelegate)]; + self.adapter.collectionViewDelegate = mockDelegate; + + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0]; + [[mockDelegate expect] collectionView:self.collectionView contextMenuConfigurationForItemAtIndexPath:indexPath point:CGPointZero]; + + // simulates the collectionview telling its delegate that it needs the context menu configuration + [self.adapter collectionView:self.collectionView contextMenuConfigurationForItemAtIndexPath:indexPath point:CGPointZero]; + + [mockDelegate verify]; +} + +- (void)test_whenContextMenuAskedCell_thatSectionControllerReceivesMethod API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos) { + self.dataSource.objects = @[@0, @1, @2]; + [self.adapter reloadDataWithCompletion:nil]; + + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0]; + + // simulates the collectionview telling its delegate that it needs the context menu configuration + [self.adapter collectionView:self.collectionView contextMenuConfigurationForItemAtIndexPath:indexPath point:CGPointZero]; + + IGListTestSection *s0 = [self.adapter sectionControllerForObject:@0]; + IGListTestSection *s1 = [self.adapter sectionControllerForObject:@1]; + IGListTestSection *s2 = [self.adapter sectionControllerForObject:@2]; + + XCTAssertTrue(s0.requestedContextMenu); + XCTAssertFalse(s1.requestedContextMenu); + XCTAssertFalse(s2.requestedContextMenu); +} + - (void)test_whenDataSourceDoesntHandleObject_thatObjectIsDropped { // IGListTestAdapterDataSource does not handle NSStrings self.dataSource.objects = @[@1, @"dog", @2]; diff --git a/Tests/IGListBindingSectionControllerTests.m b/Tests/IGListBindingSectionControllerTests.m index cc210cbc1..ab5034098 100644 --- a/Tests/IGListBindingSectionControllerTests.m +++ b/Tests/IGListBindingSectionControllerTests.m @@ -196,6 +196,15 @@ - (void)test_whenUnhighlightingCell_thatCorrectViewModelUnhighlighted { XCTAssertEqualObjects(section.unhighlightedViewModel, @"seven"); } +- (void)test_whenContextMenuAskedCell_thatCorrectViewModelRetrieved API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos) { + [self setupWithObjects:@[ + [[IGTestDiffingObject alloc] initWithKey:@1 objects:@[@7, @"seven"]], + ]]; + [self.adapter collectionView:self.collectionView contextMenuConfigurationForItemAtIndexPath:[NSIndexPath indexPathForItem:1 inSection:0] point:CGPointZero]; + IGTestDiffingSectionController *section = [self.adapter sectionControllerForObject:self.dataSource.objects.firstObject]; + XCTAssertEqualObjects(section.contextMenuViewModel, @"seven"); +} + - (void)test_whenDeselectingCell_withoutImplementation_thatNoOps { [self setupWithObjects:@[ [[IGTestDiffingObject alloc] initWithKey:@1 objects:@[@7, @"seven"]], diff --git a/Tests/Objects/IGListTestSection.h b/Tests/Objects/IGListTestSection.h index 62de17e45..5ae4573ea 100644 --- a/Tests/Objects/IGListTestSection.h +++ b/Tests/Objects/IGListTestSection.h @@ -19,5 +19,6 @@ @property (nonatomic, assign) BOOL wasHighlighted; @property (nonatomic, assign) BOOL wasUnhighlighted; @property (nonatomic, assign) BOOL wasDisplayed; +@property (nonatomic, assign) BOOL requestedContextMenu; @end diff --git a/Tests/Objects/IGListTestSection.m b/Tests/Objects/IGListTestSection.m index b5efb9679..806b7658f 100644 --- a/Tests/Objects/IGListTestSection.m +++ b/Tests/Objects/IGListTestSection.m @@ -60,6 +60,11 @@ - (void)didUnhighlightItemAtIndex:(NSInteger)index { self.wasUnhighlighted = YES; } +- (nullable UIContextMenuConfiguration *)contextMenuConfigurationForItemAtIndex:(NSInteger)index point:(CGPoint)point { + self.requestedContextMenu = YES; + return nil; +} + #pragma mark - IGListDisplayDelegate - (void)listAdapter:(IGListAdapter *)listAdapter willDisplaySectionController:(IGListSectionController *)sectionController { diff --git a/Tests/Objects/IGTestBindingWithoutDeselectionDelegate.m b/Tests/Objects/IGTestBindingWithoutDeselectionDelegate.m index 5dfc71f0b..89a3d57f1 100644 --- a/Tests/Objects/IGTestBindingWithoutDeselectionDelegate.m +++ b/Tests/Objects/IGTestBindingWithoutDeselectionDelegate.m @@ -31,4 +31,12 @@ - (void)sectionController:(nonnull IGListBindingSectionController *)sectionContr viewModel:(nonnull id)viewModel { } + +- (nullable UIContextMenuConfiguration *)sectionController:(nonnull IGListBindingSectionController *)sectionController + contextMenuConfigurationForItemAtIndex:(NSInteger)index + point:(CGPoint)point + viewModel:(nonnull id)viewModel API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos) { + return nil; +} + @end diff --git a/Tests/Objects/IGTestDiffingSectionController.h b/Tests/Objects/IGTestDiffingSectionController.h index 5d93cc5fa..c793411ed 100644 --- a/Tests/Objects/IGTestDiffingSectionController.h +++ b/Tests/Objects/IGTestDiffingSectionController.h @@ -13,5 +13,6 @@ @property (nonatomic, strong) id deselectedViewModel; @property (nonatomic, strong) id highlightedViewModel; @property (nonatomic, strong) id unhighlightedViewModel; +@property (nonatomic, strong) id contextMenuViewModel; @end diff --git a/Tests/Objects/IGTestDiffingSectionController.m b/Tests/Objects/IGTestDiffingSectionController.m index 431717bf8..df5b18260 100644 --- a/Tests/Objects/IGTestDiffingSectionController.m +++ b/Tests/Objects/IGTestDiffingSectionController.m @@ -65,4 +65,9 @@ - (void)sectionController:(IGListBindingSectionController *)sectionController di self.unhighlightedViewModel = viewModel; } +- (nullable UIContextMenuConfiguration *)sectionController:(IGListBindingSectionController *)sectionController contextMenuConfigurationForItemAtIndex:(NSInteger)index point:(CGPoint)point viewModel:(id)viewModel API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos) { + self.contextMenuViewModel = viewModel; + return nil; +} + @end