一个UICollectionView自定义layout的实现

#import <UIKit/UIKit.h> @interface AppDelegate : UIResponder <UIApplicationDelegate> @property (strong, nonatomic) UIWindow *window; @property (strong, nonatomic) NSMutableArray *letterArray; @end
AppDelegate.h
#import "AppDelegate.h" @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.letterArray = [NSMutableArray array];
for(int i=; i<; i++)
{
[self.letterArray addObject:[NSString stringWithFormat:@"%C",(unichar)(+i)]];
} return YES;
}
AppDelegate.m
#import <UIKit/UIKit.h> @interface ViewController : UIViewController @end
ViewController.h
#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: inSection:];
NSIndexPath *path2 = [NSIndexPath indexPathForItem: inSection:];
NSIndexPath *path3 = [NSIndexPath indexPathForItem: inSection:];
NSIndexPath *path4 = [NSIndexPath indexPathForItem: inSection:]; NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(,)]; [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:@""]; [collectionView insertItemsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForItem:letterArray.count- inSection:]]];
} 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(, , , ) 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: to:+];
[letterArray addObject:[NSString stringWithFormat:@"%C",asciiX]];
NSIndexPath *path = [NSIndexPath indexPathForItem:letterArray.count- inSection:];
[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
ViewController.m
#import <UIKit/UIKit.h>
#import "UICollectionView+Draggable.h" @interface CollectionViewDataSource : NSObject<UICollectionViewDataSource_Draggable, UICollectionViewDelegate> @end
CollectionViewDataSource.h
#import <Foundation/Foundation.h>
#import "AppDelegate.h"
#import "CollectionViewDataSource.h" @implementation CollectionViewDataSource - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return ;
} - (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:];
[label sizeToFit];
label.center = CGPointMake(cell.bounds.size.width/, cell.bounds.size.height/);
[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:];
[label sizeToFit];
label.center = CGPointMake(view.bounds.size.width/, view.bounds.size.height/);
[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 = [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
CollectionViewDataSource.m
#import <UIKit/UIKit.h> @interface MyCollectionReusableView : UICollectionReusableView @end
MyCollectionReusableView.h
#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:];
[label sizeToFit];
label.center = CGPointMake(frame.size.width/, frame.size.height/);
[self addSubview:label];
} return self;
} @end
MyCollectionReusableView.m
#import <UIKit/UIKit.h> @interface CircleLayout : UICollectionViewLayout @end
CircleLayout.h
#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:];
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=; i<cellCount; i++)
{
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:]; UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath]; [array addObject:attributes];
} //add first supplementaryView
NSIndexPath *indexPath = [NSIndexPath indexPathForItem: inSection:];
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(, );
attributes.center = CGPointMake(cvCenter.x + radius * cosf( * indexPath.item * M_PI / cellCount),
cvCenter.y + radius * sinf( * indexPath.item * M_PI / cellCount)); return attributes;
} - (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:elementKind withIndexPath:indexPath]; attributes.size = CGSizeMake(, );
if([elementKind isEqual:@"FirstSupplementary"])
{
attributes.center = CGPointMake(cvSize.width/, );
}
else
{
attributes.center = CGPointMake(cvSize.width/, cvSize.height-);
} return attributes;
} - (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:elementKind withIndexPath:indexPath]; attributes.size = CGSizeMake(, );
attributes.center = CGPointMake(cvSize.width/, cvSize.height/); 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(,),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
CircleLayout.m
#import "CircleLayout.h"
#import "UICollectionViewLayout_Warpable.h" @interface DraggableCircleLayout : CircleLayout <UICollectionViewLayout_Warpable> @property (readonly, nonatomic) LSCollectionViewLayoutHelper *layoutHelper; @end
DraggableCircleLayout.h
#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
DraggableCircleLayout.m
一个UICollectionView自定义layout的实现的更多相关文章
- UICollectionView Layout自定义 Layout布局
from: http://www.tuicool.com/articles/vuyIriN 当我们使用系统自带的UICollectionViewFlowLayout无法实现我们的布局时,我们就可以 ...
- UICollectionView之自定义Layout
#import <UIKit/UIKit.h> @interface WQViewController : UIViewController - (id)initWithFrame:(CG ...
- 自定义 Layout布局 UICollectionViewLayout
from: http://www.tuicool.com/articles/vuyIriN 当我们使用系统自带的UICollectionViewFlowLayout无法实现我们的布局时,我们就可以 ...
- 安卓自定义控件(四)实现自定义Layout
本来我是不准备写这篇文章的,我实在想不出有什么样奇怪的理由,会去继承ViewGroup然后自定义一个布局,大概是我的项目经验还不够吧,想了好久,想到了这样一个需求: 需求 如图:在项目中经常有一个这样 ...
- UICollectionView自定义cell布局layout
写一个类继承UICollectionViewLayout,这个类需要提供一个数组来标识各个cell的属性信息,包括位置,size大小,返回一个UICollectionViewLayoutAttribu ...
- UICollectionView的水平流水布局自定义layout
最近做合创共美的商城项目,遇到发货地址的不配送地区,是做一个弹出框,弹出框的布局是根据地名字数长短不齐的标签. 本来也可以用tableview来做的.只不过多建几个tableviewcell就可以了. ...
- iOS开发之窥探UICollectionViewController(三) --使用UICollectionView自定义瀑布流
上篇博客的实例是自带的UICollectionViewDelegateFlowLayout布局基础上来做的Demo, 详情请看<iOS开发之窥探UICollectionViewControlle ...
- 利用UICollectionViewFlowLayout的隐式动画实现UICollectionView的layout的动画调整(外加放大指定cell效果)
前几天在gitHub看到个不错的效果,就是DaiExpandCollectionView,效果如图: 所以赶紧下下来源码看看他怎么实现的,打开源码看了半天,发现他没写什么关于动画的代码啊... 经 ...
- 用NSCalendar和UICollectionView自定义日历,并实现签到显示
前一段时间因为工作需要实现了一个可以签到的日历,来记录一下实现的思路 效果如图: 这里的基本需求是: 1,显示用户某个月的签到情况,已经签到的日子打个圈,没有签到且在某个时间范围内的可以签到,其他 ...
随机推荐
- linux 自定义yum仓库、repo文件 yum命令
目录 自定义yum仓库:createrepo 自定义repo文件 使用yum命令安装httpd软件包 卸载httpd软件包:yum –y remove 软件名 清除yum缓存:yum clean al ...
- Unix/Linux系统时间函数API
首先说明关于几个时间的概念: 世界时:起初,国际上的标准时间是格林尼治标准时间,以太阳横穿本初子午线的时刻为标准时间正午12点.它根据天文环境来定义,就像古代人们根据日晷来计时一样,如下图: 原子时: ...
- web项目打包后在代码中获取资源文件
在web项目里面,有时代码里面需要引用一些自定义的配置文件,这些配置文件如果放在类路径下,项目经过打包后使用的相对路径也会发生变化,所以以下给出了三种解决方案. 一.properties下配置 在类路 ...
- vue总结 06组件
组件基础 基本示例 这里有一个 Vue 组件的示例: // 定义一个名为 button-counter 的新组件Vue.component('button-counter', { data: func ...
- python网络编程-optparse
Python 有两个内建的模块用于处理命令行参数: 一个是 getopt,<Deep in python>一书中也有提到,只能简单处理 命令行参数: 另一个是 optparse,它功能强大 ...
- java基础73 dom4j修改xml里面的内容(网页知识)
1.DOM4J对XML文件进行增删改操作 实现代码 package com.shore.code; import java.io.File; import java.io.FileOutputStre ...
- 洛谷P1841重要的城市
传送门啦 重要城市有三个性质如下: 1.重要城市能对其他两个不同城市的最短路径做出贡献 2.重要城市具有唯一性,如果两不同城市之间的最短路径有两种中间城市情况,那么这两个中间城市可以彼此代替,就都不能 ...
- js中的事件委托或是事件代理
JavaScript(jQuery)中的事件委托 https://www.cnblogs.com/zhoushengxiu/p/5703095.html js中的事件委托或是事件代理详解 https: ...
- [扩展推荐] Laravel 中利用 GeoIP 获取用户地理位置信息
我最近需要一个用户地域检测来设置用户的默认区域和货币.由 Daniel Stainback 创建的 torann/geoip 很好地满足为Laravel 5 项目提供 GeoIP 服务的要求. 这个 ...
- 2017 MoveIt!更新 ros indigo
First MoveIt! Update in 2017. Using it on NEXTAGE pt.1 2017 MoveIt! update pt.2; Stopping motion on ...