From 394760081c7c2daa5ae6c18e00cdeaf2b67e22c1 Mon Sep 17 00:00:00 2001 From: Ryan Nystrom Date: Thu, 13 Oct 2016 12:36:06 -0700 Subject: [PATCH] Add isLast/FirstSection API to IGListSectionController Summary: Product needs show that section controllers need some sort of awarenes when they are the first/last section in a list (e.g. for cell borders). Adding this simple API, non-breaking. Reviewed By: jessesquires Differential Revision: D4016023 fbshipit-source-id: c75f9298fc73875a1cabad191fe2db6cb9ee4376 --- CHANGELOG.md | 2 +- Source/IGListAdapter.m | 5 ++++ Source/IGListSectionController.h | 10 ++++++++ .../IGListSectionControllerInternal.h | 4 +++ Tests/IGListAdapterTests.m | 25 +++++++++++++++++++ 5 files changed, 45 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85cb7e9c..ff4b9849 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ The changelog for `IGListKit`. Also see the [releases](https://github.com/instag ## Master - Fixed `-[IGListAdapter reloadDataWithCompletion:]` not returning early when `collectionView` or `dataSource` is nil and `completion` is nil. [Ben Asher](https://github.com/benasher44) [#51](https://github.com/Instagram/IGListKit/pull/51) - +- Added `-isFirstSection` and `-isLastSection` APIs to `IGListSectionController` 1.0.0 ----- diff --git a/Source/IGListAdapter.m b/Source/IGListAdapter.m index 05610204..5a088e34 100644 --- a/Source/IGListAdapter.m +++ b/Source/IGListAdapter.m @@ -386,6 +386,9 @@ // for IGListSectionController subclasses after calling [super init] IGListSectionControllerPushThread(self.viewController, self); + id firstObject = objects.firstObject; + id lastObject = objects.lastObject; + for (id object in objects) { // infra checks to see if a controller exists IGListSectionController *sectionController = [map sectionControllerForObject:object]; @@ -403,6 +406,8 @@ // in case the section controller was created outside of -listAdapter:sectionControllerForObject: sectionController.collectionContext = self; sectionController.viewController = self.viewController; + sectionController.isFirstSection = (object == firstObject); + sectionController.isLastSection = (object == lastObject); // check if the item has changed instances or is new const NSUInteger oldSection = [map sectionForObject:object]; diff --git a/Source/IGListSectionController.h b/Source/IGListSectionController.h index 61f8799b..22a2a827 100644 --- a/Source/IGListSectionController.h +++ b/Source/IGListSectionController.h @@ -34,6 +34,16 @@ */ @property (nonatomic, weak, nullable, readonly) id collectionContext; +/** + Returns YES if the section controller is the first section in the list. + */ +@property (nonatomic, assign, readonly) BOOL isFirstSection; + +/** + Returns YES if the section controller is the last section in the list. + */ +@property (nonatomic, assign, readonly) BOOL isLastSection; + /** The margins used to lay out content in the section controller. diff --git a/Source/Internal/IGListSectionControllerInternal.h b/Source/Internal/IGListSectionControllerInternal.h index f7b92d90..73c9cb66 100644 --- a/Source/Internal/IGListSectionControllerInternal.h +++ b/Source/Internal/IGListSectionControllerInternal.h @@ -19,4 +19,8 @@ FOUNDATION_EXTERN void IGListSectionControllerPopThread(void); @property (nonatomic, weak, readwrite) UIViewController *viewController; +@property (nonatomic, assign, readwrite) BOOL isFirstSection; + +@property (nonatomic, assign, readwrite) BOOL isLastSection; + @end diff --git a/Tests/IGListAdapterTests.m b/Tests/IGListAdapterTests.m index de7c307d..8d16fdcf 100644 --- a/Tests/IGListAdapterTests.m +++ b/Tests/IGListAdapterTests.m @@ -452,4 +452,29 @@ XCTAssertNil(weakSectionController); } +- (void)test_whenAdapterUpdatedTwice_withThreeSections_thatSectionsUpdatedFirstLast { + self.dataSource.objects = @[@0, @1, @2]; + [self.adapter reloadDataWithCompletion:nil]; + + XCTAssertTrue([[self.adapter sectionControllerForObject:@0] isFirstSection]); + XCTAssertFalse([[self.adapter sectionControllerForObject:@1] isFirstSection]); + XCTAssertFalse([[self.adapter sectionControllerForObject:@2] isFirstSection]); + + XCTAssertFalse([[self.adapter sectionControllerForObject:@0] isLastSection]); + XCTAssertFalse([[self.adapter sectionControllerForObject:@1] isLastSection]); + XCTAssertTrue([[self.adapter sectionControllerForObject:@2] isLastSection]); + + // update and shift objects to test that first/last flags are also updated + self.dataSource.objects = @[@2, @0, @1]; + [self.adapter performUpdatesAnimated:NO completion:nil]; + + XCTAssertFalse([[self.adapter sectionControllerForObject:@0] isFirstSection]); + XCTAssertFalse([[self.adapter sectionControllerForObject:@1] isFirstSection]); + XCTAssertTrue([[self.adapter sectionControllerForObject:@2] isFirstSection]); + + XCTAssertFalse([[self.adapter sectionControllerForObject:@0] isLastSection]); + XCTAssertTrue([[self.adapter sectionControllerForObject:@1] isLastSection]); + XCTAssertFalse([[self.adapter sectionControllerForObject:@2] isLastSection]); +} + @end