zoukankan      html  css  js  c++  java
  • 一个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=0; i<26; i++)
        {
            [self.letterArray addObject:[NSString stringWithFormat:@"%C",(unichar)(65+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: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
    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 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 = [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:18];
            [label sizeToFit];
            label.center = CGPointMake(frame.size.width/2, frame.size.height/2);
            [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: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
    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
  • 相关阅读:
    Fiddler 教程
    Mongodb for C# 分组查询
    C# CryptoStream
    ECharts 纯Javascript图表库
    Mongodb For C# "Query" 对象常用的方法
    WPF 获取指定文件的Icon
    SymbolSource
    ubuntu下安装Docker
    老李推荐:第1章2节《MonkeyRunner源码剖析》概述:边界
    老李推荐: 第1章1节《MonkeyRunner源码剖析》概述:前言
  • 原文地址:https://www.cnblogs.com/CoderWayne/p/4062197.html
Copyright © 2011-2022 走看看