首页 > 代码库 > 一个UICollectionView自定义layout的实现
一个UICollectionView自定义layout的实现
#import <UIKit/UIKit.h>@interface AppDelegate : UIResponder <UIApplicationDelegate>@property (strong, nonatomic) UIWindow *window;@property (strong, nonatomic) NSMutableArray *letterArray;@end
#import "AppDelegate.h"@interface AppDelegate ()@end@implementation AppDelegate- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ self.letterArray = [NSMutableArray array]; for(int i=0; i<26; i++) { [self.letterArray addObject:[NSString stringWithFormat:@"%C",(unichar)(65+i)]]; } return YES;}
#import <UIKit/UIKit.h>@interface ViewController : UIViewController@end
#import "ViewController.h"#import "AppDelegate.h"#import "CollectionViewDataSource.h"#import "DraggableCircleLayout.h"#import "LSCollectionViewHelper.h"@interface ViewController (){ UICollectionView *collectionView; CollectionViewDataSource *cvDataSource;}@end@implementation ViewController- (IBAction)ChangeLayoutClickHandler:(id)sender{ if([collectionView.collectionViewLayout isKindOfClass:[CircleLayout class]]) { UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; collectionView.collectionViewLayout = layout; } else { CircleLayout *layout = [[CircleLayout alloc] init]; collectionView.collectionViewLayout = layout; }}- (IBAction)BatchUploadClickHandler:(id)sender{ //这里有个细节需要注意,最好是将删除操作放在添加操作前面,因为无论你顺序如何,始终都会先执行删除操作。 //如果代码顺序是先添加后删除,但实际执行顺序是先删除后添加,可能会因为索引不对影响代码逻辑。 [collectionView performBatchUpdates:^{ NSMutableArray *letterArray = [self getLetterArray]; //删除四个元素 NSIndexPath *path1 = [NSIndexPath indexPathForItem:0 inSection:0]; NSIndexPath *path2 = [NSIndexPath indexPathForItem:1 inSection:0]; NSIndexPath *path3 = [NSIndexPath indexPathForItem:2 inSection:0]; NSIndexPath *path4 = [NSIndexPath indexPathForItem:3 inSection:0]; NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0,4)]; [indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){ NSLog(@"%lu", (unsigned long)idx); }]; [letterArray removeObjectsAtIndexes:indexSet]; NSArray *array = [NSArray arrayWithObjects:path1, path2, path3, path4, nil]; [collectionView deleteItemsAtIndexPaths:array]; //添加一个元素 [letterArray addObject:@"1"]; [collectionView insertItemsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForItem:letterArray.count-1 inSection:0]]]; } completion:nil];}- (void)viewDidLoad{ [super viewDidLoad];// UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];// CircleLayout *layout = [[CircleLayout alloc] init]; DraggableCircleLayout *layout = [[DraggableCircleLayout alloc] init]; collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(10, 50, 300, 400) collectionViewLayout:layout]; collectionView.backgroundColor = [UIColor grayColor]; collectionView.draggable = YES; [collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"LetterCell"]; [collectionView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:@"FirstSupplementary" withReuseIdentifier:@"ReuseID"]; [collectionView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:@"SecondSupplementary" withReuseIdentifier:@"ReuseID"]; cvDataSource = [CollectionViewDataSource alloc]; collectionView.dataSource = cvDataSource; collectionView.delegate = cvDataSource; [self.view addSubview:collectionView]; UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureHandler:)]; [collectionView addGestureRecognizer:tapRecognizer];}- (void)tapGestureHandler:(UITapGestureRecognizer *)sender{ CGPoint point = [sender locationInView:collectionView]; NSIndexPath *tappedCellPath = [collectionView indexPathForItemAtPoint:point]; NSMutableArray *letterArray = [self getLetterArray]; if(tappedCellPath) { //删除点击的cell [letterArray removeObjectAtIndex:tappedCellPath.item]; [collectionView deleteItemsAtIndexPaths:[NSArray arrayWithObject:tappedCellPath]]; } else { //如果点击空白处,在末尾添加一个随机小写字母 unichar asciiX = (unichar)[self getRandomNumber:97 to:97+26]; [letterArray addObject:[NSString stringWithFormat:@"%C",asciiX]]; NSIndexPath *path = [NSIndexPath indexPathForItem:letterArray.count-1 inSection:0]; [collectionView insertItemsAtIndexPaths:[NSArray arrayWithObject:path]]; }}- (NSMutableArray *)getLetterArray{ AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; return appDelegate.letterArray;}- (int)getRandomNumber:(int)from to:(int)to{ return (int)(from + (arc4random() % (to-from)));}- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.}@end
#import <UIKit/UIKit.h>#import "UICollectionView+Draggable.h"@interface CollectionViewDataSource : NSObject<UICollectionViewDataSource_Draggable, UICollectionViewDelegate>@end
#import <Foundation/Foundation.h>#import "AppDelegate.h"#import "CollectionViewDataSource.h"@implementation CollectionViewDataSource- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{ return 1;}- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{ return [self getLetterArray].count;}- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"LetterCell" forIndexPath:indexPath]; //先移除可重用cell里面的子元素(否则会出现新旧交叠) [cell.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; cell.backgroundColor = [UIColor yellowColor]; UILabel *label = [[UILabel alloc] init]; label.text = [[self getLetterArray] objectAtIndex:indexPath.row]; label.font = [UIFont systemFontOfSize:12]; [label sizeToFit]; label.center = CGPointMake(cell.bounds.size.width/2, cell.bounds.size.height/2); [cell addSubview:label]; return cell;}- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath{ UICollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@"ReuseID" forIndexPath:indexPath]; view.backgroundColor = [UIColor greenColor]; UILabel *label = [[UILabel alloc] init]; label.text = kind; label.font = [UIFont systemFontOfSize:24]; [label sizeToFit]; label.center = CGPointMake(view.bounds.size.width/2, view.bounds.size.height/2); [view addSubview:label]; return view;}- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{ NSLog(@"你选择了"); // [self.myArray removeObjectAtIndex:indexPath.row];// // [collectionView deleteItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]];}- (BOOL)collectionView:(LSCollectionViewHelper *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath{ NSLog(@"canMoveItemAtIndexPath"); return YES;}- (void)collectionView:(LSCollectionViewHelper *)collectionView moveItemAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath{ NSLog(@"moveItemAtIndexPath"); NSMutableArray *data =http://www.mamicode.com/ [self getLetterArray]; NSNumber *index = [data objectAtIndex:fromIndexPath.item]; [data removeObjectAtIndex:fromIndexPath.item]; [data insertObject:index atIndex:toIndexPath.item];}- (NSMutableArray *)getLetterArray{ AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; return appDelegate.letterArray;}@end
#import <UIKit/UIKit.h>@interface MyCollectionReusableView : UICollectionReusableView@end
#import "MyCollectionReusableView.h"@implementation MyCollectionReusableView- (instancetype)initWithFrame:(CGRect)frame{ self = [super initWithFrame:frame]; if (self) { self.backgroundColor = [UIColor orangeColor]; UILabel *label = [[UILabel alloc] init]; label.text = @"Decoration View"; label.font = [UIFont systemFontOfSize:18]; [label sizeToFit]; label.center = CGPointMake(frame.size.width/2, frame.size.height/2); [self addSubview:label]; } return self;}@end
#import <UIKit/UIKit.h>@interface CircleLayout : UICollectionViewLayout@end
#import "AppDelegate.h"#import "CircleLayout.h"#import "CollectionViewDataSource.h"#import "MyCollectionReusableView.h"@interface CircleLayout(){ CGSize cvSize; CGPoint cvCenter; CGFloat radius; NSInteger cellCount;}@property (strong, nonatomic) NSMutableArray *indexPathsToAnimate;@end@implementation CircleLayout- (void)prepareLayout{ [super prepareLayout]; [self registerClass:[MyCollectionReusableView class] forDecorationViewOfKind:@"MyDecoration"]; cvSize = self.collectionView.frame.size; cellCount = [self.collectionView numberOfItemsInSection:0]; cvCenter = CGPointMake(cvSize.width / 2.0, cvSize.height / 2.0); radius = MIN(cvSize.width, cvSize.height) / 2.5;}- (CGSize)collectionViewContentSize{ return self.collectionView.bounds.size;}- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{ NSMutableArray *array = [NSMutableArray array]; //add cells for (int i=0; i<cellCount; i++) { NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0]; UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath]; [array addObject:attributes]; } //add first supplementaryView NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0]; UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForSupplementaryViewOfKind:@"FirstSupplementary" atIndexPath:indexPath]; [array addObject:attributes]; //add second supplementaryView attributes = [self layoutAttributesForSupplementaryViewOfKind:@"SecondSupplementary" atIndexPath:indexPath]; [array addObject:attributes]; //add decorationView attributes = [self layoutAttributesForDecorationViewOfKind:@"MyDecoration" atIndexPath:indexPath]; [array addObject:attributes]; return array;}- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{ UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; attributes.size = CGSizeMake(20, 20); attributes.center = CGPointMake(cvCenter.x + radius * cosf(2 * indexPath.item * M_PI / cellCount), cvCenter.y + radius * sinf(2 * indexPath.item * M_PI / cellCount)); return attributes;}- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath{ UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:elementKind withIndexPath:indexPath]; attributes.size = CGSizeMake(260, 40); if([elementKind isEqual:@"FirstSupplementary"]) { attributes.center = CGPointMake(cvSize.width/2, 40); } else { attributes.center = CGPointMake(cvSize.width/2, cvSize.height-40); } return attributes;}- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath{ UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:elementKind withIndexPath:indexPath]; attributes.size = CGSizeMake(140, 40); attributes.center = CGPointMake(cvSize.width/2, cvSize.height/2); return attributes;}//当边界更改时是否更新布局- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{ CGRect oldBounds = self.collectionView.bounds; if (CGRectGetWidth(newBounds) != CGRectGetWidth(oldBounds)) { return YES; } return NO;}//通知布局,collection view里有元素即将改变,这里可以收集改变的元素indexPath和action类型。-(void)prepareForCollectionViewUpdates:(NSArray *)updateItems{ [super prepareForCollectionViewUpdates:updateItems]; NSMutableArray *indexPaths = [NSMutableArray array]; for(UICollectionViewUpdateItem *updateItem in updateItems) { //UICollectionUpdateActionInsert, //UICollectionUpdateActionDelete, //UICollectionUpdateActionReload, //UICollectionUpdateActionMove, //UICollectionUpdateActionNone NSLog(@"before index:%d,after index:%d,action:%d", updateItem.indexPathBeforeUpdate.row,updateItem.indexPathAfterUpdate.row,updateItem.updateAction); switch (updateItem.updateAction) { case UICollectionUpdateActionInsert: [indexPaths addObject:updateItem.indexPathAfterUpdate]; break; case UICollectionUpdateActionDelete: [indexPaths addObject:updateItem.indexPathBeforeUpdate]; break; case UICollectionUpdateActionMove: [indexPaths addObject:updateItem.indexPathBeforeUpdate]; [indexPaths addObject:updateItem.indexPathAfterUpdate]; break; default: NSLog(@"unhandled case: %@", updateItem); break; } } self.indexPathsToAnimate = indexPaths;}//当一个元素被插入collection view时,返回它的初始布局,这里可以加入一些动画效果。- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath{ UICollectionViewLayoutAttributes *attr = [self layoutAttributesForItemAtIndexPath:itemIndexPath]; if([self.indexPathsToAnimate containsObject:itemIndexPath]) { attr.transform = CGAffineTransformRotate(CGAffineTransformMakeScale(10,10),M_PI); attr.center = CGPointMake(CGRectGetMidX(self.collectionView.bounds), CGRectGetMidY(self.collectionView.bounds)); [self.indexPathsToAnimate removeObject:itemIndexPath]; } return attr;}- (NSArray *)getLetterArray{ AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; return appDelegate.letterArray;}@end
#import "CircleLayout.h"#import "UICollectionViewLayout_Warpable.h"@interface DraggableCircleLayout : CircleLayout <UICollectionViewLayout_Warpable>@property (readonly, nonatomic) LSCollectionViewLayoutHelper *layoutHelper;@end
#import "DraggableCircleLayout.h"#import "LSCollectionViewLayoutHelper.h"@interface DraggableCircleLayout(){ LSCollectionViewLayoutHelper *_layoutHelper;}@end@implementation DraggableCircleLayout- (LSCollectionViewLayoutHelper *)layoutHelper{ if(_layoutHelper == nil) { _layoutHelper = [[LSCollectionViewLayoutHelper alloc] initWithCollectionViewLayout:self]; } return _layoutHelper;}- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{ return [self.layoutHelper modifiedLayoutAttributesForElements:[super layoutAttributesForElementsInRect:rect]];}@end
一个UICollectionView自定义layout的实现
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。