diff --git a/CHANGELOG.md b/CHANGELOG.md index 075b8aad..38772e11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ The changelog for `IGListKit`. Also see the [releases](https://github.com/instag - Added `-[IGListSectionController didHighlightItemAtIndex:]` and `-[IGListSectionController didUnhighlightItemAtIndex:]` APIs to support `UICollectionView` cell highlighting. [Kevin Delannoy](https://github.com/delannoyk) [(#933)](https://github.com/Instagram/IGListKit/pull/933) +- Added `-didDeselectSectionController:withObject:` to `IGListSingleSectionControllerDelegate` [Darren Clark](https://github.com/darrenclark) [(#954)](https://github.com/Instagram/IGListKit/pull/954) + - Added a new listener API to be notified when `IGListAdapter` finishes updating. Add listeners via `-[IGListAdapter addUpdateListener:]` with objects conforming to the new `IGListAdapterUpdateListener` protocol. [Ryan Nystrom](https://github.com/rnystrom) [(tbd)](https://github.com/Instagram/IGListKit/pull/tbd) - Updated project settings for iOS 11. [Ryan Nystrom](https://github.com/rnystrom) [(#942)](https://github.com/Instagram/IGListKit/pull/942) diff --git a/IGListKit.xcodeproj/project.pbxproj b/IGListKit.xcodeproj/project.pbxproj index 1d243e24..ccd22a86 100644 --- a/IGListKit.xcodeproj/project.pbxproj +++ b/IGListKit.xcodeproj/project.pbxproj @@ -266,8 +266,8 @@ 29DA5CA71EA7D37000113926 /* IGListTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 29DA5CA61EA7D37000113926 /* IGListTestCase.m */; }; 29DA5CA81EA7D37000113926 /* IGListTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 29DA5CA61EA7D37000113926 /* IGListTestCase.m */; }; 29EA6C491DB43A8000957A88 /* IGTestNibCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 294369B01DB1B7AE0025F6E7 /* IGTestNibCell.xib */; }; - 5D1C6B481FB1109000188009 /* IGListAdapterUpdateTester.h in Headers */ = {isa = PBXBuildFile; fileRef = 294CDE611F995DD7002CF6E4 /* IGListAdapterUpdateTester.h */; }; - 5D1C6B491FB1109200188009 /* IGListAdapterUpdateTester.m in Sources */ = {isa = PBXBuildFile; fileRef = 294CDE621F995DD7002CF6E4 /* IGListAdapterUpdateTester.m */; }; + 6A9EB3611F841E5D0070C572 /* IGTestSingleWithoutDeselectionDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A9EB3601F841E5D0070C572 /* IGTestSingleWithoutDeselectionDelegate.m */; }; + 6A9EB3621F841E5D0070C572 /* IGTestSingleWithoutDeselectionDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A9EB3601F841E5D0070C572 /* IGTestSingleWithoutDeselectionDelegate.m */; }; 7BC0C61B1F5C401F00A06ADD /* IGListArrayUtilsInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BC0C61A1F5C401F00A06ADD /* IGListArrayUtilsInternal.h */; }; 7BC0C61C1F5C402600A06ADD /* IGListArrayUtilsInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BC0C61A1F5C401F00A06ADD /* IGListArrayUtilsInternal.h */; }; 7BC0C61D1F5C402600A06ADD /* IGListArrayUtilsInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BC0C61A1F5C401F00A06ADD /* IGListArrayUtilsInternal.h */; }; @@ -519,6 +519,8 @@ 29DA5CA61EA7D37000113926 /* IGListTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IGListTestCase.m; sourceTree = ""; }; 29DA5CA91EA7D39B00113926 /* IGListTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IGListTestCase.h; sourceTree = ""; }; 29DA5CAA1EA7D3FF00113926 /* IGListTestHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IGListTestHelpers.h; sourceTree = ""; }; + 6A9EB35F1F841E5D0070C572 /* IGTestSingleWithoutDeselectionDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IGTestSingleWithoutDeselectionDelegate.h; sourceTree = ""; }; + 6A9EB3601F841E5D0070C572 /* IGTestSingleWithoutDeselectionDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IGTestSingleWithoutDeselectionDelegate.m; sourceTree = ""; }; 7BC0C61A1F5C401F00A06ADD /* IGListArrayUtilsInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IGListArrayUtilsInternal.h; sourceTree = ""; }; 821BC4BE1DB8C95300172ED0 /* IGListSingleStoryboardItemControllerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IGListSingleStoryboardItemControllerTests.m; sourceTree = ""; }; 821BC4C21DB8CAE900172ED0 /* IGTestStoryboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = IGTestStoryboard.storyboard; sourceTree = ""; }; @@ -738,8 +740,6 @@ children = ( 0D52F0AB1F1ADA1600460F79 /* IGListAdapter+DebugDescription.h */, 0D52F0AC1F1ADA1600460F79 /* IGListAdapter+DebugDescription.m */, - 0D52F0B31F1AE81800460F79 /* IGListBindingSectionController+DebugDescription.h */, - 0D52F0B51F1AE8BD00460F79 /* IGListBindingSectionController+DebugDescription.m */, DA5F48491E8E9D7000DAE6DA /* IGListAdapter+UICollectionView.h */, DA5F484A1E8E9D7000DAE6DA /* IGListAdapter+UICollectionView.m */, 0B3B92B61E08D7F5008390ED /* IGListAdapterInternal.h */, @@ -753,6 +753,8 @@ 297278BB1E6B58560099D8EA /* IGListBatchUpdates.h */, 297278BC1E6B58560099D8EA /* IGListBatchUpdates.m */, 297278C31E6B59D50099D8EA /* IGListBatchUpdateState.h */, + 0D52F0B31F1AE81800460F79 /* IGListBindingSectionController+DebugDescription.h */, + 0D52F0B51F1AE8BD00460F79 /* IGListBindingSectionController+DebugDescription.m */, 917E89871E800EE70015F934 /* IGListCollectionViewLayoutInternal.h */, 290DF3521E930C89009FE456 /* IGListDebugger.h */, 290DF3531E930C89009FE456 /* IGListDebugger.m */, @@ -819,6 +821,8 @@ 298DDA291E3B166100F76F50 /* IGLayoutTestItem.m */, 298DDA2A1E3B166100F76F50 /* IGLayoutTestSection.h */, 298DDA2B1E3B166100F76F50 /* IGLayoutTestSection.m */, + 294CDE611F995DD7002CF6E4 /* IGListAdapterUpdateTester.h */, + 294CDE621F995DD7002CF6E4 /* IGListAdapterUpdateTester.m */, 88144EF11D870EDC007C7F66 /* IGListTestAdapterDataSource.h */, 88144EF21D870EDC007C7F66 /* IGListTestAdapterDataSource.m */, 8285404E1DE40D2D00118B94 /* IGListTestAdapterHorizontalDataSource.h */, @@ -837,6 +841,8 @@ 8240C7F71DC2F3FB00B3AAE7 /* IGListTestStoryboardSection.m */, 88144EF71D870EDC007C7F66 /* IGListTestUICollectionViewDataSource.h */, 88144EF81D870EDC007C7F66 /* IGListTestUICollectionViewDataSource.m */, + 2995409A1F588C8D00F647CF /* IGTestBindingWithoutDeselectionDelegate.h */, + 2995409B1F588C8D00F647CF /* IGTestBindingWithoutDeselectionDelegate.m */, 88144EF91D870EDC007C7F66 /* IGTestCell.h */, 88144EFA1D870EDC007C7F66 /* IGTestCell.m */, 88144EFB1D870EDC007C7F66 /* IGTestDelegateController.h */, @@ -861,6 +867,8 @@ 26271C891DAE94E40073E116 /* IGTestSingleNibItemDataSource.m */, 821BC4D11DB9816E00172ED0 /* IGTestSingleStoryboardItemDataSource.h */, 821BC4D21DB981AB00172ED0 /* IGTestSingleStoryboardItemDataSource.m */, + 6A9EB35F1F841E5D0070C572 /* IGTestSingleWithoutDeselectionDelegate.h */, + 6A9EB3601F841E5D0070C572 /* IGTestSingleWithoutDeselectionDelegate.m */, 88144F031D870EDC007C7F66 /* IGTestStackedDataSource.h */, 88144F041D870EDC007C7F66 /* IGTestStackedDataSource.m */, 821BC4CC1DB8D8C500172ED0 /* IGTestStoryboardCell.h */, @@ -875,10 +883,6 @@ 298DD9D91E3ADE3300F76F50 /* IGTestStringBindableCell.m */, 88144F051D870EDC007C7F66 /* IGTestSupplementarySource.h */, 88144F061D870EDC007C7F66 /* IGTestSupplementarySource.m */, - 2995409A1F588C8D00F647CF /* IGTestBindingWithoutDeselectionDelegate.h */, - 2995409B1F588C8D00F647CF /* IGTestBindingWithoutDeselectionDelegate.m */, - 294CDE611F995DD7002CF6E4 /* IGListAdapterUpdateTester.h */, - 294CDE621F995DD7002CF6E4 /* IGListAdapterUpdateTester.m */, ); path = Objects; sourceTree = ""; @@ -999,7 +1003,6 @@ 0B3B93411E08D7F5008390ED /* IGListWorkingRangeHandler.h in Headers */, 294652B81EA927750063BDD9 /* IGListBatchUpdateData+DebugDescription.h in Headers */, 0B3B92E71E08D7F5008390ED /* IGListIndexPathResultInternal.h in Headers */, - 5D1C6B481FB1109000188009 /* IGListAdapterUpdateTester.h in Headers */, 0B3B932F1E08D7F5008390ED /* IGListAdapterProxy.h in Headers */, 0B3B92FD1E08D7F5008390ED /* IGListAdapterDelegate.h in Headers */, 0B3B92F71E08D7F5008390ED /* IGListAdapter.h in Headers */, @@ -1484,7 +1487,6 @@ 0B3B93131E08D7F5008390ED /* IGListReloadDataUpdater.m in Sources */, 0B3B93231E08D7F5008390ED /* IGListStackedSectionController.m in Sources */, 294652B41EA927440063BDD9 /* UICollectionView+DebugDescription.m in Sources */, - 5D1C6B491FB1109200188009 /* IGListAdapterUpdateTester.m in Sources */, 298DD9CB1E3ACFE300F76F50 /* IGListBindingSectionController.m in Sources */, 0B3B92C91E08D7F5008390ED /* IGListBatchUpdateData.mm in Sources */, ); @@ -1541,6 +1543,7 @@ 29C4748E1DDF460500AE68CE /* IGListAdapterStoryboardTests.m in Sources */, 885FE2411DC51B86009CE2B4 /* IGTestSingleNibItemDataSource.m in Sources */, 885FE2441DC51B86009CE2B4 /* IGTestStackedDataSource.m in Sources */, + 6A9EB3621F841E5D0070C572 /* IGTestSingleWithoutDeselectionDelegate.m in Sources */, 885FE2461DC51B86009CE2B4 /* IGTestSupplementarySource.m in Sources */, 885FE2431DC51B86009CE2B4 /* IGTestStoryboardViewController.m in Sources */, 885FE23A1DC51B86009CE2B4 /* IGListTestSection.m in Sources */, @@ -1651,6 +1654,7 @@ 29C4748C1DDF45F400AE68CE /* IGListAdapterProxyTests.m in Sources */, 8240C7F81DC2F3FB00B3AAE7 /* IGListTestStoryboardSection.m in Sources */, 26271C8A1DAE94E40073E116 /* IGTestSingleNibItemDataSource.m in Sources */, + 6A9EB3611F841E5D0070C572 /* IGTestSingleWithoutDeselectionDelegate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Source/IGListSingleSectionController.h b/Source/IGListSingleSectionController.h index aecc28d6..5b362714 100644 --- a/Source/IGListSingleSectionController.h +++ b/Source/IGListSingleSectionController.h @@ -53,6 +53,18 @@ NS_SWIFT_NAME(ListSingleSectionControllerDelegate) - (void)didSelectSectionController:(IGListSingleSectionController *)sectionController withObject:(id)object; +/** + Tells the delegate that the section controller was deselected. + + @param sectionController The section controller that was deselected. + @param object The model for the given section. + + @note Method is `@optional` until the 4.0.0 release where it will become required. + */ +@optional +- (void)didDeselectSectionController:(IGListSingleSectionController *)sectionController + withObject:(id)object; + @end /** diff --git a/Source/IGListSingleSectionController.m b/Source/IGListSingleSectionController.m index 311b20d8..34f20e6c 100644 --- a/Source/IGListSingleSectionController.m +++ b/Source/IGListSingleSectionController.m @@ -109,4 +109,10 @@ [self.selectionDelegate didSelectSectionController:self withObject:self.item]; } +- (void)didDeselectItemAtIndex:(NSInteger)index { + if ([self.selectionDelegate respondsToSelector:@selector(didDeselectSectionController:withObject:)]) { + [self.selectionDelegate didDeselectSectionController:self withObject:self.item]; + } +} + @end diff --git a/Tests/IGListSingleSectionControllerTests.m b/Tests/IGListSingleSectionControllerTests.m index 1da55268..27679460 100644 --- a/Tests/IGListSingleSectionControllerTests.m +++ b/Tests/IGListSingleSectionControllerTests.m @@ -14,6 +14,7 @@ #import "IGTestCell.h" #import "IGTestSingleItemDataSource.h" #import "IGListTestCase.h" +#import "IGTestSingleWithoutDeselectionDelegate.h" @interface IGListSingleSectionControllerTests : IGListTestCase @@ -101,4 +102,31 @@ [mockDelegate verify]; } +- (void)test_whenDeselected_thatDelegateReceivesEvent { + [self setupWithObjects:@[ + genTestObject(@1, @"a") + ]]; + IGListSingleSectionController *section = [self.adapter sectionControllerForObject:self.dataSource.objects.firstObject]; + id mockDelegate = [OCMockObject mockForProtocol:@protocol(IGListSingleSectionControllerDelegate)]; + section.selectionDelegate = mockDelegate; + [[mockDelegate expect] didDeselectSectionController:section withObject:self.dataSource.objects.firstObject]; + [self.adapter collectionView:self.collectionView didDeselectItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + [mockDelegate verify]; +} + +- (void)test_whenDeselected_withoutImplementation_thatNoOps { + [self setupWithObjects:@[ + genTestObject(@1, @"a") + ]]; + IGListSingleSectionController *section = [self.adapter sectionControllerForObject:self.dataSource.objects.firstObject]; + IGTestSingleWithoutDeselectionDelegate *delegate = [IGTestSingleWithoutDeselectionDelegate new]; + section.selectionDelegate = delegate; + + [self.adapter collectionView:self.collectionView didDeselectItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + XCTAssertFalse(delegate.selected); + + [self.adapter collectionView:self.collectionView didSelectItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + XCTAssertTrue(delegate.selected); +} + @end diff --git a/Tests/Objects/IGTestSingleWithoutDeselectionDelegate.h b/Tests/Objects/IGTestSingleWithoutDeselectionDelegate.h new file mode 100644 index 00000000..6b6d542d --- /dev/null +++ b/Tests/Objects/IGTestSingleWithoutDeselectionDelegate.h @@ -0,0 +1,25 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// +// GitHub: +// https://github.com/Instagram/IGListKit +// +// Documentation: +// https://instagram.github.io/IGListKit/ +// + + +#import + +#import + +@interface IGTestSingleWithoutDeselectionDelegate : NSObject + +@property (nonatomic, assign) BOOL selected; + +@end diff --git a/Tests/Objects/IGTestSingleWithoutDeselectionDelegate.m b/Tests/Objects/IGTestSingleWithoutDeselectionDelegate.m new file mode 100644 index 00000000..a9f4ba91 --- /dev/null +++ b/Tests/Objects/IGTestSingleWithoutDeselectionDelegate.m @@ -0,0 +1,25 @@ +// +// Copyright (c) 2016-present, Facebook, Inc. +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// +// GitHub: +// https://github.com/Instagram/IGListKit +// +// Documentation: +// https://instagram.github.io/IGListKit/ +// + + +#import "IGTestSingleWithoutDeselectionDelegate.h" + +@implementation IGTestSingleWithoutDeselectionDelegate + +- (void)didSelectSectionController:(IGListSingleSectionController *)sectionController withObject:(id)object { + self.selected = YES; +} + +@end