├── NECollectionViewLayout
├── Assets
│ └── .gitkeep
└── Classes
│ ├── NECollectionViewLayout.h
│ ├── NECollectionViewFlowLayout
│ ├── NECollectionViewFlowLayout+subhook.h
│ ├── ReuseView
│ │ ├── NECollectionViewFlowLayoutSectionContentScrollCollectionReusableView.h
│ │ └── NECollectionViewFlowLayoutSectionContentScrollCollectionReusableView.m
│ ├── NECollectionViewFlowLayoutInvalidationContext.h
│ ├── NECollectionViewFlowLayoutAttributes.h
│ ├── NECollectionViewFlowLayoutInvalidationContext.m
│ ├── private
│ │ ├── NECollectionViewFlowLayoutCollection.h
│ │ ├── NECollectionViewFlowLayoutTypes.h
│ │ ├── NECollectionViewFlowLayoutNode.h
│ │ ├── NECollectionViewDelegateResponds.h
│ │ ├── NECollectionViewFlowLayoutInvalidation.h
│ │ ├── NECollectionViewLayoutHelpers.h
│ │ ├── NECollectionViewUpdates.h
│ │ ├── NECollectionViewFlowLayoutLine.h
│ │ ├── NECollectionViewFlowLayoutContext.h
│ │ ├── NECollectionViewFlowLayoutItem.h
│ │ └── NECollectionViewFlowLayoutCalculator.h
│ ├── NECollectionViewFlowLayoutAnimator.m
│ ├── NECollectionViewFlowLayoutAttributes.m
│ ├── NECollectionViewFlowLayoutAnimator.h
│ └── NECollectionViewFlowLayout.h
│ └── NECollectionView
│ ├── NEOptimizeCollectionView.h
│ ├── NEOptimizeCollectionViewLayoutProtocol.h
│ └── NEOptimizeCollectionView.m
├── _Pods.xcodeproj
├── Example
├── Tests
│ ├── en.lproj
│ │ └── InfoPlist.strings
│ ├── Tests-Prefix.pch
│ ├── Tests.m
│ └── Tests-Info.plist
├── NECollectionViewLayout
│ ├── en.lproj
│ │ └── InfoPlist.strings
│ ├── NEAppDelegate.h
│ ├── NEMainTableViewController.h
│ ├── main.m
│ ├── NECollectionViewLayout-Prefix.pch
│ ├── NEMoveCollectionViewController.h
│ ├── NEPinCollectionViewController.h
│ ├── NEDeleteCollectionViewController.h
│ ├── NEInsertCollectionViewController.h
│ ├── NEUpdateCollectionViewController.h
│ ├── NEReorderCollectionViewController.h
│ ├── NEAlignmentCollectionViewController.h
│ ├── NEBackgroundCollectionViewController.h
│ ├── NEScrollDirectionCollectionViewController.h
│ ├── NETextCollectionViewCell.h
│ ├── NEFooterCollectionReusableView.h
│ ├── NEHeaderCollectionReusableView.h
│ ├── NEFooterCollectionReusableView.m
│ ├── NEHeaderCollectionReusableView.m
│ ├── NETextCollectionViewCell.m
│ ├── NECollectionViewLayout-Info.plist
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── Images.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── NEAppDelegate.m
│ ├── NEMainTableViewController.m
│ ├── NEUpdateCollectionViewController.m
│ ├── NEReorderCollectionViewController.m
│ ├── NEMoveCollectionViewController.m
│ ├── NEScrollDirectionCollectionViewController.m
│ ├── NEBackgroundCollectionViewController.m
│ ├── NEInsertCollectionViewController.m
│ ├── NEDeleteCollectionViewController.m
│ ├── NEPinCollectionViewController.m
│ └── NEAlignmentCollectionViewController.m
├── Podfile
├── NECollectionViewLayout.xcodeproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── NECollectionViewLayout-Example.xcscheme
├── NECollectionViewLayout.xcworkspace
│ ├── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
│ └── contents.xcworkspacedata
└── Podfile.lock
├── NECollectionViewLayout.modulemap
├── .gitignore
├── README.md
├── NECollectionViewLayout.podspec
└── LICENSE
/NECollectionViewLayout/Assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/_Pods.xcodeproj:
--------------------------------------------------------------------------------
1 | Example/Pods/Pods.xcodeproj
--------------------------------------------------------------------------------
/Example/Tests/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/NECollectionViewLayout.modulemap:
--------------------------------------------------------------------------------
1 | framework module NECollectionViewLayout {
2 | umbrella header "NECollectionViewLayout.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Tests/Tests-Prefix.pch:
--------------------------------------------------------------------------------
1 | // The contents of this file are implicitly included at the beginning of every test case source file.
2 |
3 | #ifdef __OBJC__
4 |
5 |
6 |
7 | #endif
8 |
--------------------------------------------------------------------------------
/Example/Podfile:
--------------------------------------------------------------------------------
1 |
2 | target 'NECollectionViewLayout_Example' do
3 | pod 'NECollectionViewLayout', :path => '../'
4 |
5 | target 'NECollectionViewLayout_Tests' do
6 | inherit! :search_paths
7 |
8 |
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewLayout.h:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewLayout.h
3 | // Pods
4 | //
5 | // Created by Daniel on 2019/12/10.
6 | //
7 |
8 | #ifndef NECollectionViewLayout_h
9 | #define NECollectionViewLayout_h
10 |
11 | #import "NEOptimizeCollectionView.h"
12 | #import "NECollectionViewFlowLayout.h"
13 |
14 | #endif /* NECollectionViewLayout_h */
15 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEAppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // NEAppDelegate.h
3 | // NECollectionViewLayout
4 | //
5 | // Created by Daniel on 11/28/2019.
6 | // Copyright (c) 2019 Daniel. All rights reserved.
7 | //
8 |
9 | @import UIKit;
10 |
11 | @interface NEAppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/Example/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - NECollectionViewLayout (0.1.0)
3 |
4 | DEPENDENCIES:
5 | - NECollectionViewLayout (from `../`)
6 |
7 | EXTERNAL SOURCES:
8 | NECollectionViewLayout:
9 | :path: "../"
10 |
11 | SPEC CHECKSUMS:
12 | NECollectionViewLayout: c2cb01f051e6197cd2ae219140fffd0e1a5dbe2c
13 |
14 | PODFILE CHECKSUM: abf0dc9a185f1407a4202a96779be45586c81f68
15 |
16 | COCOAPODS: 1.10.0
17 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEMainTableViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // NEMainTableViewController.h
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/2.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface NEMainTableViewController : UITableViewController
14 |
15 | @end
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // NECollectionViewLayout
4 | //
5 | // Created by Daniel on 11/28/2019.
6 | // Copyright (c) 2019 Daniel. All rights reserved.
7 | //
8 |
9 | @import UIKit;
10 | #import "NEAppDelegate.h"
11 |
12 | int main(int argc, char * argv[])
13 | {
14 | @autoreleasepool {
15 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([NEAppDelegate class]));
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NECollectionViewLayout-Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header
3 | //
4 | // The contents of this file are implicitly included at the beginning of every source file.
5 | //
6 |
7 | #import
8 |
9 | #ifndef __IPHONE_5_0
10 | #warning "This project uses features only available in iOS SDK 5.0 and later."
11 | #endif
12 |
13 | #ifdef __OBJC__
14 | @import UIKit;
15 | @import Foundation;
16 | #endif
17 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEMoveCollectionViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // NEMoveCollectionViewController.h
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/9.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface NEMoveCollectionViewController : UICollectionViewController
14 |
15 | @end
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEPinCollectionViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // NEPinCollectionViewController.h
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/9.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface NEPinCollectionViewController : UICollectionViewController
14 |
15 | @end
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEDeleteCollectionViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // NEDeleteCollectionViewController.h
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/9.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface NEDeleteCollectionViewController : UICollectionViewController
14 |
15 | @end
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEInsertCollectionViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // NEInsertCollectionViewController.h
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/9.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface NEInsertCollectionViewController : UICollectionViewController
14 |
15 | @end
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEUpdateCollectionViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // NEUpdateCollectionViewController.h
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/2.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface NEUpdateCollectionViewController : UICollectionViewController
14 |
15 | @end
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEReorderCollectionViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // NEReorderCollectionViewController.h
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/9.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface NEReorderCollectionViewController : UICollectionViewController
14 |
15 | @end
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEAlignmentCollectionViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // NEAlignmentCollectionViewController.h
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/9.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface NEAlignmentCollectionViewController : UICollectionViewController
14 |
15 | @end
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEBackgroundCollectionViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // NEBackgroundCollectionViewController.h
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/5.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface NEBackgroundCollectionViewController : UICollectionViewController
14 |
15 | @end
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEScrollDirectionCollectionViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // NEScrollDirectionCollectionViewController.h
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/5.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface NEScrollDirectionCollectionViewController : UICollectionViewController
14 |
15 | @end
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NETextCollectionViewCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // NETextCollectionViewCell.h
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/4.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface NETextCollectionViewCell : UICollectionViewCell
14 |
15 |
16 | @property (nonatomic, strong) UILabel *textLabel;
17 |
18 | @end
19 |
20 | NS_ASSUME_NONNULL_END
21 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEFooterCollectionReusableView.h:
--------------------------------------------------------------------------------
1 | //
2 | // NEFooterCollectionReusableView.h
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/9.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface NEFooterCollectionReusableView : UICollectionReusableView
14 |
15 | @property (nonatomic, strong) UILabel *textLabel;
16 |
17 | @end
18 |
19 | NS_ASSUME_NONNULL_END
20 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEHeaderCollectionReusableView.h:
--------------------------------------------------------------------------------
1 | //
2 | // NEHeaderCollectionReusableView.h
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/9.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface NEHeaderCollectionReusableView : UICollectionReusableView
14 |
15 | @property (nonatomic, strong) UILabel *textLabel;
16 |
17 | @end
18 |
19 | NS_ASSUME_NONNULL_END
20 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewFlowLayout/NECollectionViewFlowLayout+subhook.h:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewFlowLayout+subhook.h
3 | // CHTCollectionViewWaterfallLayout
4 | //
5 | // Created by Daniel on 2019/12/9.
6 | //
7 |
8 | #import "NECollectionViewFlowLayout.h"
9 | #import "NECollectionViewFlowLayoutCalculator.h"
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface NECollectionViewFlowLayout (subhook)
14 |
15 | - (NE::CollectionViewFlowLayout::Calculator&)layout;
16 | - (CGRect)visibleRect;
17 |
18 | @end
19 |
20 | NS_ASSUME_NONNULL_END
21 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionView/NEOptimizeCollectionView.h:
--------------------------------------------------------------------------------
1 | //
2 | // NEOptimizeCollectionView.h
3 | // CHTCollectionViewWaterfallLayout
4 | //
5 | // Created by Daniel on 2019/12/4.
6 | //
7 |
8 | #import
9 | #import "NEOptimizeCollectionViewLayoutProtocol.h"
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | /// A collection view with optimization feature. To support this feature, the layout MUST
14 | /// conform to NEOptimizeCollectionViewLayoutProtocol, and relayout the changed area.
15 | @interface NEOptimizeCollectionView : UICollectionView
16 |
17 | @end
18 |
19 | NS_ASSUME_NONNULL_END
20 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewFlowLayout/ReuseView/NECollectionViewFlowLayoutSectionContentScrollCollectionReusableView.h:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewFlowLayoutSectionContentScrollCollectionReusableView.h
3 | // CHTCollectionViewWaterfallLayout
4 | //
5 | // Created by Daniel on 2019/12/6.
6 | //
7 |
8 | #import
9 |
10 | NS_ASSUME_NONNULL_BEGIN
11 |
12 | @interface NECollectionViewFlowLayoutSectionContentScrollCollectionReusableView : UICollectionReusableView
13 |
14 | @property (nonatomic, strong) UIScrollView *scrollView;
15 |
16 | @end
17 |
18 | NS_ASSUME_NONNULL_END
19 |
--------------------------------------------------------------------------------
/Example/Tests/Tests.m:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewLayoutTests.m
3 | // NECollectionViewLayoutTests
4 | //
5 | // Created by Daniel on 11/28/2019.
6 | // Copyright (c) 2019 Daniel. All rights reserved.
7 | //
8 |
9 | @import XCTest;
10 |
11 | @interface Tests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation Tests
16 |
17 | - (void)setUp
18 | {
19 | [super setUp];
20 | // Put setup code here. This method is called before the invocation of each test method in the class.
21 | }
22 |
23 | - (void)tearDown
24 | {
25 | // Put teardown code here. This method is called after the invocation of each test method in the class.
26 | [super tearDown];
27 | }
28 |
29 | @end
30 |
31 |
--------------------------------------------------------------------------------
/Example/Tests/Tests-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundlePackageType
14 | BNDL
15 | CFBundleShortVersionString
16 | 1.0
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEFooterCollectionReusableView.m:
--------------------------------------------------------------------------------
1 | //
2 | // NEFooterCollectionReusableView.m
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/9.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import "NEFooterCollectionReusableView.h"
10 |
11 | @implementation NEFooterCollectionReusableView
12 |
13 | - (instancetype)initWithFrame:(CGRect)frame
14 | {
15 | self = [super initWithFrame:frame];
16 | if (self) {
17 | _textLabel = [UILabel new];
18 | _textLabel.textAlignment = NSTextAlignmentCenter;
19 | [self addSubview:_textLabel];
20 | }
21 | return self;
22 | }
23 |
24 | - (void)layoutSubviews {
25 | [super layoutSubviews];
26 | _textLabel.frame = self.bounds;
27 | }
28 |
29 | @end
30 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEHeaderCollectionReusableView.m:
--------------------------------------------------------------------------------
1 | //
2 | // NEHeaderCollectionReusableView.m
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/9.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import "NEHeaderCollectionReusableView.h"
10 |
11 | @implementation NEHeaderCollectionReusableView
12 |
13 | - (instancetype)initWithFrame:(CGRect)frame
14 | {
15 | self = [super initWithFrame:frame];
16 | if (self) {
17 | _textLabel = [UILabel new];
18 | _textLabel.textAlignment = NSTextAlignmentCenter;
19 | [self addSubview:_textLabel];
20 | }
21 | return self;
22 | }
23 |
24 | - (void)layoutSubviews {
25 | [super layoutSubviews];
26 | _textLabel.frame = self.bounds;
27 | }
28 |
29 | @end
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OS X
2 | .DS_Store
3 |
4 | # Xcode
5 | build/
6 | *.pbxuser
7 | !default.pbxuser
8 | *.mode1v3
9 | !default.mode1v3
10 | *.mode2v3
11 | !default.mode2v3
12 | *.perspectivev3
13 | !default.perspectivev3
14 | xcuserdata/
15 | *.xccheckout
16 | profile
17 | *.moved-aside
18 | DerivedData
19 | *.hmap
20 | *.ipa
21 |
22 | # Bundler
23 | .bundle
24 |
25 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
26 | # Carthage/Checkouts
27 |
28 | Carthage/Build
29 |
30 | # We recommend against adding the Pods directory to your .gitignore. However
31 | # you should judge for yourself, the pros and cons are mentioned at:
32 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
33 | #
34 | # Note: if you ignore the Pods directory, make sure to uncomment
35 | # `pod install` in .travis.yml
36 | #
37 | Pods/
38 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewFlowLayout/NECollectionViewFlowLayoutInvalidationContext.h:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewFlowLayoutInvalidationContext.h
3 | // CHTCollectionViewWaterfallLayout
4 | //
5 | // Created by Daniel on 2019/12/3.
6 | //
7 |
8 | #import
9 |
10 | NS_ASSUME_NONNULL_BEGIN
11 |
12 | @interface NECollectionViewFlowLayoutInvalidationContext : UICollectionViewFlowLayoutInvalidationContext
13 |
14 | #pragma mark - Section scroll support
15 |
16 | /// Invalidate the contentOffset of the section
17 | /// @param offset contentOffset of the section
18 | /// @param index the index of section
19 | - (void)invalidateScrollOffset:(CGPoint)offset forSectionAtIndex:(NSInteger)index;
20 |
21 | /// All invalidated contentOffset section infos.
22 | @property (nonatomic, readonly) NSDictionary *invalidatedSectionScrollOffsets;
23 |
24 | @end
25 |
26 | NS_ASSUME_NONNULL_END
27 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionView/NEOptimizeCollectionViewLayoutProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // NEOptimizeCollectionViewLayoutProtocol.h
3 | // CHTCollectionViewWaterfallLayout
4 | //
5 | // Created by Daniel on 2019/12/4.
6 | //
7 |
8 | #import
9 |
10 | NS_ASSUME_NONNULL_BEGIN
11 |
12 | @protocol NEOptimizeCollectionViewLayoutProtocol
13 |
14 | @required
15 | - (void)insertSections:(NSIndexSet *)sections;
16 | - (void)deleteSections:(NSIndexSet *)sections;
17 | - (void)reloadSections:(NSIndexSet *)sections;
18 | - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection;
19 |
20 | - (void)insertItemsAtIndexPaths:(NSArray *)indexPaths;
21 | - (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths;
22 | - (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths;
23 | - (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath;
24 |
25 | @end
26 |
27 | NS_ASSUME_NONNULL_END
28 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewFlowLayout/NECollectionViewFlowLayoutAttributes.h:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewFlowLayoutAttributes.h
3 | // CHTCollectionViewWaterfallLayout
4 | //
5 | // Created by Daniel on 2019/12/6.
6 | //
7 |
8 | #import
9 |
10 | NS_ASSUME_NONNULL_BEGIN
11 |
12 | @protocol NECollectionViewFlowLayoutAttributesDelegate
13 |
14 | - (void)collectionViewFlowLayoutAttributesSectionDidScrollWithContentOffset:(CGPoint)offset atIndexPath:(NSIndexPath *)indexPath;
15 |
16 | @end
17 |
18 | @interface NECollectionViewFlowLayoutAttributes : UICollectionViewLayoutAttributes
19 |
20 | @property (nonatomic, weak) id delegate;
21 | @property (nonatomic, assign) BOOL pageEnable;
22 | @property (nonatomic, assign) CGSize pageSize;
23 | @property (nonatomic, assign) CGSize contentSize;
24 | @property (nonatomic, assign) CGPoint contentOffset;
25 |
26 | @property (nonatomic, assign) BOOL pinned;
27 |
28 | @end
29 |
30 | NS_ASSUME_NONNULL_END
31 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NETextCollectionViewCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // NETextCollectionViewCell.m
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/4.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import "NETextCollectionViewCell.h"
10 |
11 | @implementation NETextCollectionViewCell
12 |
13 | - (instancetype)initWithFrame:(CGRect)frame
14 | {
15 | self = [super initWithFrame:frame];
16 | if (self) {
17 | _textLabel = [UILabel new];
18 | _textLabel.font = [UIFont systemFontOfSize:17];
19 | _textLabel.textAlignment = NSTextAlignmentCenter;
20 | [self.contentView addSubview:_textLabel];
21 | }
22 | return self;
23 | }
24 |
25 | - (void)layoutSubviews {
26 | [super layoutSubviews];
27 |
28 | _textLabel.frame = self.bounds;
29 | }
30 |
31 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
32 | [super touchesBegan:touches withEvent:event];
33 | NSLog(@"%s", sel_getName(_cmd));
34 | }
35 |
36 | @end
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NECollectionViewFlowLayout
2 |
3 | 几乎等价于UICollectionViewFlowLayout,但对其增加了一些额外的能力。
4 |
5 | ## 对齐
6 |
7 | 对齐方式可以根据section来配置了。
8 |
9 | 目前支持的对齐有:(水平方向 / 竖直方向)
10 |
11 | - 居左 / 居顶
12 | - 居右 / 居底
13 | - 居中 / 居中
14 | - 两端对齐
15 |
16 | ## 背景
17 |
18 | section可以单独设置背景view了
19 |
20 | 
21 |
22 | ## Pin
23 |
24 | 目前支持更丰富的Pin能力。并且可以对单独section设置
25 |
26 | - inside section 和系统行为一致,在section内部pin
27 | - before section 在小于等于section的位置永远展示
28 | - after section 在大于等于section的位置永远展示
29 | - always 无论何时,永远展示,类似于某些列表头部的悬停区域
30 |
31 | ## 横向滚动
32 |
33 | 类似于app store的结构。如果以前要做列表内的横向列表,需要在cell上添加列表这样的双层结构才能实现,现改为一个CollectionView来实现该能力。这样:
34 |
35 | - 减少了层次结构,减少复杂度,更符合结构上的分层
36 | - 减少了因多列表产生的offset、性能等问题
37 | - 可以完美接入cell的display事件
38 |
39 | #### Page
40 |
41 | 横向滚动可以启用page功能,会变成分页效果,可以自定义分页大小 pageSize
42 |
43 | ## 性能优化
44 |
45 | 增量更新,系统layout会全量拉取size并计算,这里优化了这种情况。
46 |
47 | 如果要启用增量更新的特性,需要将`UICollectionView`替换为`NEOptimizeCollectionView`。
48 |
49 | 并且使用支持更新协议`NEOptimizeCollectionViewLayoutProtocol`的layout。
50 |
51 | 否则增量更新特性会失效,并回到全量更新策略。
52 |
53 | ## Author
54 |
55 | Daniel, djs66256@163.com
56 |
57 | ## License
58 |
59 | See the LICENSE file for more info.
60 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewFlowLayout/NECollectionViewFlowLayoutInvalidationContext.m:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewFlowLayoutInvalidationContext.m
3 | // CHTCollectionViewWaterfallLayout
4 | //
5 | // Created by Daniel on 2019/12/3.
6 | //
7 |
8 | #import "NECollectionViewFlowLayoutInvalidationContext.h"
9 |
10 | @implementation NECollectionViewFlowLayoutInvalidationContext {
11 | NSMutableDictionary *_invalidatedSectionScrollOffsets;
12 | }
13 |
14 | //- (instancetype)init
15 | //{
16 | // self = [super init];
17 | // if (self) {
18 | // self.invalidateFlowLayoutDelegateMetrics = NO;
19 | // self.invalidateFlowLayoutAttributes = NO;
20 | // }
21 | // return self;
22 | //}
23 |
24 | - (void)invalidateScrollOffset:(CGPoint)offset forSectionAtIndex:(NSInteger)index {
25 | if (_invalidatedSectionScrollOffsets == nil) {
26 | _invalidatedSectionScrollOffsets = [NSMutableDictionary new];
27 | }
28 | _invalidatedSectionScrollOffsets[@(index)] = @(offset);
29 | }
30 |
31 | - (NSDictionary *)invalidatedSectionScrollOffsets {
32 | return _invalidatedSectionScrollOffsets;
33 | }
34 |
35 | @end
36 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewFlowLayout/private/NECollectionViewFlowLayoutCollection.h:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewFlowLayoutCollection.h
3 | // Pods
4 | //
5 | // Created by Daniel on 2019/12/4.
6 | //
7 |
8 | #ifndef NECollectionViewFlowLayoutCollection_h
9 | #define NECollectionViewFlowLayoutCollection_h
10 |
11 | #import
12 |
13 | namespace NE::CollectionViewFlowLayout {
14 |
15 | class LayoutCollection {
16 | public:
17 | LayoutCollection() {
18 | attributes_ = [NSMutableArray arrayWithCapacity:0];
19 | }
20 |
21 | void addItem(UICollectionViewLayoutAttributes *attr) {
22 | [attributes_ addObject:attr];
23 | }
24 |
25 | void addSupplementary(UICollectionViewLayoutAttributes *attr) {
26 | [attributes_ addObject:attr];
27 | }
28 |
29 | void addDecoration(UICollectionViewLayoutAttributes *attr) {
30 | [attributes_ addObject:attr];
31 | }
32 |
33 | NSMutableArray *attributes() {
34 | return attributes_;
35 | }
36 |
37 | private:
38 | __strong NSMutableArray *attributes_ = nil;
39 | };
40 |
41 | }
42 |
43 | #endif /* NECollectionViewFlowLayoutCollection_h */
44 |
--------------------------------------------------------------------------------
/NECollectionViewLayout.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod lib lint NECollectionViewLayout.podspec' to ensure this is a
3 | # valid spec before submitting.
4 | #
5 | # Any lines starting with a # are optional, but their use is encouraged
6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
7 | #
8 |
9 | Pod::Spec.new do |s|
10 | s.name = 'NECollectionViewLayout'
11 | s.version = '0.1.0'
12 | s.summary = 'NECollectionViewLayout.'
13 | s.homepage = 'https://github.com/djs66256/NECollectionViewLayout'
14 | s.license = { :text => 'Apache License 2.0' }
15 | s.author = { 'Daniel' => 'djs66256@163.com' }
16 | s.source = { :git => 'https://github.com/djs66256/NECollectionViewLayout', :tag => s.version.to_s }
17 |
18 | # s.resource_bundles = {
19 | # 'NECollectionViewLayout' => ['NECollectionViewLayout/Assets/**/*']
20 | # }
21 |
22 | s.ios.deployment_target = '9.0'
23 | s.source_files = 'NECollectionViewLayout/Classes/**/*'
24 | # s.private_header_files = 'NECollectionViewLayout/Classes/**/*.h'
25 | s.module_map = 'NECollectionViewLayout.modulemap'
26 |
27 | s.pod_target_xcconfig = {
28 | 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17',
29 | }
30 | s.library = 'c++'
31 | s.frameworks = 'UIKit', 'CoreGraphics'
32 | end
33 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewFlowLayout/NECollectionViewFlowLayoutAnimator.m:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewFlowLayoutAnimator.m
3 | // CHTCollectionViewWaterfallLayout
4 | //
5 | // Created by Daniel on 2019/12/30.
6 | //
7 |
8 | #import "NECollectionViewFlowLayoutAnimator.h"
9 |
10 | @implementation NECollectionViewFlowLayoutScaleAnimator
11 |
12 | - (UICollectionViewLayoutAttributes *)layout:(UICollectionViewLayout *)layout
13 | initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
14 | previousAttributes:(nullable UICollectionViewLayoutAttributes *)prevAttributes {
15 | UICollectionViewLayoutAttributes *attributes = prevAttributes.copy;
16 | attributes.zIndex --;
17 | attributes.alpha = 0.3;
18 | attributes.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.1, 0.1);
19 | return attributes;
20 | }
21 |
22 | - (UICollectionViewLayoutAttributes *)layout:(UICollectionViewLayout *)layout
23 | finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
24 | previousAttributes:(UICollectionViewLayoutAttributes *)prevAttributes {
25 | UICollectionViewLayoutAttributes *attributes = prevAttributes.copy;
26 | attributes.zIndex --;
27 | attributes.alpha = 0.3;
28 | attributes.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.1, 0.1);
29 | return attributes;
30 | }
31 |
32 | @end
33 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewFlowLayout/NECollectionViewFlowLayoutAttributes.m:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewFlowLayoutAttributes.m
3 | // CHTCollectionViewWaterfallLayout
4 | //
5 | // Created by Daniel on 2019/12/6.
6 | //
7 |
8 | #import "NECollectionViewFlowLayoutAttributes.h"
9 |
10 | @implementation NECollectionViewFlowLayoutAttributes
11 |
12 | - (BOOL)isEqual:(id)object {
13 | BOOL b = [super isEqual:object];
14 | if (b && [object isKindOfClass:NECollectionViewFlowLayoutAttributes.class]) {
15 | NECollectionViewFlowLayoutAttributes *attributes = (NECollectionViewFlowLayoutAttributes *)object;
16 | BOOL ret = attributes.delegate == self.delegate
17 | && attributes.pageEnable == self.pageEnable
18 | && CGSizeEqualToSize(attributes.pageSize, self.pageSize)
19 | && CGSizeEqualToSize(attributes.contentSize, self.contentSize)
20 | && CGPointEqualToPoint(attributes.contentOffset, self.contentOffset)
21 | && attributes.pinned == self.pinned;
22 |
23 | return ret;
24 | }
25 | return NO;
26 | }
27 |
28 | - (id)copyWithZone:(NSZone *)zone {
29 | NECollectionViewFlowLayoutAttributes *attributes = [super copyWithZone:zone];
30 | attributes.delegate = self.delegate;
31 | attributes.pageEnable = self.pageEnable;
32 | attributes.pageSize = self.pageSize;
33 | attributes.contentSize = self.contentSize;
34 | attributes.contentOffset = self.contentOffset;
35 | attributes.pinned = self.pinned;
36 | return attributes;
37 | }
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewFlowLayout/private/NECollectionViewFlowLayoutTypes.h:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewFlowLayoutTypes.h
3 | // Pods
4 | //
5 | // Created by Daniel on 2019/12/3.
6 | //
7 |
8 | #ifndef NECollectionViewFlowLayoutTypes_h
9 | #define NECollectionViewFlowLayoutTypes_h
10 |
11 | namespace NE {
12 | namespace CollectionViewFlowLayout {
13 | enum class Alignment {
14 | Leading = NECollectionViewFlowLayoutAlignLeading,
15 | Trailing = NECollectionViewFlowLayoutAlignTrailing,
16 | Center = NECollectionViewFlowLayoutAlignCenter,
17 | SpacingBetween = NECollectionViewFlowLayoutAlignSpacingBetween
18 | };
19 |
20 | inline CGFloat CalculatePositionWithAlignment(Alignment align, CGFloat container, CGFloat value) {
21 | switch (align) {
22 | case Alignment::Trailing:
23 | return container - value;
24 | break;
25 | case Alignment::Center:
26 | return (container - value) / 2;
27 | break;
28 | default:
29 | return 0;
30 | break;
31 | }
32 | }
33 |
34 | enum class PinToVisibleBounds {
35 | None = NECollectionViewFlowLayoutPinToVisibleBoundsNone,
36 | InsideSection = NECollectionViewFlowLayoutPinToVisibleBoundsInsideSection,
37 | AfterSection = NECollectionViewFlowLayoutPinToVisibleBoundsAfterSection,
38 | BeforeSection = NECollectionViewFlowLayoutPinToVisibleBoundsBeforeSection,
39 | Always = NECollectionViewFlowLayoutPinToVisibleBoundsAlways,
40 | };
41 |
42 | enum class ScrollDirection {
43 | Horizontal = UICollectionViewScrollDirectionHorizontal,
44 | Vertical = UICollectionViewScrollDirectionVertical,
45 | };
46 | }
47 | }
48 |
49 | #endif /* NECollectionViewFlowLayoutTypes_h */
50 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NECollectionViewLayout-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | ${PRODUCT_NAME}
9 | CFBundleExecutable
10 | ${EXECUTABLE_NAME}
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | ${PRODUCT_NAME}
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1.0
25 | LSRequiresIPhoneOS
26 |
27 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIMainStoryboardFile
30 | Main
31 | UIRequiredDeviceCapabilities
32 |
33 | armv7
34 |
35 | UISupportedInterfaceOrientations
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationLandscapeLeft
39 | UIInterfaceOrientationLandscapeRight
40 |
41 | UISupportedInterfaceOrientations~ipad
42 |
43 | UIInterfaceOrientationPortrait
44 | UIInterfaceOrientationPortraitUpsideDown
45 | UIInterfaceOrientationLandscapeLeft
46 | UIInterfaceOrientationLandscapeRight
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewFlowLayout/NECollectionViewFlowLayoutAnimator.h:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewFlowLayoutAnimator.h
3 | // CHTCollectionViewWaterfallLayout
4 | //
5 | // Created by Daniel on 2019/12/30.
6 | //
7 |
8 | #import
9 |
10 | NS_ASSUME_NONNULL_BEGIN
11 |
12 | @protocol NECollectionViewFlowLayoutAnimator
13 |
14 | @optional
15 |
16 | - (nullable UICollectionViewLayoutAttributes *)layout:(UICollectionViewLayout *)layout
17 | initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
18 | previousAttributes:(nullable UICollectionViewLayoutAttributes *)prevAttributes;
19 |
20 | - (nullable UICollectionViewLayoutAttributes *)layout:(UICollectionViewLayout *)layout
21 | finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
22 | previousAttributes:(nullable UICollectionViewLayoutAttributes *)prevAttributes;
23 | @end
24 |
25 | @protocol NECollectionViewFlowLayoutElementAnimator
26 |
27 | @optional
28 |
29 | - (nullable UICollectionViewLayoutAttributes *)layout:(UICollectionViewLayout *)layout
30 | initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
31 | elementKind:(NSString *)elementKind
32 | previousAttributes:(nullable UICollectionViewLayoutAttributes *)prevAttributes;
33 |
34 | - (nullable UICollectionViewLayoutAttributes *)layout:(UICollectionViewLayout *)layout
35 | finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
36 | elementKind:(NSString *)elementKind
37 | previousAttributes:(nullable UICollectionViewLayoutAttributes *)prevAttributes;
38 |
39 | @end
40 |
41 | @interface NECollectionViewFlowLayoutScaleAnimator : NSObject
42 |
43 | @end
44 |
45 | NS_ASSUME_NONNULL_END
46 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewFlowLayout/private/NECollectionViewFlowLayoutNode.h:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewFlowLayoutNode.h
3 | // Pods
4 | //
5 | // Created by Daniel on 2019/12/3.
6 | //
7 |
8 | #ifndef NECollectionViewFlowLayoutNode_h
9 | #define NECollectionViewFlowLayoutNode_h
10 |
11 | #import
12 |
13 | namespace NE::CollectionViewFlowLayout {
14 | class Node {
15 | public:
16 | Node() = default;
17 | Node(const CGPoint& origin) : frame_({origin, CGSizeZero}) {}
18 | Node(const Node&) = default;
19 | Node& operator=(const Node&) = default;
20 | virtual ~Node() = default;
21 |
22 | void setOrigin(const CGPoint& origin) { frame_.origin = origin; }
23 | const CGPoint& origin() const { return frame_.origin; }
24 |
25 | void setSize(const CGSize& size) { frame_.size = size; }
26 | const CGSize& size() const { return frame_.size; }
27 |
28 | void setFrame(const CGRect& frame) { frame_ = frame; }
29 | const CGRect& frame() const { return frame_; }
30 |
31 | protected:
32 | CGRect frame_{0};
33 | };
34 |
35 | class Container : public Node {
36 | public:
37 | using Node::Node;
38 | Container(CGPoint origin, CGSize fitSize) : Node(origin), fitSize_(fitSize) {}
39 | Container(const Container&) = default;
40 | Container& operator=(const Container&) = default;
41 |
42 | void setFitSize(CGSize fitSize) { fitSize_ = fitSize; }
43 | const CGSize& fitSize() const { return fitSize_; }
44 |
45 | protected:
46 | CGSize fitSize_{0};
47 |
48 | };
49 |
50 | class Content : public Node {
51 | public:
52 | using Node::Node;
53 | Content(const Content&) = default;
54 | Content& operator=(const Content&) = default;
55 |
56 | void setContentSize(CGSize size) { contentSize_ = size; }
57 | const CGSize& contentSize() const { return contentSize_; }
58 |
59 | protected:
60 | CGSize contentSize_{0};
61 | };
62 | }
63 |
64 | #endif /* NECollectionViewFlowLayoutNode_h */
65 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEAppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // NEAppDelegate.m
3 | // NECollectionViewLayout
4 | //
5 | // Created by Daniel on 11/28/2019.
6 | // Copyright (c) 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import "NEAppDelegate.h"
10 |
11 | @implementation NEAppDelegate
12 |
13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
14 | {
15 | // Override point for customization after application launch.
16 | return YES;
17 | }
18 |
19 | - (void)applicationWillResignActive:(UIApplication *)application
20 | {
21 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
22 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
23 | }
24 |
25 | - (void)applicationDidEnterBackground:(UIApplication *)application
26 | {
27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
29 | }
30 |
31 | - (void)applicationWillEnterForeground:(UIApplication *)application
32 | {
33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | - (void)applicationDidBecomeActive:(UIApplication *)application
37 | {
38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
39 | }
40 |
41 | - (void)applicationWillTerminate:(UIApplication *)application
42 | {
43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
44 | }
45 |
46 | @end
47 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionView/NEOptimizeCollectionView.m:
--------------------------------------------------------------------------------
1 | //
2 | // NEOptimizeCollectionView.m
3 | // CHTCollectionViewWaterfallLayout
4 | //
5 | // Created by Daniel on 2019/12/4.
6 | //
7 |
8 | #import "NEOptimizeCollectionView.h"
9 |
10 | #import "NEOptimizeCollectionViewLayoutProtocol.h"
11 |
12 | @implementation NEOptimizeCollectionView
13 |
14 | - (id)optimizeCollectionViewLayout {
15 | if ([self.collectionViewLayout conformsToProtocol:@protocol(NEOptimizeCollectionViewLayoutProtocol)]) {
16 | return (id)self.collectionViewLayout;
17 | }
18 | else {
19 | return nil;
20 | }
21 | }
22 |
23 | - (void)insertSections:(NSIndexSet *)sections {
24 | [self.optimizeCollectionViewLayout insertSections:sections];
25 | [super insertSections:sections];
26 | }
27 |
28 | - (void)deleteSections:(NSIndexSet *)sections {
29 | [self.optimizeCollectionViewLayout deleteSections:sections];
30 | [super deleteSections:sections];
31 | }
32 |
33 | - (void)reloadSections:(NSIndexSet *)sections {
34 | [self.optimizeCollectionViewLayout reloadSections:sections];
35 | [super reloadSections:sections];
36 | }
37 |
38 | - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection {
39 | [self.optimizeCollectionViewLayout moveSection:section toSection:newSection];
40 | [super moveSection:section toSection:newSection];
41 | }
42 |
43 | - (void)insertItemsAtIndexPaths:(NSArray *)indexPaths {
44 | [self.optimizeCollectionViewLayout insertItemsAtIndexPaths:indexPaths];
45 | [super insertItemsAtIndexPaths:indexPaths];
46 | }
47 |
48 | - (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths {
49 | [self.optimizeCollectionViewLayout deleteItemsAtIndexPaths:indexPaths];
50 | [super deleteItemsAtIndexPaths:indexPaths];
51 | }
52 |
53 | - (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths {
54 | [self.optimizeCollectionViewLayout reloadItemsAtIndexPaths:indexPaths];
55 | [super reloadItemsAtIndexPaths:indexPaths];
56 | }
57 |
58 | - (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath {
59 | [self.optimizeCollectionViewLayout moveItemAtIndexPath:indexPath toIndexPath:newIndexPath];
60 | [super moveItemAtIndexPath:indexPath toIndexPath:newIndexPath];
61 | }
62 |
63 | @end
64 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEMainTableViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // NEMainTableViewController.m
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/2.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import "NEMainTableViewController.h"
10 |
11 | @interface NEMainTableViewController ()
12 |
13 | @property (nonatomic, strong) NSArray *dataSource;
14 |
15 | @end
16 |
17 | @implementation NEMainTableViewController
18 |
19 | - (void)viewDidLoad {
20 | [super viewDidLoad];
21 |
22 | [self.tableView registerClass:UITableViewCell.class forCellReuseIdentifier:@"cell"];
23 | self.dataSource = @[
24 | @{
25 | @"title": @"update",
26 | @"class": @"NEUpdateCollectionViewController"
27 | },
28 | @{
29 | @"title": @"background",
30 | @"class": @"NEBackgroundCollectionViewController"
31 | },
32 | @{
33 | @"title": @"scroll horizontal",
34 | @"class": @"NEScrollDirectionCollectionViewController"
35 | },@{
36 | @"title": @"Alignment",
37 | @"class": @"NEAlignmentCollectionViewController"
38 | },
39 | @{
40 | @"title": @"pin",
41 | @"class": @"NEPinCollectionViewController"
42 | },
43 | @{
44 | @"title": @"insert",
45 | @"class": @"NEInsertCollectionViewController"
46 | },
47 | @{
48 | @"title": @"delete",
49 | @"class": @"NEDeleteCollectionViewController"
50 | },
51 | // TODO:
52 | // @{
53 | // @"title": @"reorder",
54 | // @"class": @"NEReorderCollectionViewController"
55 | // },
56 | @{
57 | @"title": @"Move",
58 | @"class": @"NEMoveCollectionViewController"
59 | },
60 | ];
61 | }
62 |
63 | #pragma mark - Table view data source
64 |
65 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
66 | return 1;
67 | }
68 |
69 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
70 | return self.dataSource.count;
71 | }
72 |
73 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
74 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
75 | cell.textLabel.text = self.dataSource[indexPath.row][@"title"];
76 |
77 | return cell;
78 | }
79 |
80 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
81 | Class cls = NSClassFromString(self.dataSource[indexPath.row][@"class"]);
82 | UIViewController *vc = [[cls alloc] init];
83 | [self.navigationController pushViewController:vc animated:YES];
84 | }
85 |
86 | @end
87 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewFlowLayout/private/NECollectionViewDelegateResponds.h:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewDelegateResponds.h
3 | // Pods
4 | //
5 | // Created by Daniel on 2019/11/28.
6 | //
7 |
8 | #ifndef NECollectionViewDelegateResponds_h
9 | #define NECollectionViewDelegateResponds_h
10 |
11 | #include
12 | #include "NECollectionViewFlowLayoutTypes.h"
13 | #include
14 |
15 | namespace NE {
16 | namespace CollectionViewFlowLayout {
17 |
18 | /// Cache of delegate respondsToSelector
19 | struct UICollectionViewFlowLayoutResponds {
20 | BOOL sizeForItemAtIndexPath = NO;
21 | BOOL insetForSectionAtIndex = NO;
22 | BOOL minimumLineSpacingForSectionAtIndex = NO;
23 | BOOL minimumInteritemSpacingForSectionAtIndex = NO;
24 | BOOL referenceSizeForHeaderInSection = NO;
25 | BOOL referenceSizeForFooterInSection = NO;
26 |
27 | UICollectionViewFlowLayoutResponds(id delegate) :
28 | sizeForItemAtIndexPath([delegate respondsToSelector:@selector(collectionView:layout:sizeForItemAtIndexPath:)]),
29 | insetForSectionAtIndex([delegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)]),
30 | minimumLineSpacingForSectionAtIndex([delegate respondsToSelector:@selector(collectionView:layout:minimumLineSpacingForSectionAtIndex:)]),
31 | minimumInteritemSpacingForSectionAtIndex([delegate respondsToSelector:@selector(collectionView:layout:minimumInteritemSpacingForSectionAtIndex:)]),
32 | referenceSizeForHeaderInSection([delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]),
33 | referenceSizeForFooterInSection([delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)])
34 | { }
35 | };
36 |
37 | struct NECollectionViewFlowLayoutResponds : UICollectionViewFlowLayoutResponds {
38 | public:
39 | NECollectionViewFlowLayoutResponds(id delegate):
40 | UICollectionViewFlowLayoutResponds(delegate),
41 | delegate_(delegate)
42 | {}
43 |
44 | #define RespondsList(L) \
45 | L(additionZIndexForSectionAtIndex) \
46 | \
47 | L(headerPinToVisibleBoundsForSectionAtIndex) \
48 | L(footerPinToVisibleBoundsForSectionAtIndex) \
49 | \
50 | L(backgroundVisibleForSectionAtIndex) \
51 | L(backgroundIncludeSupplementarysForSectionAtIndex) \
52 | L(backgroundInsetsForSectionAtIndex) \
53 | \
54 | L(alignHorizontalForSectionAtIndex) \
55 | L(alignVerticalForSectionAtIndex) \
56 | \
57 | L(scrollDirectionForSectionAtIndex) \
58 | L(pageEnableForSectionAtIndex) \
59 | L(pageSizeForSectionAtIndex) \
60 | L(heightForScrollHorizontalSectionAtIndex)
61 |
62 | #define RespondsGetter(method_name) \
63 | bool method_name() { \
64 | if (method_name ## _) { \
65 | return *method_name ## _; \
66 | } \
67 | else { \
68 | bool r = [delegate_ respondsToSelector:@selector(collectionView:layout:method_name:)]; \
69 | method_name ## _ = r; \
70 | return r; \
71 | } \
72 | } \
73 |
74 | RespondsList(RespondsGetter)
75 |
76 | #undef RespondsGetter
77 |
78 | private:
79 | __weak id delegate_;
80 |
81 | #define RespondsOptional(method_name) std::optional method_name ## _ = std::nullopt;
82 | RespondsList(RespondsOptional)
83 | #undef RespondsOptional
84 |
85 | #undef RespondsList
86 | };
87 | }
88 | }
89 |
90 | #endif /* NECollectionViewDelegateResponds_h */
91 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewFlowLayout/private/NECollectionViewFlowLayoutInvalidation.h:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewFlowLayoutInvalidation.h
3 | // Pods
4 | //
5 | // Created by Daniel on 2019/12/3.
6 | //
7 |
8 | #ifndef NECollectionViewFlowLayoutInvalidation_h
9 | #define NECollectionViewFlowLayoutInvalidation_h
10 |
11 | #include
12 | #include
13 | #include
14 | #import
15 | #include "NECollectionViewLayoutHelpers.h"
16 |
17 | namespace NE::CollectionViewFlowLayout {
18 |
19 | class Invalidation {
20 | public:
21 |
22 | void invalidateEverything() { invalidateEverything_ = true; }
23 | bool isInvalidateEverything() { return invalidateEverything_; }
24 | void invalidateDataSourceCounts() { invalidateDataSourceCounts_ = true; }
25 | bool isInvalidateDataSourceCounts() { return invalidateDataSourceCounts_; }
26 | void invalidateFlowLayoutDelegateMetrics() { invalidateFlowLayoutDelegateMetrics_ = true; }
27 | bool isInvalidateFlowLayoutDelegateMetrics() { return invalidateFlowLayoutDelegateMetrics_; }
28 | void invalidateFlowLayoutAttributes() { invalidateFlowLayoutAttributes_ = true; }
29 | bool isInvalidateFlowLayoutAttributes() { return invalidateFlowLayoutAttributes_; }
30 |
31 | void invalidateItem(const IndexPath indexPath) {
32 | minSection_ = std::min(minSection_, indexPath.section());
33 | invalidateItems_.insert(indexPath);
34 | }
35 |
36 | const auto& invalidateItems() { return invalidateItems_; }
37 |
38 | void invalidateSumplementary(NSString *kind, const IndexPath indexPath) {
39 | minSection_ = std::min(minSection_, indexPath.section());
40 | auto& idxes = invalidateSumplementaries_[kind];
41 | idxes.push_back(indexPath);
42 | }
43 |
44 | const auto& invalidateSumplementaries() const { return invalidateSumplementaries_; }
45 |
46 | void invalidateDecoration(NSString *kind, const IndexPath indexPath) {
47 | minSection_ = std::min(minSection_, indexPath.section());
48 | auto& idxes = invalidateDecorations_[kind];
49 | idxes.push_back(indexPath);
50 | }
51 |
52 | const auto& invalidateDecorations() const { return invalidateDecorations_; }
53 |
54 | void invalidateSectionContentOffset(const NSUInteger index, CGPoint contentOffset) {
55 | minSection_ = std::min(minSection_, index);
56 | invalidateScrollOffsets_[index] = contentOffset;
57 | }
58 | const auto& invalidatedSectionContentOffsets() const { return invalidateScrollOffsets_; }
59 |
60 | NSUInteger minSection() {
61 | return minSection_;
62 | }
63 |
64 | bool hasInvalidateAttributes() {
65 | return minSection_ != NSNotFound;
66 | }
67 |
68 | void reset() {
69 | invalidateEverything_ = false;
70 | invalidateDataSourceCounts_ = false;
71 | invalidateFlowLayoutDelegateMetrics_ = false;
72 | invalidateFlowLayoutAttributes_ = false;
73 | invalidateItems_.clear();
74 | invalidateSumplementaries_.clear();
75 | invalidateDecorations_.clear();
76 | invalidateScrollOffsets_.clear();
77 | minSection_ = NSNotFound;
78 | }
79 | private:
80 | bool invalidateEverything_{false};
81 | bool invalidateDataSourceCounts_{false};
82 | bool invalidateFlowLayoutDelegateMetrics_{false};
83 | bool invalidateFlowLayoutAttributes_{false};
84 | NSUInteger minSection_{NSNotFound};
85 |
86 | std::set invalidateItems_;
87 | std::unordered_map, std::vector> invalidateSumplementaries_;
88 | std::unordered_map, std::vector> invalidateDecorations_;
89 | std::unordered_map invalidateScrollOffsets_;
90 | };
91 |
92 | }
93 |
94 | #endif /* NECollectionViewFlowLayoutInvalidation_h */
95 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout.xcodeproj/xcshareddata/xcschemes/NECollectionViewLayout-Example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
42 |
48 |
49 |
50 |
51 |
52 |
62 |
64 |
70 |
71 |
72 |
73 |
79 |
81 |
87 |
88 |
89 |
90 |
92 |
93 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewFlowLayout/ReuseView/NECollectionViewFlowLayoutSectionContentScrollCollectionReusableView.m:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewFlowLayoutSectionContentScrollCollectionReusableView.m
3 | // CHTCollectionViewWaterfallLayout
4 | //
5 | // Created by Daniel on 2019/12/6.
6 | //
7 |
8 | #import "NECollectionViewFlowLayoutSectionContentScrollCollectionReusableView.h"
9 | #import "NECollectionViewFlowLayoutAttributes.h"
10 |
11 | @interface NECollectionViewFlowLayoutSectionContentScrollCollectionReusableViewScrollView : UIScrollView
12 |
13 | @end
14 |
15 | @implementation NECollectionViewFlowLayoutSectionContentScrollCollectionReusableViewScrollView
16 |
17 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
18 | CGPoint point = [gestureRecognizer locationInView:self];
19 | BOOL contain = CGRectContainsPoint(self.bounds, point);
20 | return !contain;
21 | }
22 |
23 | @end
24 |
25 | @implementation NECollectionViewFlowLayoutSectionContentScrollCollectionReusableView {
26 | CGSize _pageSize;
27 | BOOL _pageEnable;
28 | NSIndexPath *_indexPath;
29 | id _delegate;
30 | }
31 |
32 | - (void)dealloc {
33 | if (self.superview) {
34 | [self.superview removeGestureRecognizer:_scrollView.panGestureRecognizer];
35 | }
36 | }
37 |
38 | - (instancetype)initWithFrame:(CGRect)frame
39 | {
40 | self = [super initWithFrame:frame];
41 | if (self) {
42 | self.userInteractionEnabled = NO;
43 |
44 | _scrollView = [[NECollectionViewFlowLayoutSectionContentScrollCollectionReusableViewScrollView alloc] initWithFrame:(CGRect){ CGPointZero, frame.size }];
45 | _scrollView.scrollsToTop = NO;
46 | _scrollView.backgroundColor = [UIColor clearColor];
47 | _scrollView.alwaysBounceHorizontal = YES;
48 | _scrollView.showsVerticalScrollIndicator = NO;
49 | _scrollView.showsHorizontalScrollIndicator = NO;
50 | _scrollView.directionalLockEnabled = YES;
51 | _scrollView.delegate = self;
52 | _scrollView.decelerationRate = UIScrollViewDecelerationRateFast;
53 | if (@available(iOS 11.0, *)) {
54 | _scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
55 | }
56 | if (@available(iOS 13.0, *)) {
57 | _scrollView.automaticallyAdjustsScrollIndicatorInsets = NO;
58 | }
59 | [self addSubview:_scrollView];
60 | }
61 | return self;
62 | }
63 |
64 | - (void)layoutSubviews {
65 | [super layoutSubviews];
66 | _scrollView.frame = self.bounds;
67 | }
68 |
69 | - (void)didMoveToSuperview {
70 | UIView *superview = self.superview;
71 | if (superview) {
72 | [superview addGestureRecognizer:_scrollView.panGestureRecognizer];
73 | }
74 | else {
75 | [superview removeGestureRecognizer:_scrollView.panGestureRecognizer];
76 | }
77 | }
78 |
79 | - (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes {
80 | NSParameterAssert([layoutAttributes isKindOfClass:NECollectionViewFlowLayoutAttributes.class]);
81 | [super applyLayoutAttributes:layoutAttributes];
82 | _delegate = nil;
83 | NECollectionViewFlowLayoutAttributes *attr = (NECollectionViewFlowLayoutAttributes *)layoutAttributes;
84 | if (fabs(_scrollView.contentSize.width - attr.contentSize.width) > 0.1 ) {
85 | _scrollView.contentSize = attr.contentSize;
86 | }
87 |
88 | if (fabs(_scrollView.contentOffset.x - attr.contentOffset.x) > 0.1 ) {
89 | _scrollView.contentOffset = attr.contentOffset;
90 | }
91 |
92 | _pageEnable = attr.pageEnable;
93 | _pageSize = attr.pageSize;
94 | _indexPath = attr.indexPath;
95 | _delegate = attr.delegate;
96 | }
97 |
98 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
99 | [_delegate collectionViewFlowLayoutAttributesSectionDidScrollWithContentOffset:scrollView.contentOffset atIndexPath:_indexPath];
100 | }
101 |
102 | - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
103 | if (_pageEnable) {
104 | CGSize pageSize = _pageSize.width < 1 ? scrollView.frame.size : _pageSize;
105 | if (velocity.x > 0.1) {
106 | CGFloat pageNum = ceil(scrollView.contentOffset.x / pageSize.width);
107 | targetContentOffset->x = pageSize.width * pageNum;
108 | }
109 | else if (velocity.x < -0.1) {
110 | CGFloat pageNum = floor(scrollView.contentOffset.x / pageSize.width);
111 | targetContentOffset->x = pageSize.width * pageNum;
112 | }
113 | else {
114 | targetContentOffset->x = pageSize.width * round(scrollView.contentOffset.x / pageSize.width);
115 | }
116 | }
117 | }
118 |
119 | @end
120 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewFlowLayout/private/NECollectionViewLayoutHelpers.h:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewLayoutHelpers.h
3 | // Pods
4 | //
5 | // Created by Daniel on 2019/12/3.
6 | //
7 |
8 | #ifndef NECollectionViewLayoutHelpers_h
9 | #define NECollectionViewLayoutHelpers_h
10 |
11 | #include
12 | #include
13 |
14 | namespace NE {
15 |
16 | inline CGPoint operator+ (const CGPoint& left, const UIEdgeInsets& insets) {
17 | return CGPoint({
18 | .x = left.x + insets.left,
19 | .y = left.y + insets.top,
20 | });
21 | }
22 | inline CGPoint operator- (const CGPoint& left, const UIEdgeInsets& insets) {
23 | return CGPoint({
24 | .x = left.x - insets.left,
25 | .y = left.y - insets.top,
26 | });
27 | }
28 | inline CGSize operator+ (const CGSize& left, const UIEdgeInsets& insets) {
29 | return CGSize({
30 | .width = left.width - insets.left - insets.right,
31 | .height = left.height - insets.top - insets.bottom
32 | });
33 | }
34 | inline CGSize operator- (const CGSize& left, const UIEdgeInsets& insets) {
35 | return CGSize({
36 | .width = left.width + insets.left + insets.right,
37 | .height = left.height + insets.top + insets.bottom
38 | });
39 | }
40 |
41 | inline CGRect operator+ (const CGRect& left, const UIEdgeInsets& insets) {
42 | return {
43 | .origin = left.origin + insets,
44 | .size = left.size + insets
45 | };
46 | }
47 | inline CGRect operator- (const CGRect& left, const UIEdgeInsets& insets) {
48 | return {
49 | .origin = left.origin - insets,
50 | .size = left.size - insets
51 | };
52 | }
53 |
54 | inline bool operator== (const CGRect& left, const CGRect& right) {
55 | return CGRectEqualToRect(left, right);
56 | }
57 | inline bool operator!= (const CGRect& left, const CGRect& right) {
58 | return !CGRectEqualToRect(left, right);
59 | }
60 |
61 | inline bool operator== (const CGSize& left, const CGSize& right) {
62 | return CGSizeEqualToSize(left, right);
63 | }
64 | inline bool operator!= (const CGSize& left, const CGSize& right) {
65 | return !CGSizeEqualToSize(left, right);
66 | }
67 |
68 | inline bool operator== (const CGPoint& left, const CGPoint& right) {
69 | return CGPointEqualToPoint(left, right);
70 | }
71 | inline bool operator!= (const CGPoint& left, const CGPoint& right) {
72 | return !CGPointEqualToPoint(left, right);
73 | }
74 |
75 |
76 | inline bool operator == (const UIEdgeInsets& left, const UIEdgeInsets& right) {
77 | return UIEdgeInsetsEqualToEdgeInsets(left, right);
78 | }
79 | inline bool operator != (const UIEdgeInsets& left, const UIEdgeInsets& right) {
80 | return !UIEdgeInsetsEqualToEdgeInsets(left, right);
81 | }
82 |
83 |
84 | class IndexPath : std::array {
85 | private:
86 | using Super = std::array;
87 | public:
88 | IndexPath(NSIndexPath *indexPath) : Super({static_cast([indexPath section]), static_cast([indexPath item])}) {}
89 | IndexPath(NSUInteger section, NSUInteger item) : Super({section, item}) {}
90 | NSUInteger section() const { return at(0); }
91 | void setSection(NSUInteger section) { at(0) = section; }
92 | NSUInteger item() const { return at(1); }
93 | void setItem(NSUInteger item) { at(1) = item; }
94 | operator NSIndexPath *() const {
95 | return [NSIndexPath indexPathForItem:at(1) inSection:at(0)];
96 | }
97 | };
98 |
99 | inline bool operator> (const IndexPath& left, const IndexPath& right) {
100 | if (left.section() > right.section()) return true;
101 | else if (left.section() == right.section()) return left.item() > right.item();
102 | else return false;
103 | }
104 |
105 | inline bool operator>= (const IndexPath& left, const IndexPath& right) {
106 | if (left.section() > right.section()) return true;
107 | else if (left.section() == right.section()) return left.item() >= right.item();
108 | else return false;
109 | }
110 |
111 | inline bool operator< (const IndexPath& left, const IndexPath& right) {
112 | return !operator>=(left, right);
113 | }
114 |
115 | inline bool operator<= (const IndexPath& left, const IndexPath& right) {
116 | return !operator>(left, right);
117 | }
118 |
119 | inline bool operator== (const IndexPath& left, const IndexPath& right) {
120 | return left.section() == right.section() && left.item() == right.item();
121 | }
122 |
123 | inline bool operator!= (const IndexPath& left, const IndexPath& right) {
124 | return !operator==(left, right);
125 | }
126 |
127 | template
128 | struct ObjcRef {
129 | __strong T *value;
130 | ObjcRef(T *val) : value(val) {}
131 | T *operator*() const noexcept { return value; }
132 | operator T*() const noexcept { return value; }
133 | };
134 |
135 | }
136 |
137 | namespace std {
138 |
139 | template
140 | struct hash> {
141 | size_t operator()(const NE::ObjcRef& obj) const noexcept {
142 | return [*obj hash];
143 | }
144 | };
145 |
146 | template
147 | bool operator==(const NE::ObjcRef& left, const NE::ObjcRef& right) noexcept {
148 | return [*left isEqual:*right];
149 | }
150 |
151 | template
152 | bool operator!=(const NE::ObjcRef& left, const NE::ObjcRef& right) noexcept {
153 | return ![*left isEqual:*right];
154 | }
155 |
156 | }
157 |
158 | #endif /* NECollectionViewLayoutHelpers_h */
159 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewFlowLayout/private/NECollectionViewUpdates.h:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewUpdaters.h
3 | // Pods
4 | //
5 | // Created by Daniel on 2019/12/4.
6 | //
7 |
8 | #ifndef NECollectionViewUpdates_h
9 | #define NECollectionViewUpdates_h
10 |
11 | #import
12 | #include
13 | #include
14 | #include
15 | #include "NECollectionViewLayoutHelpers.h"
16 |
17 | namespace NE::CollectionView {
18 |
19 | enum class UpdateAction {
20 | Insert,
21 | Delete,
22 | Reload,
23 | Move,
24 | };
25 |
26 | class UpdateItem {
27 | public:
28 | explicit UpdateItem(UpdateAction action, std::set&& section) :
29 | action_(action), isSection_(true), sections_(std::move(section))
30 | {}
31 |
32 | explicit UpdateItem(UpdateAction action, std::set&& section) :
33 | action_(action), isSection_(false), indexPaths_(std::move(section))
34 | {}
35 |
36 | explicit UpdateItem(UpdateAction action, NSUInteger from, NSUInteger to) :
37 | action_(action), isSection_(true), moveSections_({from, to})
38 | {}
39 |
40 | explicit UpdateItem(UpdateAction action, IndexPath from, IndexPath to) :
41 | action_(action), isSection_(false), moveIndexPaths_({from, to})
42 | {}
43 |
44 | UpdateAction action() const { return action_; }
45 | bool isSection() const { return isSection_; }
46 | const auto& sections() const { return sections_; }
47 | const auto& indexPaths() const { return indexPaths_; }
48 | const auto& moveSections() const { return moveSections_; }
49 | const auto& moveIndexPaths() const { return moveIndexPaths_; }
50 |
51 | private:
52 | UpdateAction action_;
53 | bool isSection_;
54 | std::set sections_;
55 | std::set indexPaths_;
56 | std::pair moveSections_{0, 0};
57 | std::pair moveIndexPaths_{0, 0};
58 | };
59 |
60 | class Updates {
61 | private:
62 | struct SectionCompare {
63 | bool operator()(const IndexPath& i1, const IndexPath& i2) {
64 | return i1.section() < i2.section();
65 | }
66 | };
67 | public:
68 | void insertSections(std::set&& section) {
69 | minSection_ = std::min(minSection_, *std::min_element(section.cbegin(), section.cend()));
70 | updates_.emplace_back(UpdateAction::Insert, std::move(section));
71 | }
72 | void deleteSections(std::set&& section) {
73 | minSection_ = std::min(minSection_, *std::min_element(section.cbegin(), section.cend()));
74 | updates_.emplace_back(UpdateAction::Delete, std::move(section));
75 | }
76 |
77 | void reloadSections(std::set&& section) {
78 | minSection_ = std::min(minSection_, *std::min_element(section.cbegin(), section.cend()));
79 | updates_.emplace_back(UpdateAction::Reload, std::move(section));
80 | }
81 |
82 | void moveSection(NSUInteger from, NSUInteger to) {
83 | minSection_ = std::min(minSection_, std::min(from, to));
84 | updates_.emplace_back(UpdateAction::Move, from, to);
85 | }
86 |
87 | void insertItems(std::set&& indexPaths) {
88 | auto minIndexPath = indexPaths.begin();// std::min_element(indexPaths.cbegin(), indexPaths.cend(), SectionCompare());
89 | minSection_ = std::min(minSection_, minIndexPath->section());
90 | updates_.emplace_back(UpdateAction::Insert, std::move(indexPaths));
91 | }
92 |
93 | void deleteItems(std::set&& indexPaths) {
94 | auto minIndexPath = indexPaths.begin();//std::min_element(indexPaths.cbegin(), indexPaths.cend(), SectionCompare());
95 | minSection_ = std::min(minSection_, minIndexPath->section());
96 | updates_.emplace_back(UpdateAction::Delete, std::move(indexPaths));
97 | }
98 |
99 | void reloadItems(std::set&& indexPaths) {
100 | auto minIndexPath = indexPaths.begin();// std::min_element(indexPaths.cbegin(), indexPaths.cend(), SectionCompare());
101 | minSection_ = std::min(minSection_, minIndexPath->section());
102 | updates_.emplace_back(UpdateAction::Reload, std::move(indexPaths));
103 | }
104 |
105 | void moveItem(IndexPath from, IndexPath to) {
106 | minSection_ = std::min(minSection_, std::min(from.section(), to.section()));
107 | updates_.emplace_back(UpdateAction::Move, from, to);
108 | }
109 |
110 | NSUInteger minSection() const {
111 | return minSection_;
112 | }
113 |
114 | bool hasModified() const {
115 | return minSection_ != NSNotFound;
116 | }
117 |
118 | void reset() {
119 | minSection_ = NSNotFound;
120 | updates_.clear();
121 | }
122 |
123 | const std::vector& updateItems() const {
124 | return updates_;
125 | }
126 |
127 | private:
128 | std::vector updates_;
129 | NSUInteger minSection_{NSNotFound};
130 | };
131 |
132 | }
133 |
134 | #endif /* NECollectionViewUpdates_h */
135 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEUpdateCollectionViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // NEUpdateCollectionViewController.m
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/2.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "NEUpdateCollectionViewController.h"
12 | #import "NETextCollectionViewCell.h"
13 |
14 | @interface NEUpdateCollectionViewController ()
15 |
16 | @property (nonatomic, assign) BOOL expanded;
17 |
18 | @end
19 |
20 | @implementation NEUpdateCollectionViewController
21 |
22 | static NSString * const reuseIdentifier = @"Cell";
23 |
24 | - (instancetype)init
25 | {
26 | NECollectionViewFlowLayout *layout = [NECollectionViewFlowLayout new];
27 | // UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new];
28 | layout.minimumLineSpacing = 10;
29 | layout.minimumInteritemSpacing = 10;
30 | // layout.invalidateFlowLayoutDelegateMetricsWhenUpdates = YES;
31 | // UICollectionViewLayout *layout = [NECollectionViewFlowLayout new];
32 | self = [self initWithCollectionViewLayout:layout];
33 | if (self) {
34 |
35 | }
36 | return self;
37 | }
38 |
39 | - (void)loadView {
40 | self.collectionView = [[NEOptimizeCollectionView alloc] initWithFrame:UIScreen.mainScreen.bounds
41 | collectionViewLayout:self.collectionViewLayout];
42 | self.collectionView.backgroundColor = UIColor.blackColor;
43 |
44 | // self.collectionView = [[UICollectionView alloc] initWithFrame:UIScreen.mainScreen.bounds
45 | // collectionViewLayout:self.collectionViewLayout];
46 | }
47 |
48 | - (void)viewDidLoad {
49 | [super viewDidLoad];
50 |
51 | // Register cell classes
52 | [self.collectionView registerClass:[NETextCollectionViewCell class] forCellWithReuseIdentifier:NSStringFromClass(NETextCollectionViewCell.class)];
53 | [self.collectionView registerClass:[UICollectionReusableView class]
54 | forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
55 | withReuseIdentifier:NSStringFromClass(UICollectionReusableView.class)];
56 | [self.collectionView registerClass:[UICollectionReusableView class]
57 | forSupplementaryViewOfKind:UICollectionElementKindSectionFooter
58 | withReuseIdentifier:NSStringFromClass(UICollectionReusableView.class)];
59 |
60 | }
61 |
62 | #pragma mark
63 |
64 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
65 | return 4;
66 | }
67 |
68 |
69 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
70 | return 100;
71 | }
72 |
73 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
74 | NETextCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass(NETextCollectionViewCell.class) forIndexPath:indexPath];
75 | cell.backgroundColor = [UIColor grayColor];
76 | cell.textLabel.text = @(indexPath.item).stringValue;
77 | return cell;
78 | }
79 |
80 | - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
81 | UICollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:NSStringFromClass(UICollectionReusableView.class) forIndexPath:indexPath];
82 | if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
83 | view.backgroundColor = [UIColor yellowColor];
84 | }
85 | else if ([kind isEqualToString:UICollectionElementKindSectionFooter]) {
86 | view.backgroundColor = [UIColor blueColor];
87 | }
88 | else {
89 | view.backgroundColor = [UIColor redColor];
90 | }
91 | return view;
92 | }
93 |
94 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
95 | return UIEdgeInsetsMake(10, 20, 10, 20);
96 | }
97 |
98 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
99 | return CGSizeMake(collectionView.frame.size.width, 60);
100 | }
101 |
102 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section {
103 | return CGSizeMake(collectionView.frame.size.width, 30);
104 | }
105 |
106 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
107 | return self.expanded ? CGSizeMake(collectionView.frame.size.width / 5, indexPath.item % 3 ? 44 : 88) : CGSizeMake(collectionView.frame.size.width / 3, indexPath.item % 3 ? 44 : 88);
108 | }
109 |
110 | - (NECollectionViewFlowLayoutAlignment)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout alignVerticalForSectionAtIndex:(NSInteger)section {
111 | return section % 4;
112 | }
113 |
114 | - (NECollectionViewFlowLayoutAlignment)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout alignHorizontalForSectionAtIndex:(NSInteger)section {
115 | return section % 4;
116 | }
117 |
118 | - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
119 | self.expanded = !self.expanded;
120 |
121 | // [collectionView reloadItemsAtIndexPaths:@[indexPath]];
122 | // [collectionView reloadData];
123 | [CATransaction begin];
124 | [CATransaction setDisableActions:YES];
125 | CFTimeInterval t1 = CACurrentMediaTime();
126 | [collectionView performBatchUpdates:^{
127 | // __auto_type ctx = [UICollectionViewFlowLayoutInvalidationContext new];
128 | // [ctx invalidateItemsAtIndexPaths:@[indexPath]];
129 | // [collectionView.collectionViewLayout invalidateLayoutWithContext:ctx];
130 | [collectionView reloadItemsAtIndexPaths:@[indexPath]];
131 | } completion:^(BOOL finished) {
132 | CFTimeInterval t2 = CACurrentMediaTime();
133 | NSLog(@"duration = %f", t2 - t1);
134 | }];
135 | [CATransaction commit];
136 | }
137 |
138 |
139 | @end
140 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEReorderCollectionViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // NEReorderCollectionViewController.m
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/9.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "NETextCollectionViewCell.h"
12 | #import "NEHeaderCollectionReusableView.h"
13 | #import "NEFooterCollectionReusableView.h"
14 | #import "NEReorderCollectionViewController.h"
15 |
16 | @interface NEReorderCollectionViewController ()
17 |
18 | @property (nonatomic, strong) NSMutableArray *> *dataSourece;
19 |
20 | @end
21 |
22 | @implementation NEReorderCollectionViewController
23 |
24 | - (instancetype)init
25 | {
26 | NECollectionViewFlowLayout *layout = [NECollectionViewFlowLayout new];
27 | // UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new];
28 | layout.minimumLineSpacing = 10;
29 | layout.minimumInteritemSpacing = 10;
30 | // UICollectionViewLayout *layout = [NECollectionViewFlowLayout new];
31 | self = [self initWithCollectionViewLayout:layout];
32 | if (self) {
33 |
34 | }
35 | return self;
36 | }
37 |
38 | - (void)loadView {
39 | self.collectionView = [[NEOptimizeCollectionView alloc] initWithFrame:UIScreen.mainScreen.bounds
40 | collectionViewLayout:self.collectionViewLayout];
41 |
42 | // self.collectionView = [[UICollectionView alloc] initWithFrame:UIScreen.mainScreen.bounds
43 | // collectionViewLayout:self.collectionViewLayout];
44 | }
45 |
46 | - (void)viewDidLoad {
47 | [super viewDidLoad];
48 |
49 | // Register cell classes
50 | [self.collectionView registerClass:[NETextCollectionViewCell class] forCellWithReuseIdentifier:NSStringFromClass(NETextCollectionViewCell.class)];
51 | [self.collectionView registerClass:[NEHeaderCollectionReusableView class]
52 | forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
53 | withReuseIdentifier:NSStringFromClass(NEHeaderCollectionReusableView.class)];
54 | [self.collectionView registerClass:[NEFooterCollectionReusableView class]
55 | forSupplementaryViewOfKind:UICollectionElementKindSectionFooter
56 | withReuseIdentifier:NSStringFromClass(NEFooterCollectionReusableView.class)];
57 | [self.collectionView registerClass:[UICollectionReusableView class]
58 | forSupplementaryViewOfKind:NECollectionElementKindSectionBackground
59 | withReuseIdentifier:NSStringFromClass(UICollectionReusableView.class)];
60 |
61 | self.dataSourece = [NSMutableArray new];
62 |
63 | for (int i = 0; i < 1; i++) {
64 | NSMutableArray *section = [NSMutableArray new];
65 | for (int r = 0; r < 20; r++) {
66 | [section addObject:@{
67 | @"title" : [NSString stringWithFormat:@"%d-%d", i, r]
68 | }];
69 | }
70 | [self.dataSourece addObject:section];
71 | }
72 |
73 | }
74 |
75 | #pragma mark
76 |
77 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
78 | return self.dataSourece.count;
79 | }
80 |
81 |
82 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
83 | return [self.dataSourece[section] count];
84 | }
85 |
86 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
87 | NETextCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass(NETextCollectionViewCell.class) forIndexPath:indexPath];
88 | cell.backgroundColor = [UIColor grayColor];
89 | cell.textLabel.text = self.dataSourece[indexPath.section][indexPath.item][@"title"];
90 | return cell;
91 | }
92 |
93 | - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
94 | if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
95 | NEHeaderCollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind
96 | withReuseIdentifier:NSStringFromClass(NEHeaderCollectionReusableView.class)
97 | forIndexPath:indexPath];
98 | view.backgroundColor = [UIColor yellowColor];
99 | view.textLabel.text = [NSString stringWithFormat:@"Header - %@", @(indexPath.section).stringValue];
100 | return view;
101 | }
102 | else if ([kind isEqualToString:UICollectionElementKindSectionFooter]) {
103 | NEFooterCollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind
104 | withReuseIdentifier:NSStringFromClass(NEFooterCollectionReusableView.class)
105 | forIndexPath:indexPath];
106 | view.backgroundColor = [UIColor blueColor];
107 | view.textLabel.text = [NSString stringWithFormat:@"Footer - %@", @(indexPath.section).stringValue];
108 | return view;
109 | }
110 | else {
111 | UICollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:NSStringFromClass(UICollectionReusableView.class) forIndexPath:indexPath];
112 | view.backgroundColor = [UIColor colorWithWhite:1 alpha:0.3];
113 | view.clipsToBounds = YES;
114 | view.layer.cornerRadius = 10;
115 | return view;
116 | }
117 | return nil;
118 | }
119 |
120 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
121 | return UIEdgeInsetsMake(20, 20, 20, 20);
122 | }
123 |
124 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
125 | return CGSizeMake(collectionView.frame.size.width, 60);
126 | }
127 |
128 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section {
129 | return CGSizeMake(collectionView.frame.size.width, 30);
130 | }
131 |
132 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
133 | return CGSizeMake(collectionView.frame.size.width / 6, indexPath.item % 3 ? 88 : 44);
134 | }
135 |
136 | - (NECollectionViewFlowLayoutAlignment)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout alignVerticalForSectionAtIndex:(NSInteger)section {
137 | return section % 3;
138 | }
139 |
140 | - (NECollectionViewFlowLayoutAlignment)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout alignHorizontalForSectionAtIndex:(NSInteger)section {
141 | return section % 4;
142 | }
143 |
144 | - (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {
145 | [self.dataSourece.firstObject exchangeObjectAtIndex:sourceIndexPath.item withObjectAtIndex:destinationIndexPath.item];
146 | }
147 |
148 | - (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath {
149 | return YES;
150 | }
151 |
152 | @end
153 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewFlowLayout/private/NECollectionViewFlowLayoutLine.h:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewFlowLayoutLine.h
3 | // Pods
4 | //
5 | // Created by Daniel on 2019/12/3.
6 | //
7 |
8 | #ifndef NECollectionViewFlowLayoutLine_h
9 | #define NECollectionViewFlowLayoutLine_h
10 |
11 | #include
12 | #include "NECollectionViewFlowLayoutItem.h"
13 |
14 | namespace NE::CollectionViewFlowLayout {
15 |
16 | class Line : public Container {
17 | public:
18 | Line(CGRect bounds, CGFloat spacing, Alignment alignHorizontal, Alignment alignVertical)
19 | : Container(bounds.origin, bounds.size), spacing_(spacing), alignHorizontal_(alignHorizontal), alignVertical_(alignVertical) {}
20 |
21 |
22 | bool pushItem(Item* item) {
23 | if (items_.size() == 0) {
24 | lineWidth_ = item->contentSize().width;
25 | lineHeight_ = item->contentSize().height;
26 | items_.push_back(item);
27 | return true;
28 | }
29 | else {
30 | auto newLineWidth = lineWidth_ + item->contentSize().width + spacing_;
31 | if (newLineWidth > fitSize().width) return false;
32 |
33 | lineWidth_ = newLineWidth;
34 | lineHeight_ = std::max(lineHeight_, item->contentSize().height);
35 | items_.push_back(item);
36 | return true;
37 | }
38 | }
39 |
40 | void calculateLayout() {
41 | if (items_.size() == 0) return ;
42 | if (items_.size() == 1) {
43 | //
44 | auto width = std::min(lineWidth_, fitSize().width);
45 | auto x = origin().x + CalculatePositionWithAlignment(alignHorizontal_, fitSize().width, width);
46 | auto item = items_[0];
47 | item->setFrame({
48 | .origin = {
49 | .x = x,
50 | .y = origin().y,
51 | },
52 | .size = {
53 | .width = width,
54 | .height = item->contentSize().height
55 | }
56 | });
57 | }
58 | else {
59 | auto origin = this->origin();
60 | origin.x += CalculatePositionWithAlignment(alignHorizontal_, fitSize().width, lineWidth_);
61 | auto spacing = spacing_;
62 | if (alignHorizontal_ == Alignment::SpacingBetween) {
63 | auto itemsWidth = 0.;
64 | for (auto item : items_) {
65 | itemsWidth += item->contentSize().width;
66 | }
67 | spacing = (fitSize().width - itemsWidth) / (items_.size() - 1);
68 | }
69 | for (auto item : items_) {
70 | item->setFrame({
71 | .origin = {
72 | .x = origin.x,
73 | .y = origin.y + CalculatePositionWithAlignment(alignVertical_, lineHeight_, item->contentSize().height),
74 | },
75 | .size = item->contentSize()
76 | });
77 | origin.x += item->contentSize().width + spacing;
78 | }
79 | }
80 | }
81 |
82 | void newLine(CGFloat lineSpacing) {
83 | setOrigin({ origin().x, lineSpacing + origin().y + lineHeight_ });
84 |
85 | items_.clear();
86 | lineWidth_ = lineHeight_ = 0;
87 | }
88 |
89 | CGFloat lineWidth() { return lineWidth_; }
90 | CGFloat lineHeight() { return lineHeight_; }
91 |
92 | ~Line() override {}
93 | private:
94 | CGFloat spacing_{0};
95 | Alignment alignHorizontal_ = Alignment::Center;
96 | Alignment alignVertical_ = Alignment::Center;
97 | std::vector- items_;
98 |
99 | CGFloat lineWidth_{0};
100 | CGFloat lineHeight_{0};
101 |
102 | Line(Line&) = delete;
103 | Line& operator=(Line&) = delete;
104 | }; // END Line
105 |
106 | class Column : public Container {
107 | public:
108 | Column(CGRect bounds, CGFloat spacing, Alignment alignHorizontal, Alignment alignVertical)
109 | : Container(bounds.origin, bounds.size), spacing_(spacing), alignHorizontal_(alignHorizontal), alignVertical_(alignVertical) {}
110 |
111 |
112 | bool pushItem(Item* item) {
113 | if (items_.size() == 0) {
114 | columnWidth_ = item->contentSize().width;
115 | columnHeight_ = item->contentSize().height;
116 | items_.push_back(item);
117 | return true;
118 | }
119 | else {
120 | auto newColumnHeight = columnHeight_ + item->contentSize().height + spacing_;
121 | if (newColumnHeight > fitSize().height) return false;
122 |
123 | columnHeight_ = newColumnHeight;
124 | columnWidth_ = std::max(columnWidth_, item->contentSize().width);
125 | items_.push_back(item);
126 | return true;
127 | }
128 | }
129 |
130 | void calculateLayout() {
131 | if (items_.size() == 0) return ;
132 | if (items_.size() == 1) {
133 | //
134 | auto y = origin().y + CalculatePositionWithAlignment(alignVertical_, fitSize().height, columnHeight_);
135 | auto item = items_[0];
136 | item->setFrame({
137 | .origin = {
138 | .x = origin().x,
139 | .y = y,
140 | },
141 | .size = item->contentSize()
142 | });
143 | }
144 | else {
145 | auto origin = this->origin();
146 | origin.y += CalculatePositionWithAlignment(alignVertical_, fitSize().height, columnHeight_);
147 | auto spacing = spacing_;
148 | if (alignVertical_ == Alignment::SpacingBetween) {
149 | auto itemsHeight = 0.;
150 | for (auto item : items_) {
151 | itemsHeight += item->contentSize().height;
152 | }
153 | spacing = (fitSize().height - itemsHeight) / (items_.size() - 1);
154 | }
155 | for (auto item : items_) {
156 | item->setFrame({
157 | .origin = {
158 | .x = origin.x + CalculatePositionWithAlignment(alignHorizontal_, columnWidth_, item->contentSize().width),
159 | .y = origin.y,
160 | },
161 | .size = item->contentSize()
162 | });
163 | origin.y += item->contentSize().height + spacing;
164 | }
165 | }
166 | }
167 |
168 | void newColumn(CGFloat lineSpacing) {
169 | setOrigin({ origin().x + lineSpacing + columnWidth_, origin().y});
170 |
171 | items_.clear();
172 | columnWidth_ = columnHeight_ = 0;
173 | }
174 |
175 | CGFloat columnWidth() { return columnWidth_; }
176 | CGFloat columnHeight() { return columnHeight_; }
177 |
178 | ~Column() override {}
179 | private:
180 | CGFloat spacing_{0};
181 | Alignment alignHorizontal_ = Alignment::Center;
182 | Alignment alignVertical_ = Alignment::Center;
183 | std::vector
- items_;
184 |
185 | CGFloat columnWidth_{0};
186 | CGFloat columnHeight_{0};
187 |
188 | Column(Column&) = delete;
189 | Column& operator=(Column&) = delete;
190 | };
191 |
192 | }
193 |
194 | #endif /* NECollectionViewFlowLayoutLine_h */
195 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewFlowLayout/private/NECollectionViewFlowLayoutContext.h:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewFlowLayoutContext.h
3 | // Pods
4 | //
5 | // Created by Daniel on 2019/12/3.
6 | //
7 |
8 | #ifndef NECollectionViewFlowLayoutContext_h
9 | #define NECollectionViewFlowLayoutContext_h
10 |
11 | #include "NECollectionViewDelegateResponds.h"
12 | #import "NECollectionViewFlowLayout.h"
13 | #import "NECollectionViewFlowLayoutAttributes.h"
14 |
15 | namespace NE {
16 | namespace CollectionViewFlowLayout {
17 | class Context {
18 | public:
19 | Context(){}
20 | Context(NECollectionViewFlowLayout *layout) :
21 | layout_(layout),
22 | attributesClass_([[layout class] layoutAttributesClass]),
23 | delegate_(static_cast>(layout.collectionView.delegate)),
24 | collectionView_(layout.collectionView),
25 | responds_(layout.collectionView.delegate) {}
26 |
27 | NSInteger numberOfSections() {
28 | return collectionView_.numberOfSections;
29 | }
30 |
31 | NSInteger numberOfItemsInSection(NSInteger section) {
32 | return [collectionView_ numberOfItemsInSection:section];
33 | }
34 |
35 | CGSize sizeForItemAtIndexPath(NSIndexPath *indexPath) {
36 | if (responds_.sizeForItemAtIndexPath) {
37 | return [delegate_ collectionView:collectionView_ layout:layout_ sizeForItemAtIndexPath:indexPath];
38 | }
39 | else {
40 | return layout_.itemSize;
41 | }
42 | }
43 |
44 | UIEdgeInsets insetForSectionAtIndex(NSInteger section) {
45 | if (responds_.insetForSectionAtIndex) {
46 | return [delegate_ collectionView:collectionView_ layout:layout_ insetForSectionAtIndex:section];
47 | }
48 | else {
49 | return layout_.sectionInset;
50 | }
51 | }
52 |
53 | CGFloat minimumLineSpacingForSectionAtIndex(NSInteger section) {
54 | if (responds_.minimumLineSpacingForSectionAtIndex) {
55 | return [delegate_ collectionView:collectionView_ layout:layout_ minimumLineSpacingForSectionAtIndex:section];
56 | }
57 | else {
58 | return layout_.minimumLineSpacing;
59 | }
60 | }
61 |
62 | CGFloat minimumInteritemSpacingForSectionAtIndex(NSInteger section) {
63 | if (responds_.minimumInteritemSpacingForSectionAtIndex) {
64 | return [delegate_ collectionView:collectionView_ layout:layout_ minimumInteritemSpacingForSectionAtIndex:section];
65 | }
66 | else {
67 | return layout_.minimumInteritemSpacing;
68 | }
69 | }
70 |
71 | CGSize referenceSizeForHeaderInSection(NSInteger section) {
72 | if (responds_.referenceSizeForHeaderInSection) {
73 | return [delegate_ collectionView:collectionView_ layout:layout_ referenceSizeForHeaderInSection:section];
74 | }
75 | else {
76 | return layout_.headerReferenceSize;
77 | }
78 | }
79 |
80 | CGSize referenceSizeForFooterInSection(NSInteger section) {
81 | if (responds_.referenceSizeForFooterInSection) {
82 | return [delegate_ collectionView:collectionView_ layout:layout_ referenceSizeForFooterInSection:section];
83 | }
84 | else {
85 | return layout_.footerReferenceSize;
86 | }
87 | }
88 |
89 | UICollectionViewScrollDirection sectionScrollDirection() {
90 | return layout_.sectionScrollDirection;
91 | }
92 |
93 | bool isCustomSectionWidth() {
94 | return layout_.sectionWidth > 1;
95 | }
96 |
97 | CGFloat sectionWidth() {
98 | return layout_.sectionWidth;
99 | }
100 |
101 | CGFloat sectionSpacing() {
102 | return layout_.sectionSpacing;
103 | }
104 |
105 | #define PropertyList(L) \
106 | L(NSInteger, additionZIndexForSectionAtIndex, 0) \
107 | \
108 | L(bool, backgroundVisibleForSectionAtIndex, false) \
109 | L(bool, backgroundIncludeSupplementarysForSectionAtIndex, false) \
110 | L(UIEdgeInsets, backgroundInsetsForSectionAtIndex, UIEdgeInsetsZero) \
111 | \
112 | L(Alignment, alignHorizontalForSectionAtIndex, static_cast(layout_.alignHorizontal)) \
113 | L(Alignment, alignVerticalForSectionAtIndex, static_cast(layout_.alignVertical)) \
114 | \
115 | L(ScrollDirection, scrollDirectionForSectionAtIndex, ScrollDirection::Vertical) \
116 | L(bool, pageEnableForSectionAtIndex, false) \
117 | L(CGSize, pageSizeForSectionAtIndex, CGSizeZero) \
118 | L(CGFloat, heightForScrollHorizontalSectionAtIndex, 0.)
119 |
120 | #define PropertyGetter(Type, method_name, default_value) \
121 | Type method_name(NSUInteger section) {\
122 | if (responds_.method_name()) {\
123 | return static_cast([delegate_ collectionView:collectionView_ layout:layout_ method_name:section]);\
124 | }\
125 | return default_value;\
126 | }\
127 |
128 | PropertyList(PropertyGetter)
129 |
130 | #undef PropertyGetter
131 | #undef PropertyList
132 |
133 | PinToVisibleBounds headerPinToVisibleBoundsForSectionAtIndex(NSUInteger section) {
134 | if (pinToVisibleBoundsEnable() && responds_.headerPinToVisibleBoundsForSectionAtIndex()) {
135 | return static_cast([delegate_ collectionView:collectionView_
136 | layout:layout_
137 | headerPinToVisibleBoundsForSectionAtIndex:section]);
138 | }
139 | return PinToVisibleBounds::None;
140 | }
141 |
142 | PinToVisibleBounds footerPinToVisibleBoundsForSectionAtIndex(NSUInteger section) {
143 | if (pinToVisibleBoundsEnable() && responds_.footerPinToVisibleBoundsForSectionAtIndex()) {
144 | return static_cast([delegate_ collectionView:collectionView_
145 | layout:layout_
146 | footerPinToVisibleBoundsForSectionAtIndex:section]);
147 | }
148 | return PinToVisibleBounds::None;
149 | }
150 |
151 | NECollectionViewFlowLayoutAttributes *cellAttributes(NSIndexPath *indexPath) {
152 | NECollectionViewFlowLayoutAttributes* attr = [[[layout_ class] layoutAttributesClass] layoutAttributesForCellWithIndexPath:indexPath];
153 | attr.delegate = (id)layout_;
154 | return attr;
155 | }
156 | NECollectionViewFlowLayoutAttributes *supplementaryAttributes(NSString *kind, NSIndexPath *indexPath) {
157 | NECollectionViewFlowLayoutAttributes* attr = [[[layout_ class] layoutAttributesClass] layoutAttributesForSupplementaryViewOfKind:kind withIndexPath:indexPath];
158 | attr.delegate = (id)layout_;
159 | return attr;
160 | }
161 | NECollectionViewFlowLayoutAttributes *decorationAttributes(NSString *kind, NSIndexPath *indexPath) {
162 | NECollectionViewFlowLayoutAttributes* attr = [[[layout_ class] layoutAttributesClass] layoutAttributesForDecorationViewOfKind:kind withIndexPath:indexPath];
163 | attr.delegate = (id)layout_;
164 | return attr;
165 | }
166 |
167 |
168 | private:
169 | __weak NECollectionViewFlowLayout *layout_ = nil;
170 | __weak id delegate_ = nil;
171 | __weak UICollectionView *collectionView_ = nil;
172 | NECollectionViewFlowLayoutResponds responds_ = nil;
173 | Class attributesClass_ = Nil;
174 |
175 | bool pinToVisibleBoundsEnable() { return layout_.pinToVisibleBoundsEnable; }
176 | };
177 | }
178 | }
179 |
180 | #endif /* NECollectionViewFlowLayoutContext_h */
181 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEMoveCollectionViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // NEMoveCollectionViewController.m
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/9.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "NETextCollectionViewCell.h"
12 | #import "NEHeaderCollectionReusableView.h"
13 | #import "NEFooterCollectionReusableView.h"
14 | #import "NEMoveCollectionViewController.h"
15 |
16 | @interface NEMoveCollectionViewController ()
17 |
18 | @property (nonatomic, strong) NSMutableArray *> *dataSourece;
19 |
20 | @end
21 |
22 | @implementation NEMoveCollectionViewController
23 |
24 | - (instancetype)init
25 | {
26 | NECollectionViewFlowLayout *layout = [NECollectionViewFlowLayout new];
27 | layout.pinToVisibleBoundsEnable = YES;
28 | // UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new];
29 | layout.minimumLineSpacing = 10;
30 | layout.minimumInteritemSpacing = 10;
31 | // UICollectionViewLayout *layout = [NECollectionViewFlowLayout new];
32 | self = [self initWithCollectionViewLayout:layout];
33 | if (self) {
34 |
35 | }
36 | return self;
37 | }
38 |
39 | - (void)loadView {
40 | self.collectionView = [[NEOptimizeCollectionView alloc] initWithFrame:UIScreen.mainScreen.bounds
41 | collectionViewLayout:self.collectionViewLayout];
42 | self.collectionView.backgroundColor = UIColor.blackColor;
43 |
44 | // self.collectionView = [[UICollectionView alloc] initWithFrame:UIScreen.mainScreen.bounds
45 | // collectionViewLayout:self.collectionViewLayout];
46 | }
47 |
48 | - (void)viewDidLoad {
49 | [super viewDidLoad];
50 |
51 | __auto_type moveItem = [[UIBarButtonItem alloc] initWithTitle:@"item" style:UIBarButtonItemStylePlain target:self action:@selector(moveItem)];
52 | __auto_type moveSection = [[UIBarButtonItem alloc] initWithTitle:@"section" style:UIBarButtonItemStylePlain target:self action:@selector(moveSection)];
53 | self.navigationItem.rightBarButtonItems = @[moveSection, moveItem];
54 |
55 | // Register cell classes
56 | [self.collectionView registerClass:[NETextCollectionViewCell class] forCellWithReuseIdentifier:NSStringFromClass(NETextCollectionViewCell.class)];
57 | [self.collectionView registerClass:[NEHeaderCollectionReusableView class]
58 | forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
59 | withReuseIdentifier:NSStringFromClass(NEHeaderCollectionReusableView.class)];
60 | [self.collectionView registerClass:[NEFooterCollectionReusableView class]
61 | forSupplementaryViewOfKind:UICollectionElementKindSectionFooter
62 | withReuseIdentifier:NSStringFromClass(NEFooterCollectionReusableView.class)];
63 | [self.collectionView registerClass:[UICollectionReusableView class]
64 | forSupplementaryViewOfKind:NECollectionElementKindSectionBackground
65 | withReuseIdentifier:NSStringFromClass(UICollectionReusableView.class)];
66 |
67 | self.dataSourece = [NSMutableArray new];
68 |
69 | for (int i = 0; i < 3; i++) {
70 | NSMutableArray *section = [NSMutableArray new];
71 | for (int r = 0; r < 10; r++) {
72 | [section addObject:@{
73 | @"title" : [NSString stringWithFormat:@"%d-%d", i, r]
74 | }];
75 | }
76 | [self.dataSourece addObject:section];
77 | }
78 |
79 | }
80 |
81 | #pragma mark
82 |
83 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
84 | return self.dataSourece.count;
85 | }
86 |
87 |
88 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
89 | return [self.dataSourece[section] count];
90 | }
91 |
92 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
93 | NETextCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass(NETextCollectionViewCell.class) forIndexPath:indexPath];
94 | cell.backgroundColor = [UIColor grayColor];
95 | cell.textLabel.text = self.dataSourece[indexPath.section][indexPath.item][@"title"];
96 | return cell;
97 | }
98 |
99 | - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
100 | if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
101 | NEHeaderCollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind
102 | withReuseIdentifier:NSStringFromClass(NEHeaderCollectionReusableView.class)
103 | forIndexPath:indexPath];
104 | view.backgroundColor = [UIColor yellowColor];
105 | view.textLabel.text = [NSString stringWithFormat:@"Header - %@", @(indexPath.section).stringValue];
106 | return view;
107 | }
108 | else if ([kind isEqualToString:UICollectionElementKindSectionFooter]) {
109 | NEFooterCollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind
110 | withReuseIdentifier:NSStringFromClass(NEFooterCollectionReusableView.class)
111 | forIndexPath:indexPath];
112 | view.backgroundColor = [UIColor blueColor];
113 | view.textLabel.text = [NSString stringWithFormat:@"Footer - %@", @(indexPath.section).stringValue];
114 | return view;
115 | }
116 | else {
117 | UICollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:NSStringFromClass(UICollectionReusableView.class) forIndexPath:indexPath];
118 | view.backgroundColor = [UIColor colorWithWhite:1 alpha:0.3];
119 | view.clipsToBounds = YES;
120 | view.layer.cornerRadius = 10;
121 | return view;
122 | }
123 | return nil;
124 | }
125 |
126 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
127 | return UIEdgeInsetsMake(20, 20, 20, 20);
128 | }
129 |
130 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
131 | return CGSizeMake(collectionView.frame.size.width, 60);
132 | }
133 |
134 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section {
135 | return CGSizeMake(collectionView.frame.size.width, 30);
136 | }
137 |
138 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
139 | return CGSizeMake(collectionView.frame.size.width / 6, 88);
140 | }
141 |
142 | - (NECollectionViewFlowLayoutAlignment)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout alignVerticalForSectionAtIndex:(NSInteger)section {
143 | return section % 3;
144 | }
145 |
146 | - (NECollectionViewFlowLayoutAlignment)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout alignHorizontalForSectionAtIndex:(NSInteger)section {
147 | return section % 4;
148 | }
149 |
150 | - (void)moveItem {
151 | __auto_type section = self.dataSourece.firstObject;
152 | __auto_type obj = [section objectAtIndex:0];
153 | [section removeObjectAtIndex:0];
154 | [section addObject:obj];
155 | [self.collectionView moveItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]
156 | toIndexPath:[NSIndexPath indexPathForItem:section.count - 1 inSection:0]];
157 | }
158 |
159 | - (void)moveSection {
160 | __auto_type obj = self.dataSourece[0];
161 | [self.dataSourece removeObjectAtIndex:0];
162 | [self.dataSourece addObject:obj];
163 | [self.collectionView moveSection:0 toSection:self.dataSourece.count - 1];
164 | }
165 |
166 | @end
167 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEScrollDirectionCollectionViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // NEScrollDirectionCollectionViewController.m
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/5.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "NETextCollectionViewCell.h"
12 | #import "NEHeaderCollectionReusableView.h"
13 | #import "NEFooterCollectionReusableView.h"
14 | #import "NEScrollDirectionCollectionViewController.h"
15 |
16 | @interface NEScrollDirectionCollectionViewController ()
17 |
18 | @property (nonatomic, assign) NSInteger sections;
19 |
20 | @end
21 |
22 | @implementation NEScrollDirectionCollectionViewController
23 |
24 | - (instancetype)init
25 | {
26 | NECollectionViewFlowLayout *layout = [NECollectionViewFlowLayout new];
27 | // UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new];
28 | layout.minimumLineSpacing = 10;
29 | layout.minimumInteritemSpacing = 10;
30 | // UICollectionViewLayout *layout = [NECollectionViewFlowLayout new];
31 | self = [self initWithCollectionViewLayout:layout];
32 | if (self) {
33 | _sections = 4;
34 | }
35 | return self;
36 | }
37 |
38 | - (void)deleteSection {
39 | self.sections --;
40 | [self.collectionView deleteSections:[NSIndexSet indexSetWithIndex:self.sections]];
41 | }
42 |
43 | - (void)loadView {
44 | self.collectionView = [[NEOptimizeCollectionView alloc] initWithFrame:UIScreen.mainScreen.bounds
45 | collectionViewLayout:self.collectionViewLayout];
46 | self.collectionView.backgroundColor = UIColor.blackColor;
47 |
48 | // self.collectionView = [[UICollectionView alloc] initWithFrame:UIScreen.mainScreen.bounds
49 | // collectionViewLayout:self.collectionViewLayout];
50 | }
51 |
52 | - (void)viewDidLoad {
53 | [super viewDidLoad];
54 |
55 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"delete" style:UIBarButtonItemStylePlain target:self action:@selector(deleteSection)];
56 |
57 | // Register cell classes
58 | [self.collectionView registerClass:[NETextCollectionViewCell class] forCellWithReuseIdentifier:NSStringFromClass(NETextCollectionViewCell.class)];
59 | [self.collectionView registerClass:[NEHeaderCollectionReusableView class]
60 | forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
61 | withReuseIdentifier:NSStringFromClass(NEHeaderCollectionReusableView.class)];
62 | [self.collectionView registerClass:[NEFooterCollectionReusableView class]
63 | forSupplementaryViewOfKind:UICollectionElementKindSectionFooter
64 | withReuseIdentifier:NSStringFromClass(NEFooterCollectionReusableView.class)];
65 |
66 | }
67 |
68 | #pragma mark
69 |
70 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
71 | return self.sections;
72 | }
73 |
74 |
75 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
76 | return 20;
77 | }
78 |
79 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
80 | NETextCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass(NETextCollectionViewCell.class) forIndexPath:indexPath];
81 | cell.backgroundColor = [UIColor grayColor];
82 | cell.textLabel.text = [NSString stringWithFormat:@"%@-%@", @(indexPath.section).stringValue, @(indexPath.item).stringValue];
83 | return cell;
84 | }
85 |
86 | - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
87 | if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
88 | NEHeaderCollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind
89 | withReuseIdentifier:NSStringFromClass(NEHeaderCollectionReusableView.class)
90 | forIndexPath:indexPath];
91 | view.backgroundColor = [UIColor yellowColor];
92 | view.textLabel.text = [NSString stringWithFormat:@"Header - %@", @(indexPath.section).stringValue];
93 | return view;
94 | }
95 | else if ([kind isEqualToString:UICollectionElementKindSectionFooter]) {
96 | NEFooterCollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind
97 | withReuseIdentifier:NSStringFromClass(NEFooterCollectionReusableView.class)
98 | forIndexPath:indexPath];
99 | view.backgroundColor = [UIColor blueColor];
100 | view.textLabel.text = [NSString stringWithFormat:@"Footer - %@", @(indexPath.section).stringValue];
101 | return view;
102 | }
103 | return nil;
104 | }
105 |
106 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
107 | return UIEdgeInsetsMake(10, 15, 10, 15);
108 | }
109 |
110 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
111 | return CGSizeMake(collectionView.frame.size.width, 60);
112 | }
113 |
114 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section {
115 | return CGSizeMake(collectionView.frame.size.width, 30);
116 | }
117 |
118 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
119 | return CGSizeMake(collectionView.frame.size.width / 5, indexPath.item % 3 ? 44 : 88);
120 | }
121 |
122 | - (NECollectionViewFlowLayoutAlignment)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout alignVerticalForSectionAtIndex:(NSInteger)section {
123 | return section % 4;
124 | }
125 |
126 | - (NECollectionViewFlowLayoutAlignment)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout alignHorizontalForSectionAtIndex:(NSInteger)section {
127 | return section % 4;
128 | }
129 |
130 | #pragma mark - Scroll direction delegate & page enable & page size
131 |
132 | - (UICollectionViewScrollDirection)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout scrollDirectionForSectionAtIndex:(NSInteger)section {
133 | return UICollectionViewScrollDirectionHorizontal;
134 | }
135 |
136 | - (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout pageEnableForSectionAtIndex:(NSInteger)section {
137 | return YES;
138 | }
139 |
140 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout pageSizeForSectionAtIndex:(NSInteger)section {
141 | return CGSizeMake(collectionView.frame.size.width * 0.8, 1);
142 | }
143 |
144 | - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout heightForScrollHorizontalSectionAtIndex:(NSInteger)section {
145 | return 44 * 3 + 10 + 10;
146 | }
147 |
148 | - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
149 |
150 |
151 | // [collectionView reloadItemsAtIndexPaths:@[indexPath]];
152 | // [collectionView reloadData];
153 | [CATransaction begin];
154 | [CATransaction setDisableActions:YES];
155 | CFTimeInterval t1 = CACurrentMediaTime();
156 | [collectionView performBatchUpdates:^{
157 | // __auto_type ctx = [UICollectionViewFlowLayoutInvalidationContext new];
158 | // [ctx invalidateItemsAtIndexPaths:@[indexPath]];
159 | // [collectionView.collectionViewLayout invalidateLayoutWithContext:ctx];
160 | [collectionView reloadItemsAtIndexPaths:@[indexPath]];
161 | } completion:^(BOOL finished) {
162 | CFTimeInterval t2 = CACurrentMediaTime();
163 | NSLog(@"duration = %f", t2 - t1);
164 | }];
165 | [CATransaction commit];
166 | }
167 |
168 |
169 | @end
170 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEBackgroundCollectionViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // NEBackgroundCollectionViewController.m
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/5.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "NETextCollectionViewCell.h"
12 | #import "NEHeaderCollectionReusableView.h"
13 | #import "NEFooterCollectionReusableView.h"
14 | #import "NEBackgroundCollectionViewController.h"
15 |
16 | @interface NEBackgroundCollectionViewController ()
17 |
18 | @end
19 |
20 | @implementation NEBackgroundCollectionViewController
21 |
22 | - (instancetype)init
23 | {
24 | NECollectionViewFlowLayout *layout = [NECollectionViewFlowLayout new];
25 | // UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new];
26 | layout.minimumLineSpacing = 10;
27 | layout.minimumInteritemSpacing = 10;
28 | // UICollectionViewLayout *layout = [NECollectionViewFlowLayout new];
29 | // layout.sectionScrollDirection = UICollectionViewScrollDirectionHorizontal;
30 | // layout.sectionWidth = 300;
31 | layout.sectionSpacing = 30;
32 | self = [self initWithCollectionViewLayout:layout];
33 | if (self) {
34 |
35 | }
36 | return self;
37 | }
38 |
39 | - (void)loadView {
40 | self.collectionView = [[UICollectionView alloc] initWithFrame:UIScreen.mainScreen.bounds
41 | collectionViewLayout:self.collectionViewLayout];
42 | self.collectionView.backgroundColor = UIColor.blackColor;
43 | // self.collectionView = [[UICollectionView alloc] initWithFrame:UIScreen.mainScreen.bounds
44 | // collectionViewLayout:self.collectionViewLayout];
45 | }
46 |
47 | - (void)viewDidLoad {
48 | [super viewDidLoad];
49 |
50 | // Register cell classes
51 | [self.collectionView registerClass:[NETextCollectionViewCell class] forCellWithReuseIdentifier:NSStringFromClass(NETextCollectionViewCell.class)];
52 | [self.collectionView registerClass:[NEHeaderCollectionReusableView class]
53 | forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
54 | withReuseIdentifier:NSStringFromClass(NEHeaderCollectionReusableView.class)];
55 | [self.collectionView registerClass:[NEFooterCollectionReusableView class]
56 | forSupplementaryViewOfKind:UICollectionElementKindSectionFooter
57 | withReuseIdentifier:NSStringFromClass(NEFooterCollectionReusableView.class)];
58 | [self.collectionView registerClass:[UICollectionReusableView class]
59 | forSupplementaryViewOfKind:NECollectionElementKindSectionBackground
60 | withReuseIdentifier:NSStringFromClass(UICollectionReusableView.class)];
61 |
62 | }
63 |
64 | #pragma mark
65 |
66 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
67 | return 4;
68 | }
69 |
70 |
71 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
72 | return 20;
73 | }
74 |
75 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
76 | NETextCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass(NETextCollectionViewCell.class) forIndexPath:indexPath];
77 | cell.backgroundColor = [UIColor grayColor];
78 | cell.textLabel.text = [NSString stringWithFormat:@"%@-%@", @(indexPath.section).stringValue, @(indexPath.item).stringValue];
79 | return cell;
80 | }
81 |
82 | - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
83 | if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
84 | NEHeaderCollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind
85 | withReuseIdentifier:NSStringFromClass(NEHeaderCollectionReusableView.class)
86 | forIndexPath:indexPath];
87 | view.backgroundColor = [UIColor yellowColor];
88 | view.textLabel.text = [NSString stringWithFormat:@"Header - %@", @(indexPath.section).stringValue];
89 | return view;
90 | }
91 | else if ([kind isEqualToString:UICollectionElementKindSectionFooter]) {
92 | NEFooterCollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind
93 | withReuseIdentifier:NSStringFromClass(NEFooterCollectionReusableView.class)
94 | forIndexPath:indexPath];
95 | view.backgroundColor = [UIColor blueColor];
96 | view.textLabel.text = [NSString stringWithFormat:@"Footer - %@", @(indexPath.section).stringValue];
97 | return view;
98 | }
99 | else {
100 | UICollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:NSStringFromClass(UICollectionReusableView.class) forIndexPath:indexPath];
101 | view.backgroundColor = [UIColor colorWithWhite:1 alpha:0.3];
102 | view.clipsToBounds = YES;
103 | view.layer.cornerRadius = 10;
104 | return view;
105 | }
106 | return nil;
107 | }
108 |
109 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
110 | return UIEdgeInsetsMake(20, 20, 20, 20);
111 | }
112 |
113 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
114 | return CGSizeMake(collectionView.frame.size.width, 60);
115 | }
116 |
117 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section {
118 | return CGSizeMake(collectionView.frame.size.width, 30);
119 | }
120 |
121 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
122 | return CGSizeMake(collectionView.frame.size.width / 6, indexPath.item % 3 ? 44 : 88);
123 | }
124 |
125 | - (NECollectionViewFlowLayoutAlignment)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout alignVerticalForSectionAtIndex:(NSInteger)section {
126 | return section % 3;
127 | }
128 |
129 | - (NECollectionViewFlowLayoutAlignment)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout alignHorizontalForSectionAtIndex:(NSInteger)section {
130 | return section % 4;
131 | }
132 |
133 |
134 | - (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout backgroundVisibleForSectionAtIndex:(NSInteger)section {
135 | return YES;
136 | }
137 |
138 | - (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout backgroundIncludeSupplementarysForSectionAtIndex:(NSInteger)section {
139 | return NO;
140 | }
141 |
142 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout backgroundInsetsForSectionAtIndex:(NSInteger)section {
143 | return UIEdgeInsetsMake(-10, -10, -10, -10);
144 | }
145 |
146 | - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
147 |
148 |
149 | // [collectionView reloadItemsAtIndexPaths:@[indexPath]];
150 | // [collectionView reloadData];
151 | [CATransaction begin];
152 | [CATransaction setDisableActions:YES];
153 | CFTimeInterval t1 = CACurrentMediaTime();
154 | [collectionView performBatchUpdates:^{
155 | // __auto_type ctx = [UICollectionViewFlowLayoutInvalidationContext new];
156 | // [ctx invalidateItemsAtIndexPaths:@[indexPath]];
157 | // [collectionView.collectionViewLayout invalidateLayoutWithContext:ctx];
158 | [collectionView reloadItemsAtIndexPaths:@[indexPath]];
159 | } completion:^(BOOL finished) {
160 | CFTimeInterval t2 = CACurrentMediaTime();
161 | NSLog(@"duration = %f", t2 - t1);
162 | }];
163 | [CATransaction commit];
164 | }
165 |
166 |
167 | @end
168 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewFlowLayout/private/NECollectionViewFlowLayoutItem.h:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewFlowLayoutItem.h
3 | // Pods
4 | //
5 | // Created by Daniel on 2019/12/3.
6 | //
7 |
8 | #ifndef NECollectionViewFlowLayoutItem_h
9 | #define NECollectionViewFlowLayoutItem_h
10 |
11 | #import
12 | #include "NECollectionViewFlowLayoutNode.h"
13 | #include "NECollectionViewLayoutHelpers.h"
14 | #include "NECollectionViewFlowLayoutContext.h"
15 |
16 | namespace NE::CollectionViewFlowLayout {
17 |
18 | constexpr NSInteger HeaderZIndex = 10;
19 | constexpr NSInteger FooterZIndex = 10;
20 | constexpr NSInteger ItemZIndex = 0;
21 | constexpr NSInteger BackgroundZIndex = -10;
22 | constexpr NSInteger ScrollContentZIndex = -10;
23 |
24 | struct ItemAttributesTraits {
25 | constexpr static NSInteger zIndex = ItemZIndex;
26 | static auto attributes(Context& ctx, NSIndexPath* indexPath) {
27 | return ctx.cellAttributes(indexPath);
28 | }
29 | static auto contentSize(Context& ctx, NSIndexPath* indexPath) {
30 | return ctx.sizeForItemAtIndexPath(indexPath);
31 | }
32 | };
33 |
34 | struct HeaderAttributesTraits {
35 | constexpr static NSInteger zIndex = HeaderZIndex;
36 | static auto attributes(Context& ctx, NSIndexPath* indexPath) {
37 | return ctx.supplementaryAttributes(UICollectionElementKindSectionHeader, indexPath);
38 | }
39 | static auto contentSize(Context& ctx, NSIndexPath* indexPath) {
40 | return ctx.referenceSizeForHeaderInSection(indexPath.section);
41 | }
42 | };
43 |
44 | struct FooterAttributesTraits {
45 | constexpr static NSInteger zIndex = FooterZIndex;
46 | static auto attributes(Context& ctx, NSIndexPath* indexPath) {
47 | return ctx.supplementaryAttributes(UICollectionElementKindSectionFooter, indexPath);
48 | }
49 | static auto contentSize(Context& ctx, NSIndexPath* indexPath) {
50 | return ctx.referenceSizeForFooterInSection(indexPath.section);
51 | }
52 | };
53 |
54 | extern "C" NSString * const NECollectionElementKindSectionBackground;
55 | struct BackgroundAttributesTraits {
56 | constexpr static NSInteger zIndex = BackgroundZIndex;
57 | static auto attributes(Context& ctx, NSIndexPath* indexPath) {
58 | return ctx.supplementaryAttributes(NECollectionElementKindSectionBackground, indexPath);
59 | }
60 | static auto contentSize(Context& ctx, NSIndexPath* indexPath) {
61 | return CGSizeZero;
62 | }
63 | };
64 |
65 | extern "C" NSString * const NECollectionElementKindSectionScrollContent;
66 | struct ScrollContentAttributesTraits {
67 | constexpr static NSInteger zIndex = ScrollContentZIndex;
68 | static auto attributes(Context& ctx, NSIndexPath* indexPath) {
69 | return ctx.decorationAttributes(NECollectionElementKindSectionScrollContent, indexPath);
70 | }
71 | static auto contentSize(Context& ctx, NSIndexPath* indexPath) {
72 | return CGSizeZero;
73 | }
74 | };
75 |
76 | template
77 | class AttributesContent : public Content {
78 | public:
79 | AttributesContent() : Content(), zIndex_(Traits::zIndex) {}
80 | AttributesContent(const CGPoint& origin) : Content(origin), zIndex_(Traits::zIndex) {}
81 | AttributesContent(const AttributesContent& other) : Content(other),
82 | zIndex_(other.zIndex_),
83 | dataSourceDirty_(other.dataSourceDirty_),
84 | indexPath_(other.indexPath_) { };
85 | AttributesContent& operator=(const AttributesContent& other) {
86 | if (this == &other) return *this;
87 | Content::operator=(other);
88 | zIndex_ = other.zIndex_;
89 | dataSourceDirty_ = other.dataSourceDirty_;
90 | indexPath_ = other.indexPath_;
91 | return *this;
92 | };
93 |
94 | void setIndexPath(IndexPath indexPath) {
95 | indexPath_ = indexPath;
96 | attributes_ = nil; // When indexPath changed, attributes MUST create a new one.
97 | }
98 | IndexPath indexPath() const { return { static_cast(indexPath_.section), static_cast(indexPath_.item) }; }
99 |
100 | void setZIndex(NSInteger zIndex) {
101 | zIndex_ = zIndex + Traits::zIndex;
102 | }
103 | NSInteger zIndex() const { return zIndex_; }
104 |
105 | template
106 | void refreshSizeFromDelegate(Context& ctx) {
107 | if (force || dataSourceDirty()) {
108 | setContentSize(Traits::contentSize(ctx, indexPath_));
109 | }
110 | clearDataSourceDirty();
111 | }
112 |
113 | virtual NECollectionViewFlowLayoutAttributes* attributes(Context& ctx) {
114 | if (attributes_ == nil) {
115 | attributes_ = Traits::attributes(ctx, indexPath_);
116 | }
117 | attributes_.frame = frame();
118 | attributes_.zIndex = zIndex_;
119 | return attributes_;
120 | }
121 |
122 | void markDataSourceDirty() { dataSourceDirty_ = true; }
123 | void clearDataSourceDirty() { dataSourceDirty_ = false; }
124 | bool dataSourceDirty() { return dataSourceDirty_; }
125 |
126 | private:
127 | NSInteger zIndex_ = 0;
128 | bool dataSourceDirty_ = true;
129 | __strong NSIndexPath *indexPath_;
130 | __strong NECollectionViewFlowLayoutAttributes *attributes_;
131 | }; // END Item
132 |
133 | using Item = AttributesContent;
134 | using Header = AttributesContent;
135 | using Footer = AttributesContent;
136 | using Background = AttributesContent;
137 |
138 | class ScrollContent : public AttributesContent {
139 | using Super = AttributesContent;
140 | public:
141 | using AttributesContent::AttributesContent;
142 | ScrollContent(const ScrollContent&) = default;
143 | ScrollContent& operator=(const ScrollContent&) = default;
144 |
145 | void setContentOffset(const CGPoint& contentOffset) {
146 | contentOffset_ = contentOffset;
147 | }
148 | const CGPoint& contentOffset() const {
149 | return contentOffset_;
150 | }
151 |
152 | void setContentSize(const CGSize& contentSize) {
153 | contentSize_ = contentSize;
154 | }
155 | const CGSize& contentSize() const {
156 | return contentSize_;
157 | }
158 |
159 | std::pair adjustContentOffsetToVisible() {
160 | if (pageEnable()) {
161 | auto pageWidth = pageSize_.width < 1 ? frame().size.width : pageSize_.width;
162 | auto index = static_cast(round(contentOffset_.x / pageWidth));
163 | auto x = MAX(0, MIN(pageWidth * index, contentSize_.width - frame().size.width));
164 | return { contentOffset_.x != x, { x, contentOffset_.y } };
165 | }
166 | else {
167 | auto x = MAX(0, MIN(contentOffset_.x, contentSize_.width - frame().size.width));
168 | return { x != contentOffset_.x, { x, contentOffset_.y } };
169 | }
170 | }
171 |
172 | void setPageEnable(bool enable) { pageEnable_ = enable; }
173 | bool pageEnable() const { return pageEnable_; }
174 |
175 | void setPageSize(const CGSize size) { pageSize_ = size; }
176 | const CGSize& pageSize() const { return pageSize_; }
177 |
178 | NECollectionViewFlowLayoutAttributes* attributes(Context& ctx) override {
179 | auto attributes = Super::attributes(ctx);
180 | attributes.contentOffset = contentOffset();
181 | attributes.contentSize = contentSize();
182 | attributes.pageEnable = pageEnable_;
183 | attributes.pageSize = pageSize_;
184 | return attributes;
185 | }
186 |
187 | private:
188 | CGPoint contentOffset_ {0};
189 | CGSize contentSize_ {0};
190 | bool pageEnable_ = false;
191 | CGSize pageSize_ {0};
192 | };
193 | }
194 |
195 | #endif /* NECollectionViewFlowLayoutItem_h */
196 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEInsertCollectionViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // NEInsertCollectionViewController.m
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/9.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "NETextCollectionViewCell.h"
12 | #import "NEHeaderCollectionReusableView.h"
13 | #import "NEFooterCollectionReusableView.h"
14 | #import "NEInsertCollectionViewController.h"
15 |
16 | @interface NEInsertCollectionViewController ()
17 |
18 | @property (nonatomic, strong) NSMutableArray *> *dataSourece;
19 |
20 | @end
21 |
22 | @implementation NEInsertCollectionViewController
23 |
24 | - (instancetype)init
25 | {
26 | NECollectionViewFlowLayout *layout = [NECollectionViewFlowLayout new];
27 | layout.pinToVisibleBoundsEnable = YES;
28 | layout.appearenceAnimator = [NECollectionViewFlowLayoutScaleAnimator new];
29 | // UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new];
30 | layout.minimumLineSpacing = 10;
31 | layout.minimumInteritemSpacing = 10;
32 | // UICollectionViewLayout *layout = [NECollectionViewFlowLayout new];
33 | self = [self initWithCollectionViewLayout:layout];
34 | if (self) {
35 |
36 | }
37 | return self;
38 | }
39 |
40 | - (void)loadView {
41 | self.collectionView = [[UICollectionView alloc] initWithFrame:UIScreen.mainScreen.bounds
42 | collectionViewLayout:self.collectionViewLayout];
43 | self.collectionView.backgroundColor = UIColor.blackColor;
44 | // self.collectionView = [[UICollectionView alloc] initWithFrame:UIScreen.mainScreen.bounds
45 | // collectionViewLayout:self.collectionViewLayout];
46 | }
47 |
48 | - (void)viewDidLoad {
49 | [super viewDidLoad];
50 |
51 | __auto_type insertItem = [[UIBarButtonItem alloc] initWithTitle:@"item" style:UIBarButtonItemStylePlain target:self action:@selector(insertItem)];
52 | __auto_type insertSection = [[UIBarButtonItem alloc] initWithTitle:@"section" style:UIBarButtonItemStylePlain target:self action:@selector(insertSection)];
53 | __auto_type reload = [[UIBarButtonItem alloc] initWithTitle:@"reload" style:UIBarButtonItemStylePlain target:self.collectionView action:@selector(reloadData)];
54 | self.navigationItem.rightBarButtonItems = @[insertSection, insertItem, reload];
55 |
56 | // Register cell classes
57 | [self.collectionView registerClass:[NETextCollectionViewCell class] forCellWithReuseIdentifier:NSStringFromClass(NETextCollectionViewCell.class)];
58 | [self.collectionView registerClass:[NEHeaderCollectionReusableView class]
59 | forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
60 | withReuseIdentifier:NSStringFromClass(NEHeaderCollectionReusableView.class)];
61 | [self.collectionView registerClass:[NEFooterCollectionReusableView class]
62 | forSupplementaryViewOfKind:UICollectionElementKindSectionFooter
63 | withReuseIdentifier:NSStringFromClass(NEFooterCollectionReusableView.class)];
64 | [self.collectionView registerClass:[UICollectionReusableView class]
65 | forSupplementaryViewOfKind:NECollectionElementKindSectionBackground
66 | withReuseIdentifier:NSStringFromClass(UICollectionReusableView.class)];
67 |
68 | self.dataSourece = [NSMutableArray new];
69 |
70 | for (int i = 0; i < 2; i++) {
71 | NSMutableArray *section = [NSMutableArray new];
72 | for (int r = 0; r < 2; r++) {
73 | [section addObject:@{
74 | @"title" : [NSString stringWithFormat:@"%d-%d", i, r]
75 | }];
76 | }
77 | [self.dataSourece addObject:section];
78 | }
79 |
80 | }
81 |
82 | #pragma mark
83 |
84 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
85 | return self.dataSourece.count;
86 | }
87 |
88 |
89 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
90 | return [self.dataSourece[section] count];
91 | }
92 |
93 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
94 | NETextCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass(NETextCollectionViewCell.class) forIndexPath:indexPath];
95 | cell.backgroundColor = [UIColor grayColor];
96 | cell.textLabel.text = self.dataSourece[indexPath.section][indexPath.item][@"title"];
97 | return cell;
98 | }
99 |
100 | - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
101 | if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
102 | NEHeaderCollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind
103 | withReuseIdentifier:NSStringFromClass(NEHeaderCollectionReusableView.class)
104 | forIndexPath:indexPath];
105 | view.backgroundColor = [UIColor yellowColor];
106 | view.textLabel.text = [NSString stringWithFormat:@"Header - %@", @(indexPath.section).stringValue];
107 | return view;
108 | }
109 | else if ([kind isEqualToString:UICollectionElementKindSectionFooter]) {
110 | NEFooterCollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind
111 | withReuseIdentifier:NSStringFromClass(NEFooterCollectionReusableView.class)
112 | forIndexPath:indexPath];
113 | view.backgroundColor = [UIColor blueColor];
114 | view.textLabel.text = [NSString stringWithFormat:@"Footer - %@", @(indexPath.section).stringValue];
115 | return view;
116 | }
117 | else {
118 | UICollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:NSStringFromClass(UICollectionReusableView.class) forIndexPath:indexPath];
119 | view.backgroundColor = [UIColor colorWithWhite:1 alpha:0.3];
120 | view.clipsToBounds = YES;
121 | view.layer.cornerRadius = 10;
122 | return view;
123 | }
124 | return nil;
125 | }
126 |
127 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
128 | return UIEdgeInsetsMake(20, 20, 20, 20);
129 | }
130 |
131 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
132 | return CGSizeMake(collectionView.frame.size.width, 60);
133 | }
134 |
135 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section {
136 | return CGSizeMake(collectionView.frame.size.width, 30);
137 | }
138 |
139 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
140 | return CGSizeMake(collectionView.frame.size.width / 6, 88);
141 | }
142 |
143 | - (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout backgroundVisibleForSectionAtIndex:(NSInteger)section {
144 | return YES;
145 | }
146 |
147 | - (NECollectionViewFlowLayoutAlignment)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout alignVerticalForSectionAtIndex:(NSInteger)section {
148 | return section % 3;
149 | }
150 |
151 | - (NECollectionViewFlowLayoutAlignment)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout alignHorizontalForSectionAtIndex:(NSInteger)section {
152 | return section % 4;
153 | }
154 |
155 | - (void)insertItem {
156 | [self.dataSourece.firstObject insertObject:@{
157 | @"title" : @"inserted"
158 | } atIndex:0];
159 | [self.collectionView insertItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:0 inSection:0]]];
160 | }
161 |
162 | - (void)insertSection {
163 | NSMutableArray *section = [NSMutableArray new];
164 | for (int r = 0; r < 10; r++) {
165 | [section addObject:@{
166 | @"title" : [NSString stringWithFormat:@"inserted-%d", r]
167 | }];
168 | }
169 | [self.dataSourece insertObject:section atIndex:0];
170 | [self.collectionView insertSections:[NSIndexSet indexSetWithIndex:0]];
171 | }
172 |
173 | @end
174 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewFlowLayout/NECollectionViewFlowLayout.h:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewFlowLayout.h
3 | // Pods-NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/11/28.
6 | //
7 |
8 | #import
9 | #import "NEOptimizeCollectionViewLayoutProtocol.h"
10 | #import "NECollectionViewFlowLayoutAnimator.h"
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | typedef NS_ENUM(NSInteger, NECollectionViewFlowLayoutAlignment) {
15 | NECollectionViewFlowLayoutAlignLeading, // left or top
16 | NECollectionViewFlowLayoutAlignTrailing, // right or bottom
17 | NECollectionViewFlowLayoutAlignCenter, // in the center of container
18 | NECollectionViewFlowLayoutAlignSpacingBetween, // leading & trailing with same spacing between items
19 | };
20 |
21 | static inline NSString *NECollectionViewFlowLayoutAlignmentToReadable(NECollectionViewFlowLayoutAlignment alignment) {
22 | switch (alignment) {
23 | case NECollectionViewFlowLayoutAlignLeading: return @"leading";
24 | case NECollectionViewFlowLayoutAlignTrailing: return @"trailing";
25 | case NECollectionViewFlowLayoutAlignCenter: return @"center";
26 | case NECollectionViewFlowLayoutAlignSpacingBetween: return @"spacing between";
27 | default: return @"unknow";
28 | }
29 | }
30 |
31 | typedef NS_ENUM(NSInteger, NECollectionViewFlowLayoutPinToVisibleBounds) {
32 | NECollectionViewFlowLayoutPinToVisibleBoundsNone, // No pinning
33 | NECollectionViewFlowLayoutPinToVisibleBoundsInsideSection, // Pin inside section, the same as UICollectionViewFlowLayout
34 | NECollectionViewFlowLayoutPinToVisibleBoundsAfterSection, // Pin at its section and after its sections, use by header normally.
35 | NECollectionViewFlowLayoutPinToVisibleBoundsBeforeSection, // Pin at its section and before its sections, use by footer normally.
36 | NECollectionViewFlowLayoutPinToVisibleBoundsAlways, // Always pin at collection view. MUST be only one, or its behavior is undefined.
37 | };
38 |
39 | static inline NSString *NECollectionViewFlowLayoutPinToVisibleBoundsToReadable(NECollectionViewFlowLayoutPinToVisibleBounds type) {
40 | switch (type) {
41 | case NECollectionViewFlowLayoutPinToVisibleBoundsNone: return @"none";
42 | case NECollectionViewFlowLayoutPinToVisibleBoundsInsideSection: return @"inside section";
43 | case NECollectionViewFlowLayoutPinToVisibleBoundsAfterSection: return @"after section";
44 | case NECollectionViewFlowLayoutPinToVisibleBoundsBeforeSection: return @"before section";
45 | case NECollectionViewFlowLayoutPinToVisibleBoundsAlways: return @"always";
46 | default: return @"unknow";
47 | }
48 | }
49 |
50 | #ifdef __cplusplus
51 | extern "C" {
52 | #endif
53 |
54 | UIKIT_EXTERN NSString *const NECollectionElementKindSectionBackground;
55 |
56 | #ifdef __cplusplus
57 | }
58 | #endif
59 |
60 | @protocol NECollectionViewDelegateFlowLayout
61 | @optional
62 |
63 | - (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout scrollViewDidScrollToContentOffset:(CGPoint)contentOffset forSectionAtIndex:(NSInteger)section;
64 |
65 | #pragma mark - Zindex
66 | /// Addtion z index for all item in the section. For example, default 0 will be ( 0 + addition ) zIndex.
67 | /// May used for more complex custom layout.
68 | - (NSInteger)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout additionZIndexForSectionAtIndex:(NSInteger)section;
69 |
70 | #pragma mark - Pin to visible bounds
71 | /// The section header pin to visible bounds config.
72 | - (NECollectionViewFlowLayoutPinToVisibleBounds)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout headerPinToVisibleBoundsForSectionAtIndex:(NSInteger)section;
73 | /// The section footer pin to visible bounds config.
74 | - (NECollectionViewFlowLayoutPinToVisibleBounds)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout footerPinToVisibleBoundsForSectionAtIndex:(NSInteger)section;
75 |
76 | #pragma mark - Section background support
77 | /// Show a background view in the section below the items.
78 | /// Once return YES, you MUST return a supplementary view of NECollectionElementKindSectionBackground.
79 | - (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout backgroundVisibleForSectionAtIndex:(NSInteger)section;
80 |
81 | /// Define the background size. If return YES, the size will contains the header & footer.
82 | /// Otherwise it is just the items size, without SectionInsets.
83 | - (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout backgroundIncludeSupplementarysForSectionAtIndex:(NSInteger)section;
84 |
85 | /// This method gives you a change to modify the background size that calculate by above configs.
86 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout backgroundInsetsForSectionAtIndex:(NSInteger)section;
87 |
88 | #pragma mark - Section layout alignment
89 |
90 | /// Declare the horizontal aligment of a line in the section.
91 | - (NECollectionViewFlowLayoutAlignment)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout alignHorizontalForSectionAtIndex:(NSInteger)section;
92 |
93 | /// Declare the vertical aligment of a line in the section.
94 | - (NECollectionViewFlowLayoutAlignment)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout alignVerticalForSectionAtIndex:(NSInteger)section;
95 |
96 | #pragma mark - Section scroll direction and page
97 | /// Declare the scroll direction of the section. Default Vertical.
98 | - (UICollectionViewScrollDirection)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout scrollDirectionForSectionAtIndex:(NSInteger)section;
99 |
100 | /// Declare the content height of the section, when scroll direction Horizontal. Default 0.
101 | /// Return 0 means use items max height, and location in one line.
102 | /// When is not 0, layout will effect by vertical alignment and horizontal alignment.
103 | - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout heightForScrollHorizontalSectionAtIndex:(NSInteger)section;
104 |
105 | /// Enable page scroll in the section.
106 | - (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout pageEnableForSectionAtIndex:(NSInteger)section;
107 |
108 | /// Decide the page width of the scroll view. Default CGSizeZero.
109 | /// Zero means the page size is equal to the frame.
110 | /// Height is no meaning now, for preversed usage.
111 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout pageSizeForSectionAtIndex:(NSInteger)section;
112 |
113 | @end
114 |
115 | @interface NECollectionViewFlowLayout : UICollectionViewLayout
116 |
117 | @property (nonatomic) UICollectionViewScrollDirection sectionScrollDirection; // Define the section layout direction.
118 | @property (nonatomic) CGFloat sectionWidth; // Default 0, means auto, will use container's width.
119 | @property (nonatomic) CGFloat sectionSpacing; // Default 0, the spacing between sections, not include section insets.
120 |
121 | @property (nonatomic) NECollectionViewFlowLayoutAlignment alignHorizontal; // Default leading(left).
122 | @property (nonatomic) NECollectionViewFlowLayoutAlignment alignVertical; // Default center.
123 |
124 | @property (nonatomic) CGFloat minimumLineSpacing;
125 | @property (nonatomic) CGFloat minimumInteritemSpacing;
126 | @property (nonatomic) CGSize itemSize;
127 |
128 | @property (nonatomic) CGSize headerReferenceSize;
129 | @property (nonatomic) CGSize footerReferenceSize;
130 | @property (nonatomic) UIEdgeInsets sectionInset;
131 |
132 | @property (nonatomic, assign) BOOL pinToVisibleBoundsEnable; // Enable for pinning feature. Default NO.
133 |
134 | @property (nonatomic, strong) id appearenceAnimator; // Animator for items
135 | @property (nonatomic, strong) id supplementaryAppearenceAnimator; // Animator for supplementaries
136 | @property (nonatomic, strong) id decorationAppearenceAnimator; // Animator for decoration views
137 |
138 | /// It will close the optimization feature when setting YES.
139 | @property (nonatomic) BOOL invalidateFlowLayoutDelegateMetricsWhenUpdates; // Default NO.
140 |
141 | /// Get the current contentOffset of the section. If the section is not scroll horizontal, the value is meaningless.
142 | /// It MUST call after [collectionView layoutIfNeeded]
143 | - (CGPoint)contentOffsetForSectionAtIndex:(NSInteger)section;
144 | /// Set the current contentOffset of the section. If the section is not scroll horizontal, do nothing.
145 | /// It MUST call after [collectionView layoutIfNeeded]
146 | - (void)setContentOffset:(CGPoint)contentOffset forSectionAtIndex:(NSInteger)section;
147 |
148 | /// Get the frame of the section.
149 | - (CGRect)frameForSectionAtIndex:(NSInteger)section;
150 |
151 | @end
152 |
153 | NS_ASSUME_NONNULL_END
154 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEDeleteCollectionViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // NEDeleteCollectionViewController.m
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/9.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "NETextCollectionViewCell.h"
12 | #import "NEHeaderCollectionReusableView.h"
13 | #import "NEFooterCollectionReusableView.h"
14 | #import "NEDeleteCollectionViewController.h"
15 |
16 | @interface NEDeleteCollectionViewController ()
17 |
18 | @property (nonatomic, strong) NSMutableArray *> *dataSourece;
19 |
20 | @end
21 |
22 | @implementation NEDeleteCollectionViewController
23 |
24 | - (instancetype)init
25 | {
26 | NECollectionViewFlowLayout *layout = [NECollectionViewFlowLayout new];
27 | layout.pinToVisibleBoundsEnable = YES;
28 | layout.appearenceAnimator = [NECollectionViewFlowLayoutScaleAnimator new];
29 | // UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new];
30 | layout.minimumLineSpacing = 10;
31 | layout.minimumInteritemSpacing = 10;
32 | // UICollectionViewLayout *layout = [NECollectionViewFlowLayout new];
33 | self = [self initWithCollectionViewLayout:layout];
34 | if (self) {
35 |
36 | }
37 | return self;
38 | }
39 |
40 | - (void)loadView {
41 | self.collectionView = [[NEOptimizeCollectionView alloc] initWithFrame:UIScreen.mainScreen.bounds
42 | collectionViewLayout:self.collectionViewLayout];
43 | self.collectionView.backgroundColor = UIColor.blackColor;
44 |
45 | // self.collectionView = [[UICollectionView alloc] initWithFrame:UIScreen.mainScreen.bounds
46 | // collectionViewLayout:self.collectionViewLayout];
47 | }
48 |
49 | - (void)viewDidLoad {
50 | [super viewDidLoad];
51 |
52 | __auto_type deleteItem = [[UIBarButtonItem alloc] initWithTitle:@"item" style:UIBarButtonItemStylePlain target:self action:@selector(deleteItem)];
53 | __auto_type deletSection = [[UIBarButtonItem alloc] initWithTitle:@"section" style:UIBarButtonItemStylePlain target:self action:@selector(deletSection)];
54 | self.navigationItem.rightBarButtonItems = @[deletSection, deleteItem];
55 |
56 | // Register cell classes
57 | [self.collectionView registerClass:[NETextCollectionViewCell class] forCellWithReuseIdentifier:NSStringFromClass(NETextCollectionViewCell.class)];
58 | [self.collectionView registerClass:[NEHeaderCollectionReusableView class]
59 | forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
60 | withReuseIdentifier:NSStringFromClass(NEHeaderCollectionReusableView.class)];
61 | [self.collectionView registerClass:[NEFooterCollectionReusableView class]
62 | forSupplementaryViewOfKind:UICollectionElementKindSectionFooter
63 | withReuseIdentifier:NSStringFromClass(NEFooterCollectionReusableView.class)];
64 | [self.collectionView registerClass:[UICollectionReusableView class]
65 | forSupplementaryViewOfKind:NECollectionElementKindSectionBackground
66 | withReuseIdentifier:NSStringFromClass(UICollectionReusableView.class)];
67 |
68 | self.dataSourece = [NSMutableArray new];
69 |
70 | for (int i = 0; i < 10; i++) {
71 | NSMutableArray *section = [NSMutableArray new];
72 | for (int r = 0; r < 20; r++) {
73 | [section addObject:@{
74 | @"title" : [NSString stringWithFormat:@"%d-%d", i, r]
75 | }];
76 | }
77 | [self.dataSourece addObject:section];
78 | }
79 |
80 | }
81 |
82 | #pragma mark
83 |
84 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
85 | return self.dataSourece.count;
86 | }
87 |
88 |
89 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
90 | return [self.dataSourece[section] count];
91 | }
92 |
93 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
94 | NETextCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass(NETextCollectionViewCell.class) forIndexPath:indexPath];
95 | cell.backgroundColor = [UIColor grayColor];
96 | cell.textLabel.text = self.dataSourece[indexPath.section][indexPath.item][@"title"];
97 | return cell;
98 | }
99 |
100 | - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
101 | if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
102 | NEHeaderCollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind
103 | withReuseIdentifier:NSStringFromClass(NEHeaderCollectionReusableView.class)
104 | forIndexPath:indexPath];
105 | view.backgroundColor = [UIColor yellowColor];
106 | view.textLabel.text = [NSString stringWithFormat:@"Header - %@", @(indexPath.section).stringValue];
107 | return view;
108 | }
109 | else if ([kind isEqualToString:UICollectionElementKindSectionFooter]) {
110 | NEFooterCollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind
111 | withReuseIdentifier:NSStringFromClass(NEFooterCollectionReusableView.class)
112 | forIndexPath:indexPath];
113 | view.backgroundColor = [UIColor blueColor];
114 | view.textLabel.text = [NSString stringWithFormat:@"Footer - %@", @(indexPath.section).stringValue];
115 | return view;
116 | }
117 | else {
118 | UICollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:NSStringFromClass(UICollectionReusableView.class) forIndexPath:indexPath];
119 | view.backgroundColor = [UIColor colorWithWhite:1 alpha:0.3];
120 | view.clipsToBounds = YES;
121 | view.layer.cornerRadius = 10;
122 | return view;
123 | }
124 | return nil;
125 | }
126 |
127 | - (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
128 |
129 | }
130 |
131 | - (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
132 |
133 | }
134 |
135 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
136 | return UIEdgeInsetsMake(20, 20, 20, 20);
137 | }
138 |
139 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
140 | return CGSizeMake(collectionView.frame.size.width, 60);
141 | }
142 |
143 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section {
144 | return CGSizeMake(collectionView.frame.size.width, 30);
145 | }
146 |
147 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
148 | return CGSizeMake(collectionView.frame.size.width / 6, 88);
149 | }
150 |
151 | - (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout backgroundVisibleForSectionAtIndex:(NSInteger)section {
152 | return YES;
153 | }
154 |
155 | - (NECollectionViewFlowLayoutAlignment)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout alignVerticalForSectionAtIndex:(NSInteger)section {
156 | return section % 3;
157 | }
158 |
159 | - (NECollectionViewFlowLayoutAlignment)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout alignHorizontalForSectionAtIndex:(NSInteger)section {
160 | return section % 4;
161 | }
162 |
163 | - (void)deleteItem {
164 | if (self.dataSourece.firstObject.count) {
165 | NSInteger index = self.dataSourece.firstObject.count - 1;
166 | [self.dataSourece.firstObject removeObjectAtIndex:index];
167 | [self.collectionView deleteItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:index inSection:0]]];
168 | }
169 | }
170 |
171 | - (void)deletSection {
172 | if (self.dataSourece.count) {
173 | NSInteger i = 0; //self.dataSourece.count - 1;
174 | [self.dataSourece removeObjectAtIndex:i];
175 | [self.collectionView performBatchUpdates:^{
176 | [self.collectionView deleteSections:[NSIndexSet indexSetWithIndex:i]];
177 | } completion:^(BOOL finished) {
178 |
179 | }];
180 | }
181 | }
182 |
183 | @end
184 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEPinCollectionViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // NEPinCollectionViewController.m
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/9.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "NETextCollectionViewCell.h"
12 | #import "NEHeaderCollectionReusableView.h"
13 | #import "NEFooterCollectionReusableView.h"
14 | #import "NEPinCollectionViewController.h"
15 |
16 | @interface NEPinCollectionViewController ()
17 | @property (nonatomic, assign) NECollectionViewFlowLayoutPinToVisibleBounds style;
18 | @end
19 |
20 | @implementation NEPinCollectionViewController
21 |
22 | - (instancetype)init
23 | {
24 | NECollectionViewFlowLayout *layout = [NECollectionViewFlowLayout new];
25 | layout.pinToVisibleBoundsEnable = YES;
26 | // UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new];
27 | layout.minimumLineSpacing = 10;
28 | layout.minimumInteritemSpacing = 10;
29 | // UICollectionViewLayout *layout = [NECollectionViewFlowLayout new];
30 | self = [self initWithCollectionViewLayout:layout];
31 | if (self) {
32 |
33 | }
34 | return self;
35 | }
36 |
37 | - (void)loadView {
38 | self.collectionView = [[NEOptimizeCollectionView alloc] initWithFrame:UIScreen.mainScreen.bounds
39 | collectionViewLayout:self.collectionViewLayout];
40 | self.collectionView.backgroundColor = UIColor.blackColor;
41 |
42 | // self.collectionView = [[UICollectionView alloc] initWithFrame:UIScreen.mainScreen.bounds
43 | // collectionViewLayout:self.collectionViewLayout];
44 | }
45 |
46 | - (void)viewDidLoad {
47 | [super viewDidLoad];
48 |
49 | __auto_type pin = [[UIBarButtonItem alloc] initWithTitle:@"pin" style:UIBarButtonItemStylePlain target:self action:@selector(pin)];
50 | __auto_type style = [[UIBarButtonItem alloc] initWithTitle:@"style" style:UIBarButtonItemStylePlain target:self action:@selector(pinStyle)];
51 | self.navigationItem.rightBarButtonItems = @[pin, style];
52 |
53 | // Register cell classes
54 | [self.collectionView registerClass:[NETextCollectionViewCell class] forCellWithReuseIdentifier:NSStringFromClass(NETextCollectionViewCell.class)];
55 | [self.collectionView registerClass:[NEHeaderCollectionReusableView class]
56 | forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
57 | withReuseIdentifier:NSStringFromClass(NEHeaderCollectionReusableView.class)];
58 | [self.collectionView registerClass:[NEFooterCollectionReusableView class]
59 | forSupplementaryViewOfKind:UICollectionElementKindSectionFooter
60 | withReuseIdentifier:NSStringFromClass(NEFooterCollectionReusableView.class)];
61 | [self.collectionView registerClass:[UICollectionReusableView class]
62 | forSupplementaryViewOfKind:NECollectionElementKindSectionBackground
63 | withReuseIdentifier:NSStringFromClass(UICollectionReusableView.class)];
64 |
65 | }
66 |
67 | #pragma mark
68 |
69 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
70 | return 4;
71 | }
72 |
73 |
74 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
75 | return 40;
76 | }
77 |
78 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
79 | NETextCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass(NETextCollectionViewCell.class) forIndexPath:indexPath];
80 | cell.backgroundColor = [UIColor grayColor];
81 | cell.textLabel.text = [NSString stringWithFormat:@"%@-%@", @(indexPath.section).stringValue, @(indexPath.item).stringValue];
82 | return cell;
83 | }
84 |
85 | - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
86 | if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
87 | NEHeaderCollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind
88 | withReuseIdentifier:NSStringFromClass(NEHeaderCollectionReusableView.class)
89 | forIndexPath:indexPath];
90 | view.backgroundColor = [UIColor yellowColor];
91 | view.textLabel.text = [NSString stringWithFormat:@"Header - %@", @(indexPath.section).stringValue];
92 | return view;
93 | }
94 | else if ([kind isEqualToString:UICollectionElementKindSectionFooter]) {
95 | NEFooterCollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind
96 | withReuseIdentifier:NSStringFromClass(NEFooterCollectionReusableView.class)
97 | forIndexPath:indexPath];
98 | view.backgroundColor = [UIColor blueColor];
99 | view.textLabel.text = [NSString stringWithFormat:@"Footer - %@", @(indexPath.section).stringValue];
100 | return view;
101 | }
102 | else {
103 | UICollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:NSStringFromClass(UICollectionReusableView.class) forIndexPath:indexPath];
104 | view.backgroundColor = [UIColor colorWithWhite:1 alpha:0.3];
105 | view.clipsToBounds = YES;
106 | view.layer.cornerRadius = 10;
107 | return view;
108 | }
109 | return nil;
110 | }
111 |
112 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
113 | return UIEdgeInsetsMake(20, 20, 20, 20);
114 | }
115 |
116 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
117 | return CGSizeMake(collectionView.frame.size.width, 60);
118 | }
119 |
120 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section {
121 | return CGSizeMake(collectionView.frame.size.width, 30);
122 | }
123 |
124 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
125 | return CGSizeMake(collectionView.frame.size.width / 6, indexPath.item % 3 ? 44 : 88);
126 | }
127 |
128 | - (NECollectionViewFlowLayoutAlignment)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout alignVerticalForSectionAtIndex:(NSInteger)section {
129 | return section % 3;
130 | }
131 |
132 | - (NECollectionViewFlowLayoutAlignment)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout alignHorizontalForSectionAtIndex:(NSInteger)section {
133 | return section % 4;
134 | }
135 |
136 | - (NECollectionViewFlowLayoutPinToVisibleBounds)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout headerPinToVisibleBoundsForSectionAtIndex:(NSInteger)section {
137 | if (section == 1) {
138 | return self.style;
139 | }
140 | return NECollectionViewFlowLayoutPinToVisibleBoundsNone;
141 | }
142 |
143 | - (NECollectionViewFlowLayoutPinToVisibleBounds)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout footerPinToVisibleBoundsForSectionAtIndex:(NSInteger)section {
144 | if (section == 1) {
145 | return self.style;
146 | }
147 | return NECollectionViewFlowLayoutPinToVisibleBoundsNone;
148 | }
149 |
150 |
151 | - (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout backgroundVisibleForSectionAtIndex:(NSInteger)section {
152 | return YES;
153 | }
154 |
155 | - (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout backgroundIncludeSupplementarysForSectionAtIndex:(NSInteger)section {
156 | return NO;
157 | }
158 |
159 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout backgroundInsetsForSectionAtIndex:(NSInteger)section {
160 | return UIEdgeInsetsMake(-10, -10, -10, -10);
161 | }
162 |
163 | - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
164 |
165 | }
166 |
167 | - (void)pin {
168 | NECollectionViewFlowLayout *layout = (NECollectionViewFlowLayout *)self.collectionViewLayout;
169 | layout.pinToVisibleBoundsEnable = !layout.pinToVisibleBoundsEnable;
170 | [self updateTitle];
171 | }
172 |
173 | - (void)pinStyle {
174 | self.style ++;
175 | if (self.style > NECollectionViewFlowLayoutPinToVisibleBoundsAlways) {
176 | self.style = NECollectionViewFlowLayoutPinToVisibleBoundsNone;
177 | }
178 | [self.collectionViewLayout invalidateLayout];
179 | [self updateTitle];
180 | }
181 |
182 | - (void)updateTitle {
183 | self.title = [NSString stringWithFormat:@"%@-%@",
184 | @([(NECollectionViewFlowLayout *)self.collectionViewLayout pinToVisibleBoundsEnable]),
185 | NECollectionViewFlowLayoutPinToVisibleBoundsToReadable(self.style)];
186 | }
187 |
188 | @end
189 |
--------------------------------------------------------------------------------
/Example/NECollectionViewLayout/NEAlignmentCollectionViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // NEAlignmentCollectionViewController.m
3 | // NECollectionViewLayout_Example
4 | //
5 | // Created by Daniel on 2019/12/9.
6 | // Copyright © 2019 Daniel. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "NETextCollectionViewCell.h"
12 | #import "NEHeaderCollectionReusableView.h"
13 | #import "NEFooterCollectionReusableView.h"
14 | #import "NEAlignmentCollectionViewController.h"
15 |
16 | @interface NEAlignmentCollectionViewController ()
17 | @property (nonatomic, assign) NECollectionViewFlowLayoutAlignment hStyle;
18 | @property (nonatomic, assign) NECollectionViewFlowLayoutAlignment vStyle;
19 |
20 | @end
21 |
22 | @implementation NEAlignmentCollectionViewController
23 |
24 | - (instancetype)init
25 | {
26 | NECollectionViewFlowLayout *layout = [NECollectionViewFlowLayout new];
27 | layout.pinToVisibleBoundsEnable = YES;
28 | // UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new];
29 | layout.minimumLineSpacing = 10;
30 | layout.minimumInteritemSpacing = 10;
31 | // UICollectionViewLayout *layout = [NECollectionViewFlowLayout new];
32 | self = [self initWithCollectionViewLayout:layout];
33 | if (self) {
34 |
35 | }
36 | return self;
37 | }
38 |
39 | - (void)loadView {
40 | self.collectionView = [[NEOptimizeCollectionView alloc] initWithFrame:UIScreen.mainScreen.bounds
41 | collectionViewLayout:self.collectionViewLayout];
42 | self.collectionView.backgroundColor = UIColor.blackColor;
43 |
44 | // self.collectionView = [[UICollectionView alloc] initWithFrame:UIScreen.mainScreen.bounds
45 | // collectionViewLayout:self.collectionViewLayout];
46 | }
47 |
48 | - (void)viewDidLoad {
49 | [super viewDidLoad];
50 |
51 | [self updateTitle];
52 | __auto_type hStyle = [[UIBarButtonItem alloc] initWithTitle:@"hstyle" style:UIBarButtonItemStylePlain target:self action:@selector(switchHStyle)];
53 | __auto_type vStyle = [[UIBarButtonItem alloc] initWithTitle:@"vstyle" style:UIBarButtonItemStylePlain target:self action:@selector(switchVStyle)];
54 | self.navigationItem.rightBarButtonItems = @[hStyle, vStyle];
55 |
56 | // Register cell classes
57 | [self.collectionView registerClass:[NETextCollectionViewCell class] forCellWithReuseIdentifier:NSStringFromClass(NETextCollectionViewCell.class)];
58 | [self.collectionView registerClass:[NEHeaderCollectionReusableView class]
59 | forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
60 | withReuseIdentifier:NSStringFromClass(NEHeaderCollectionReusableView.class)];
61 | [self.collectionView registerClass:[NEFooterCollectionReusableView class]
62 | forSupplementaryViewOfKind:UICollectionElementKindSectionFooter
63 | withReuseIdentifier:NSStringFromClass(NEFooterCollectionReusableView.class)];
64 | [self.collectionView registerClass:[UICollectionReusableView class]
65 | forSupplementaryViewOfKind:NECollectionElementKindSectionBackground
66 | withReuseIdentifier:NSStringFromClass(UICollectionReusableView.class)];
67 |
68 | }
69 |
70 | #pragma mark
71 |
72 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
73 | return 4;
74 | }
75 |
76 |
77 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
78 | return 40;
79 | }
80 |
81 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
82 | NETextCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass(NETextCollectionViewCell.class) forIndexPath:indexPath];
83 | cell.backgroundColor = [UIColor grayColor];
84 | cell.textLabel.text = [NSString stringWithFormat:@"%@-%@", @(indexPath.section).stringValue, @(indexPath.item).stringValue];
85 | return cell;
86 | }
87 |
88 | - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
89 | if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
90 | NEHeaderCollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind
91 | withReuseIdentifier:NSStringFromClass(NEHeaderCollectionReusableView.class)
92 | forIndexPath:indexPath];
93 | view.backgroundColor = [UIColor yellowColor];
94 | view.textLabel.text = [NSString stringWithFormat:@"Header - %@", @(indexPath.section).stringValue];
95 | return view;
96 | }
97 | else if ([kind isEqualToString:UICollectionElementKindSectionFooter]) {
98 | NEFooterCollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind
99 | withReuseIdentifier:NSStringFromClass(NEFooterCollectionReusableView.class)
100 | forIndexPath:indexPath];
101 | view.backgroundColor = [UIColor blueColor];
102 | view.textLabel.text = [NSString stringWithFormat:@"Footer - %@", @(indexPath.section).stringValue];
103 | return view;
104 | }
105 | else {
106 | UICollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:NSStringFromClass(UICollectionReusableView.class) forIndexPath:indexPath];
107 | view.backgroundColor = [UIColor colorWithWhite:1 alpha:0.3];
108 | view.clipsToBounds = YES;
109 | view.layer.cornerRadius = 10;
110 | return view;
111 | }
112 | return nil;
113 | }
114 |
115 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
116 | return UIEdgeInsetsMake(20, 20, 20, 20);
117 | }
118 |
119 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
120 | return CGSizeMake(collectionView.frame.size.width, 60);
121 | }
122 |
123 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section {
124 | return CGSizeMake(collectionView.frame.size.width, 30);
125 | }
126 |
127 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
128 | return CGSizeMake(collectionView.frame.size.width / 6, indexPath.item % 3 ? 44 : 88);
129 | }
130 |
131 | - (NECollectionViewFlowLayoutAlignment)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout alignVerticalForSectionAtIndex:(NSInteger)section {
132 | return self.vStyle;
133 | }
134 |
135 | - (NECollectionViewFlowLayoutAlignment)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout alignHorizontalForSectionAtIndex:(NSInteger)section {
136 | return self.hStyle;
137 | }
138 |
139 | - (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout backgroundVisibleForSectionAtIndex:(NSInteger)section {
140 | return YES;
141 | }
142 |
143 | - (BOOL)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout backgroundIncludeSupplementarysForSectionAtIndex:(NSInteger)section {
144 | return NO;
145 | }
146 |
147 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout backgroundInsetsForSectionAtIndex:(NSInteger)section {
148 | return UIEdgeInsetsMake(-10, -10, -10, -10);
149 | }
150 |
151 | - (UICollectionViewScrollDirection)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout scrollDirectionForSectionAtIndex:(NSInteger)section {
152 | if (section >= 1) {
153 | return UICollectionViewScrollDirectionHorizontal;
154 | }
155 | return UICollectionViewScrollDirectionVertical;
156 | }
157 |
158 | - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout heightForScrollHorizontalSectionAtIndex:(NSInteger)section {
159 | if (section == 2) {
160 | return 300;
161 | }
162 | return 0;
163 | }
164 |
165 | - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
166 |
167 | }
168 |
169 | - (void)switchHStyle {
170 | self.hStyle ++;
171 | if (self.hStyle > NECollectionViewFlowLayoutAlignSpacingBetween) {
172 | self.hStyle = NECollectionViewFlowLayoutAlignLeading;
173 | }
174 | [self.collectionViewLayout invalidateLayout];
175 | [self updateTitle];
176 | }
177 | - (void)switchVStyle {
178 | self.vStyle ++;
179 | if (self.vStyle > NECollectionViewFlowLayoutAlignSpacingBetween) {
180 | self.vStyle = NECollectionViewFlowLayoutAlignLeading;
181 | }
182 | [self.collectionViewLayout invalidateLayout];
183 | [self updateTitle];
184 | }
185 |
186 | - (void)updateTitle {
187 | self.title = [NSString stringWithFormat:@"h: %@, v: %@",
188 | NECollectionViewFlowLayoutAlignmentToReadable(self.hStyle),
189 | NECollectionViewFlowLayoutAlignmentToReadable(self.vStyle)];
190 | }
191 |
192 | @end
193 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/NECollectionViewLayout/Classes/NECollectionViewFlowLayout/private/NECollectionViewFlowLayoutCalculator.h:
--------------------------------------------------------------------------------
1 | //
2 | // NECollectionViewFlowLayoutCalculator.h
3 | // Pods
4 | //
5 | // Created by Daniel on 2019/11/28.
6 | //
7 |
8 | #ifndef NECollectionViewFlowLayoutCalculator_h
9 | #define NECollectionViewFlowLayoutCalculator_h
10 |
11 | #include
12 | #include
13 | #include
14 | #include "NECollectionViewLayoutHelpers.h"
15 | #include "NECollectionViewFlowLayoutContext.h"
16 | #include "NECollectionViewFlowLayoutSection.h"
17 | #include "NECollectionViewFlowLayoutInvalidation.h"
18 | #import "NECollectionViewFlowLayoutInvalidationContext.h"
19 | #include "NECollectionViewFlowLayoutCollection.h"
20 | #include "NECollectionViewUpdates.h"
21 |
22 | namespace NE::CollectionViewFlowLayout {
23 |
24 | class Calculator : public Container {
25 | public:
26 | using Updates = CollectionView::Updates;
27 |
28 | Calculator() {}
29 | ~Calculator() override {}
30 |
31 | void setBounds(const CGRect& bounds) {
32 | if (bounds_ != bounds) {
33 | bounds_ = bounds;
34 | markLayoutDirty();
35 | }
36 | }
37 |
38 | void setContext(Context&& ctx) { context_ = ctx; }
39 | Context& context() { return context_; }
40 |
41 | Updates& updates() { return updates_; }
42 |
43 | const auto& sections() const { return sections_; }
44 |
45 | bool invalidateFlowLayoutDelegateMetricsWhenUpdates() {
46 | return invalidateFlowLayoutDelegateMetricsWhenUpdates_;
47 | }
48 | void setInvalidateFlowLayoutDelegateMetricsWhenUpdates(bool b) {
49 | invalidateFlowLayoutDelegateMetricsWhenUpdates_ = b;
50 | }
51 |
52 | void invalidate(NECollectionViewFlowLayoutInvalidationContext *ctx) {
53 | if (ctx.invalidateEverything) invalidation_.invalidateEverything();
54 | if (ctx.invalidateDataSourceCounts) invalidation_.invalidateDataSourceCounts();
55 | if (ctx.invalidateFlowLayoutDelegateMetrics) invalidation_.invalidateFlowLayoutDelegateMetrics();
56 | if (ctx.invalidateFlowLayoutAttributes) invalidation_.invalidateFlowLayoutAttributes();
57 | if (ctx.invalidatedItemIndexPaths.count) {
58 | for (NSIndexPath *idx in ctx.invalidatedItemIndexPaths) {
59 | invalidation_.invalidateItem(idx);
60 | }
61 | }
62 | if (ctx.invalidatedSupplementaryIndexPaths.count) {
63 | [ctx.invalidatedSupplementaryIndexPaths enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSArray * _Nonnull obj, BOOL * _Nonnull stop) {
64 | for (NSIndexPath *idx in obj) {
65 | invalidation_.invalidateSumplementary(key, idx);
66 | }
67 | }];
68 | }
69 | if (ctx.invalidatedDecorationIndexPaths.count) {
70 | [ctx.invalidatedDecorationIndexPaths enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSArray * _Nonnull obj, BOOL * _Nonnull stop) {
71 | for (NSIndexPath *idx in obj) {
72 | invalidation_.invalidateDecoration(key, idx);
73 | }
74 | }];
75 | }
76 | if (ctx.invalidatedSectionScrollOffsets.count) {
77 | [ctx.invalidatedSectionScrollOffsets enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull key, NSValue * _Nonnull obj, BOOL * _Nonnull stop) {
78 | invalidation_.invalidateSectionContentOffset(key.unsignedIntegerValue, obj.CGPointValue);
79 | }];
80 | }
81 | }
82 |
83 | void calculateLayoutIfNeeded() {
84 | NSUInteger minSection = NSNotFound;
85 | bool refreshSize = false;
86 | bool refreshSizeAll = false;
87 | if (!invalidation_.isInvalidateEverything() && invalidation_.isInvalidateDataSourceCounts()) {
88 | // Update actions(delete, insert) needs prev attributes. We cache it here ONLY update occurs.
89 | cacheResult();
90 | }
91 | else {
92 | clearCachedResult();
93 | }
94 |
95 | if (invalidation_.isInvalidateEverything()) {
96 | refreshAll();
97 | refreshSizeAll = refreshSize = true;
98 | minSection = 0;
99 | }
100 | else if (invalidation_.isInvalidateDataSourceCounts()) {
101 | if (!invalidateFlowLayoutDelegateMetricsWhenUpdates_ && updates_.hasModified()) {
102 | markUpdates();
103 | refreshSize = true;
104 | minSection = updates_.minSection();
105 | }
106 | else {
107 | refreshAll();
108 | refreshSizeAll = refreshSize = true;
109 | minSection = 0;
110 | }
111 | }
112 | else if (invalidation_.isInvalidateFlowLayoutDelegateMetrics()) {
113 | refreshAll();
114 | refreshSizeAll = refreshSize = true;
115 | minSection = 0;
116 | }
117 | if (!refreshSizeAll && invalidation_.isInvalidateFlowLayoutAttributes() && invalidation_.hasInvalidateAttributes()) {
118 | markInvalidation();
119 | refreshSize = true;
120 | minSection = std::min(minSection, invalidation_.minSection());
121 | }
122 |
123 | auto& offsets = invalidation_.invalidatedSectionContentOffsets();
124 |
125 | if (!refreshSize) {
126 | if (!offsets.empty()) {
127 | relayoutSectionContentOffset(offsets);
128 | }
129 | }
130 | else {
131 | updateContentOffsets(offsets);
132 | relayout(minSection == NSNotFound ? 0 : minSection);
133 | }
134 |
135 | invalidation_.reset();
136 | updates_.reset();
137 | }
138 |
139 | const auto& cachedPrevSections() { return cachedSections_; }
140 |
141 | void cacheResult() {
142 | cachedSections_ = sections_;
143 | }
144 |
145 | void clearCachedResult() {
146 | cachedSections_.clear();
147 | }
148 |
149 | #pragma mark - attributes
150 | void collectLayoutAttributesInRect(LayoutCollection& collection, const CGRect& rect, const CGRect& visibleRect) {
151 | for (auto& section : sections_) {
152 | section.collectLayoutAttributesInRect(context(), collection, rect, visibleRect);
153 | }
154 | }
155 |
156 | UICollectionViewLayoutAttributes *itemAttributedAtIndexPath(NSIndexPath *indexPath) {
157 | auto section = indexPath.section;
158 | if (section < sections_.size()) {
159 | return sections_.at(section).itemAttributeAtIndex(context_, indexPath.item);
160 | }
161 | return nil;
162 | }
163 |
164 | UICollectionViewLayoutAttributes *prevItemAttributedAtIndexPath(NSIndexPath *indexPath) {
165 | auto section = indexPath.section;
166 | if (section < cachedSections_.size()) {
167 | return cachedSections_.at(section).itemAttributeAtIndex(context_, indexPath.item);
168 | }
169 | return nil;
170 | }
171 |
172 | UICollectionViewLayoutAttributes *supplementaryAtIndexPath(NSString *kind, NSIndexPath *indexPath, const CGRect& visibleRect) {
173 | auto section = indexPath.section;
174 | if (section < sections_.size()) {
175 | return sections_.at(section).supplementaryAttributeAtIndex(context_, kind, indexPath.item, visibleRect);
176 | }
177 | return nil;
178 | }
179 |
180 | UICollectionViewLayoutAttributes *prevSupplementaryAtIndexPath(NSString *kind, NSIndexPath *indexPath, const CGRect& visibleRect) {
181 | auto section = indexPath.section;
182 | if (section < cachedSections_.size()) {
183 | return cachedSections_.at(section).supplementaryAttributeAtIndex(context_, kind, indexPath.item, visibleRect);
184 | }
185 | return nil;
186 | }
187 |
188 | UICollectionViewLayoutAttributes *prevDecorationAtIndexPath(NSString *kind, NSIndexPath *indexPath) {
189 | auto section = indexPath.section;
190 | if (section < cachedSections_.size()) {
191 | return cachedSections_.at(section).decorationAttributeAtIndex(context_, kind, indexPath.item);
192 | }
193 | return nil;
194 | }
195 |
196 | UICollectionViewLayoutAttributes *decorationAtIndexPath(NSString *kind, NSIndexPath *indexPath) {
197 | auto section = indexPath.section;
198 | if (section < sections_.size()) {
199 | return sections_.at(section).decorationAttributeAtIndex(context_, kind, indexPath.item);
200 | }
201 | return nil;
202 | }
203 |
204 | NSArray *sectionHeaderPinToVisibleIndexes() {
205 | NSMutableArray *array = [NSMutableArray array];
206 | for (auto& section : sections_) {
207 | if (section.headerPin() != PinToVisibleBounds::None
208 | && section.header().contentSize() != CGSizeZero) {
209 | [array addObject:[NSIndexPath indexPathForItem:0 inSection:section.section()]];
210 | }
211 | }
212 | return array;
213 | }
214 | NSArray *sectionFooterPinToVisibleIndexes() {
215 | NSMutableArray *array = [NSMutableArray array];
216 | for (auto& section : sections_) {
217 | if (section.footerPin() != PinToVisibleBounds::None
218 | && section.footer().contentSize() != CGSizeZero) {
219 | [array addObject:[NSIndexPath indexPathForItem:0 inSection:section.section()]];
220 | }
221 | }
222 | return array;
223 | }
224 |
225 | NSIndexPath *targetIndexPathAtPosition(const CGPoint& point) {
226 | for (auto& section : sections_) {
227 | if (CGRectContainsPoint(section.frame(), point)) {
228 | return section.targetIndexPathAtPosition(point);
229 | }
230 | }
231 | return nil;
232 | }
233 |
234 | CGSize contentSize() {
235 | return frame().size;
236 | }
237 | protected:
238 |
239 | void markLayoutDirty() {
240 | for (auto& section : sections_) {
241 | section.markLayoutDirty();
242 | }
243 | }
244 |
245 | void markInvalidation() {
246 | for (auto& idx : invalidation_.invalidateItems()) {
247 | auto& section = sections_.at(idx.section());
248 | section.markItemDataSourceDirty(idx.item());
249 | }
250 | for (auto& [kind, idxes] : invalidation_.invalidateSumplementaries()) {
251 | for (auto& idx : idxes) {
252 | auto& section = sections_.at(idx.section());
253 | section.markSupplementaryDataSourceDirty(kind, idx.item());
254 | }
255 | }
256 | for (auto& [kind, idxes] : invalidation_.invalidateDecorations()) {
257 | for (auto& idx : idxes) {
258 | auto& section = sections_.at(idx.section());
259 | section.markDecorationDataSrouceDirty(kind, idx.item());
260 | }
261 | }
262 | }
263 |
264 | void markUpdates() {
265 | using namespace NE::CollectionView;
266 | for (auto& item : updates_.updateItems()) {
267 | switch (item.action()) {
268 | case UpdateAction::Insert:
269 | if (item.isSection()) {
270 | auto& sections = item.sections();
271 | insertSections(sections);
272 | }
273 | else {
274 | auto& indexPaths = item.indexPaths();
275 | for (auto& indexPath : indexPaths) {
276 | auto& section = sections_.at(indexPath.section());
277 | section.insertItem(indexPath.item());
278 | }
279 | }
280 | break;
281 | case UpdateAction::Delete:
282 | if (item.isSection()) {
283 | auto& sections = item.sections();
284 | deleteSections(sections);
285 | }
286 | else {
287 | auto& indexPaths = item.indexPaths();
288 | for (auto& indexPath : indexPaths) {
289 | auto& section = sections_.at(indexPath.section());
290 | section.deleteItem(indexPath.item());
291 | }
292 | }
293 | break;
294 | case UpdateAction::Reload:
295 | if (item.isSection()) {
296 | auto& sections = item.sections();
297 | for (auto& section : sections) {
298 | sections_.at(section).markDataSourceDirty();
299 | }
300 | }
301 | else {
302 | auto& items = item.indexPaths();
303 | for (auto& indexPath : items) {
304 | sections_.at(indexPath.section()).markItemDataSourceDirty(indexPath.item());
305 | }
306 | }
307 | break;
308 | case UpdateAction::Move:
309 | if (item.isSection()) {
310 | auto fromIt = sections_.begin() + item.moveSections().first;
311 | auto toIt = sections_.begin() + item.moveSections().second;
312 | if (fromIt == toIt) {
313 | return;
314 | }
315 | if (fromIt < toIt) {
316 | fromIt->markLayoutDirty();
317 | for (; fromIt != toIt; ++fromIt) {
318 | std::iter_swap(fromIt, fromIt + 1);
319 | fromIt->markLayoutDirty();
320 | }
321 | }
322 | else {
323 | fromIt->markLayoutDirty();
324 | for (; fromIt != toIt; --fromIt) {
325 | std::iter_swap(fromIt, fromIt - 1);
326 | fromIt->markLayoutDirty();
327 | }
328 | }
329 | }
330 | else {
331 | auto& from = item.moveIndexPaths().first;
332 | auto& to = item.moveIndexPaths().second;
333 | if (from.section() == to.section()) {
334 | auto& section = sections_.at(from.section());
335 | section.markLayoutDirty();
336 | }
337 | else {
338 | auto& fromSection = sections_.at(from.section());
339 | auto& toSection = sections_.at(to.section());
340 | fromSection.deleteItem(from.item());
341 | toSection.insertItem(to.item());
342 | }
343 | }
344 | break;
345 | }
346 | }
347 | }
348 |
349 | void updateContentOffsets(const std::unordered_map& invalidatedOffsets) {
350 | if (invalidatedOffsets.empty()) return;
351 | for (auto& [index, offset] : invalidatedOffsets) {
352 | if (index < sections_.size()) {
353 | auto& section = sections_.at(index);
354 | section.setContentOffset(offset);
355 | }
356 | }
357 | }
358 |
359 | void refreshAll() {
360 | refreshDataSource();
361 | for (auto& section : sections_) {
362 | section.markDataSourceDirty();
363 | }
364 | }
365 | void refreshDataSource() {
366 | auto numberOfSections = context().numberOfSections();
367 | if (sections_.size() != numberOfSections) {
368 | sections_.resize(numberOfSections);
369 | }
370 | }
371 | void refreshIndexes() {
372 | NSUInteger idx = 0;
373 | for (auto& section : sections_) {
374 | section.setSection(idx);
375 | ++ idx;
376 | }
377 | }
378 |
379 | void relayoutSectionContentOffset(const std::unordered_map& invalidatedOffsets) {
380 | if (invalidatedOffsets.empty()) return;
381 | for (auto& [index, offset] : invalidatedOffsets) {
382 | if (index < sections_.size()) {
383 | auto& section = sections_.at(index);
384 | section.relayoutContentOffsetChanged(offset);
385 | }
386 | }
387 | }
388 |
389 | void relayout(NSInteger fromSection = 0) {
390 | refreshIndexes();
391 | auto origin = Node::origin();
392 |
393 | NSUInteger i = 0;
394 | auto sectionSpacing = context().sectionSpacing();
395 | if (context().sectionScrollDirection() == UICollectionViewScrollDirectionVertical) {
396 | auto contentSize = CGSize({
397 | .width = context().isCustomSectionWidth() ? context().sectionWidth() : bounds_.size.width,
398 | .height = 0
399 | });
400 | for (auto& section : sections_) {
401 | if (i >= fromSection) {
402 | auto newOrigin = CGPoint({
403 | .x = origin.x,
404 | .y = origin.y + contentSize.height + (i != 0 ? sectionSpacing : 0)
405 | });
406 | if (newOrigin != section.origin()) {
407 | section.setOrigin(newOrigin);
408 | section.markLayoutDirty();
409 | }
410 | section.setFitSize(contentSize);
411 |
412 | section.relayout(context());
413 | }
414 |
415 | contentSize.height += section.frame().size.height;
416 | if (i > 0) {
417 | contentSize.height += sectionSpacing;
418 | }
419 | ++i;
420 | }
421 | setSize(contentSize);
422 | }
423 | else {
424 | auto contentFitSize = CGSize({
425 | .width = context().isCustomSectionWidth() ? context().sectionWidth() : bounds_.size.width,
426 | .height = bounds_.size.height
427 | });
428 | auto contentSize = CGSize {
429 | .width = 0,
430 | .height = 0
431 | };
432 | for (auto& section : sections_) {
433 | if (i >= fromSection) {
434 | auto newOrigin = CGPoint({
435 | .x = origin.x + contentSize.width + (i != 0 ? sectionSpacing : 0),
436 | .y = origin.y
437 | });
438 | if (newOrigin != section.origin()) {
439 | section.setOrigin(newOrigin);
440 | section.markLayoutDirty();
441 | }
442 | section.setFitSize(contentFitSize);
443 |
444 | section.relayout(context());
445 | }
446 |
447 | auto size = section.frame().size;
448 | contentSize.width += size.width;
449 | // height should use max section height?
450 | contentSize.height = std::max(contentSize.height, size.height);
451 | if (i > 0) {
452 | contentSize.width += sectionSpacing;
453 | }
454 |
455 | ++i;
456 | }
457 | setSize(contentSize);
458 | }
459 | }
460 |
461 | void deleteSections(const std::set& idxes) {
462 | for (auto it = idxes.rbegin(); it != idxes.rend(); ++it) {
463 | deleteSection(*it);
464 | }
465 | }
466 |
467 | void deleteSection(NSUInteger idx) {
468 | sections_.erase(sections_.begin() + idx);
469 | }
470 |
471 | void insertSections(const std::set& idxes) {
472 | for (auto it = idxes.rbegin(); it != idxes.rend(); ++it) {
473 | insertSection(*it);
474 | }
475 | }
476 | void insertSection(NSUInteger idx) {
477 | sections_.emplace(sections_.begin() + idx);
478 | }
479 |
480 | private:
481 | Context context_;
482 | Invalidation invalidation_;
483 | Updates updates_;
484 | CGRect bounds_;
485 | CGRect visibleRect_;
486 | std::vector sections_;
487 | std::vector cachedSections_;
488 |
489 | bool pinToVisibleBounds_ = false;
490 | bool invalidateFlowLayoutDelegateMetricsWhenUpdates_ = false;
491 |
492 | // calculate value
493 | bool dirty_ = true;
494 | bool scrollOffsetDirty_ = true;
495 |
496 |
497 | Calculator(Calculator&) = delete;
498 | Calculator& operator =(Calculator&) = delete;
499 | };
500 |
501 | }
502 |
503 | #endif /* NECollectionViewFlowLayoutCalculator_h */
504 |
--------------------------------------------------------------------------------