实现采用的是UICollectionView和UICollectionViewFlowLayout。关于UICollectionView的详细讲解请参考
一、实现layout的DecorationView
//// FWBookShelfDecarationViewCollectionReusableView.h// FWPersonalApp//// Created by hzkmn on 16/2/18.// Copyright © 2016年 ForrstWoo. All rights reserved.//#import <UIKit/UIKit.h>extern NSInteger const kDecorationViewHeight;@interface FWBookShelfDecarationView : UICollectionReusableView@end
//// FWBookShelfDecarationViewCollectionReusableView.m// FWPersonalApp//// Created by hzkmn on 16/2/18.// Copyright © 2016年 ForrstWoo. All rights reserved.//#import "FWBookShelfDecarationView.h"NSInteger const kDecorationViewHeight = 216;@implementation FWBookShelfDecarationView- (instancetype)initWithFrame:(CGRect)frame{if (self = [super initWithFrame:frame]){UIImageView *img = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, screenSize.width, kDecorationViewHeight)];img.image = [UIImage imageNamed:@"boolshelf.png"];[self addSubview:img];}return self;}@endFWBookShelfDecarationView类非常简单只是定义了Decarationview的背景图片,图上。
//// LXReorderableCollectionViewFlowLayout.h//// Created by Stan Chang Khin Boon on 1/10/12.// Copyright (c) 2012 d--buzz. All rights reserved.//#import <UIKit/UIKit.h>@interface LXReorderableCollectionViewFlowLayout : UICollectionViewFlowLayout <UIGestureRecognizerDelegate>@property (assign, nonatomic) CGFloat scrollingSpeed;@property (assign, nonatomic) UIEdgeInsets scrollingTriggerEdgeInsets;@property (strong, nonatomic, readonly) UILongPressGestureRecognizer *longPressGestureRecognizer;@property (strong, nonatomic, readonly) UIPanGestureRecognizer *panGestureRecognizer;- (void)setUpGestureRecognizersOnCollectionView __attribute__((deprecated("Calls to setUpGestureRecognizersOnCollectionView method are not longer needed as setup are done automatically through KVO.")));@end@protocol LXReorderableCollectionViewDataSource <UICollectionViewDataSource>@optional- (void)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath willMoveToIndexPath:(NSIndexPath *)toIndexPath;- (void)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath didMoveToIndexPath:(NSIndexPath *)toIndexPath;- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath;- (BOOL)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath canMoveToIndexPath:(NSIndexPath *)toIndexPath;@end@protocol LXReorderableCollectionViewDelegateFlowLayout <UICollectionViewDelegateFlowLayout>@optional- (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout willBeginDraggingItemAtIndexPath:(NSIndexPath *)indexPath;- (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout didBeginDraggingItemAtIndexPath:(NSIndexPath *)indexPath;- (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout willEndDraggingItemAtIndexPath:(NSIndexPath *)indexPath;- (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout didEndDraggingItemAtIndexPath:(NSIndexPath *)indexPath;@end
//// LXReorderableCollectionViewFlowLayout.m//// Created by Stan Chang Khin Boon on 1/10/12.// Copyright (c) 2012 d--buzz. All rights reserved.//#import "LXReorderableCollectionViewFlowLayout.h"#import <QuartzCore/QuartzCore.h>#import <objc/runtime.h>#define LX_FRAMES_PER_SECOND 60.0#ifndef CGGEOMETRY_LXSUPPORT_H_CG_INLINE CGPointLXS_CGPointAdd(CGPoint point1, CGPoint point2) {return CGPointMake(point1.x + point2.x, point1.y + point2.y);}#endiftypedef NS_ENUM(NSInteger, LXScrollingDirection) {LXScrollingDirectionUnknown = 0,LXScrollingDirectionUp,LXScrollingDirectionDown,LXScrollingDirectionLeft,LXScrollingDirectionRight};static NSString * const kLXScrollingDirectionKey = @"LXScrollingDirection";static NSString * const kLXCollectionViewKeyPath = @"collectionView";@interface CADisplayLink (LX_userInfo)@property (nonatomic, copy) NSDictionary *LX_userInfo;@end@implementation CADisplayLink (LX_userInfo)- (void) setLX_userInfo:(NSDictionary *) LX_userInfo {objc_setAssociatedObject(self, "LX_userInfo", LX_userInfo, OBJC_ASSOCIATION_COPY);}- (NSDictionary *) LX_userInfo {return objc_getAssociatedObject(self, "LX_userInfo");}@end@interface UICollectionViewCell (LXReorderableCollectionViewFlowLayout)- (UIImage *)LX_rasterizedImage;@end@implementation UICollectionViewCell (LXReorderableCollectionViewFlowLayout)- (UIImage *)LX_rasterizedImage {UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0.0f);[self.layer renderInContext:UIGraphicsGetCurrentContext()];UIImage *image = UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();return image;}@end@interface LXReorderableCollectionViewFlowLayout ()@property (strong, nonatomic) NSIndexPath *selectedItemIndexPath;@property (strong, nonatomic) UIView *currentView;@property (assign, nonatomic) CGPoint currentViewCenter;@property (assign, nonatomic) CGPoint panTranslationInCollectionView;@property (strong, nonatomic) CADisplayLink *displayLink;@property (assign, nonatomic, readonly) id<LXReorderableCollectionViewDataSource> dataSource;@property (assign, nonatomic, readonly) id<LXReorderableCollectionViewDelegateFlowLayout> delegate;@end@implementation LXReorderableCollectionViewFlowLayout- (void)setDefaults {_scrollingSpeed = 300.0f;_scrollingTriggerEdgeInsets = UIEdgeInsetsMake(50.0f, 50.0f, 50.0f, 50.0f);}- (void)setupCollectionView {_longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:selfaction:@selector(handleLongPressGesture:)];_longPressGestureRecognizer.delegate = self;// Links the default long press gesture recognizer to the custom long press gesture recognizer we are creating now// by enforcing failure dependency so that they doesn"t clash.for (UIGestureRecognizer *gestureRecognizer in self.collectionView.gestureRecognizers) {if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) {[gestureRecognizer requireGestureRecognizerToFail:_longPressGestureRecognizer];}}[self.collectionView addGestureRecognizer:_longPressGestureRecognizer];_panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:selfaction:@selector(handlePanGesture:)];_panGestureRecognizer.delegate = self;[self.collectionView addGestureRecognizer:_panGestureRecognizer];// Useful in multiple scenarios: one common scenario being when the Notification Center drawer is pulled down[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleApplicationWillResignActive:) name: UIApplicationWillResignActiveNotification object:nil];}- (id)init {self = [super init];if (self) {[self setDefaults];[self addObserver:self forKeyPath:kLXCollectionViewKeyPath options:NSKeyValueObservingOptionNew context:nil];}return self;}- (id)initWithCoder:(NSCoder *)aDecoder {self = [super initWithCoder:aDecoder];if (self) {[self setDefaults];[self addObserver:self forKeyPath:kLXCollectionViewKeyPath options:NSKeyValueObservingOptionNew context:nil];}return self;}- (void)dealloc {[self invalidatesScrollTimer];[self removeObserver:self forKeyPath:kLXCollectionViewKeyPath];[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];}- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes {if ([layoutAttributes.indexPath isEqual:self.selectedItemIndexPath]) {layoutAttributes.hidden = YES;}}- (id<LXReorderableCollectionViewDataSource>)dataSource {return (id<LXReorderableCollectionViewDataSource>)self.collectionView.dataSource;}- (id<LXReorderableCollectionViewDelegateFlowLayout>)delegate {return (id<LXReorderableCollectionViewDelegateFlowLayout>)self.collectionView.delegate;}- (void)invalidateLayoutIfNecessary {NSIndexPath *newIndexPath = [self.collectionView indexPathForItemAtPoint:self.currentView.center];NSIndexPath *previousIndexPath = self.selectedItemIndexPath;if ((newIndexPath == nil) || [newIndexPath isEqual:previousIndexPath]) {return;}if ([self.dataSource respondsToSelector:@selector(collectionView:itemAtIndexPath:canMoveToIndexPath:)] &&![self.dataSource collectionView:self.collectionView itemAtIndexPath:previousIndexPath canMoveToIndexPath:newIndexPath]) {return;}self.selectedItemIndexPath = newIndexPath;if ([self.dataSource respondsToSelector:@selector(collectionView:itemAtIndexPath:willMoveToIndexPath:)]) {[self.dataSource collectionView:self.collectionView itemAtIndexPath:previousIndexPath willMoveToIndexPath:newIndexPath];}__weak typeof(self) weakSelf = self;[self.collectionView performBatchUpdates:^{__strong typeof(self) strongSelf = weakSelf;if (strongSelf) {[strongSelf.collectionView deleteItemsAtIndexPaths:@[ previousIndexPath ]];[strongSelf.collectionView insertItemsAtIndexPaths:@[ newIndexPath ]];}} completion:^(BOOL finished) {__strong typeof(self) strongSelf = weakSelf;if ([strongSelf.dataSource respondsToSelector:@selector(collectionView:itemAtIndexPath:didMoveToIndexPath:)]) {[strongSelf.dataSource collectionView:strongSelf.collectionView itemAtIndexPath:previousIndexPath didMoveToIndexPath:newIndexPath];}}];}- (void)invalidatesScrollTimer {if (!self.displayLink.paused) {[self.displayLink invalidate];}self.displayLink = nil;}- (void)setupScrollTimerInDirection:(LXScrollingDirection)direction {if (!self.displayLink.paused) {LXScrollingDirection oldDirection = [self.displayLink.LX_userInfo[kLXScrollingDirectionKey] integerValue];if (direction == oldDirection) {return;}}[self invalidatesScrollTimer];self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleScroll:)];self.displayLink.LX_userInfo = @{ kLXScrollingDirectionKey : @(direction) };[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];}#pragma mark - Target/Action methods// Tight loop, allocate memory sparely, even if they are stack allocation.- (void)handleScroll:(CADisplayLink *)displayLink {LXScrollingDirection direction = (LXScrollingDirection)[displayLink.LX_userInfo[kLXScrollingDirectionKey] integerValue];if (direction == LXScrollingDirectionUnknown) {return;}CGSize frameSize = self.collectionView.bounds.size;CGSize contentSize = self.collectionView.contentSize;CGPoint contentOffset = self.collectionView.contentOffset;UIEdgeInsets contentInset = self.collectionView.contentInset;// Important to have an integer `distance` as the `contentOffset` property automatically gets rounded// and it would diverge from the view"s center resulting in a "cell is slipping away under finger"-bug.CGFloat distance = rint(self.scrollingSpeed / LX_FRAMES_PER_SECOND);CGPoint translation = CGPointZero;switch(direction) {case LXScrollingDirectionUp: {distance = -distance;CGFloat minY = 0.0f - contentInset.top;if ((contentOffset.y + distance) <= minY) {distance = -contentOffset.y - contentInset.top;}translation = CGPointMake(0.0f, distance);} break;case LXScrollingDirectionDown: {CGFloat maxY = MAX(contentSize.height, frameSize.height) - frameSize.height + contentInset.bottom;if ((contentOffset.y + distance) >= maxY) {distance = maxY - contentOffset.y;}translation = CGPointMake(0.0f, distance);} break;case LXScrollingDirectionLeft: {distance = -distance;CGFloat minX = 0.0f - contentInset.left;if ((contentOffset.x + distance) <= minX) {distance = -contentOffset.x - contentInset.left;}translation = CGPointMake(distance, 0.0f);} break;case LXScrollingDirectionRight: {CGFloat maxX = MAX(contentSize.width, frameSize.width) - frameSize.width + contentInset.right;if ((contentOffset.x + distance) >= maxX) {distance = maxX - contentOffset.x;}translation = CGPointMake(distance, 0.0f);} break;default: {// Do nothing...} break;}self.currentViewCenter = LXS_CGPointAdd(self.currentViewCenter, translation);self.currentView.center = LXS_CGPointAdd(self.currentViewCenter, self.panTranslationInCollectionView);self.collectionView.contentOffset = LXS_CGPointAdd(contentOffset, translation);}- (void)handleLongPressGesture:(UILongPressGestureRecognizer *)gestureRecognizer {switch(gestureRecognizer.state) {case UIGestureRecognizerStateBegan: {NSIndexPath *currentIndexPath = [self.collectionView indexPathForItemAtPoint:[gestureRecognizer locationInView:self.collectionView]];if ([self.dataSource respondsToSelector:@selector(collectionView:canMoveItemAtIndexPath:)] &&![self.dataSource collectionView:self.collectionView canMoveItemAtIndexPath:currentIndexPath]) {return;}self.selectedItemIndexPath = currentIndexPath;if ([self.delegate respondsToSelector:@selector(collectionView:layout:willBeginDraggingItemAtIndexPath:)]) {[self.delegate collectionView:self.collectionView layout:self willBeginDraggingItemAtIndexPath:self.selectedItemIndexPath];}UICollectionViewCell *collectionViewCell = [self.collectionView cellForItemAtIndexPath:self.selectedItemIndexPath];self.currentView = [[UIView alloc] initWithFrame:collectionViewCell.frame];collectionViewCell.highlighted = YES;UIImageView *highlightedImageView = [[UIImageView alloc] initWithImage:[collectionViewCell LX_rasterizedImage]];highlightedImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;highlightedImageView.alpha = 1.0f;collectionViewCell.highlighted = NO;UIImageView *imageView = [[UIImageView alloc] initWithImage:[collectionViewCell LX_rasterizedImage]];imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;imageView.alpha = 0.0f;[self.currentView addSubview:imageView];[self.currentView addSubview:highlightedImageView];[self.collectionView addSubview:self.currentView];self.currentViewCenter = self.currentView.center;__weak typeof(self) weakSelf = self;[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{ __strong typeof(self) strongSelf = weakSelf; if (strongSelf) { strongSelf.currentView.transform = CGAffineTransformMakeScale(1.1f, 1.1f); highlightedImageView.alpha = 0.0f; imageView.alpha = 1.0f; } } completion:^(BOOL finished) { __strong typeof(self) strongSelf = weakSelf; if (strongSelf) { [highlightedImageView removeFromSuperview];if ([strongSelf.delegate respondsToSelector:@selector(collectionView:layout:didBeginDraggingItemAtIndexPath:)]) { [strongSelf.delegate collectionView:strongSelf.collectionView layout:strongSelf didBeginDraggingItemAtIndexPath:strongSelf.selectedItemIndexPath]; } } }];[self invalidateLayout];} break;case UIGestureRecognizerStateCancelled:case UIGestureRecognizerStateEnded: {NSIndexPath *currentIndexPath = self.selectedItemIndexPath;if (currentIndexPath) {if ([self.delegate respondsToSelector:@selector(collectionView:layout:willEndDraggingItemAtIndexPath:)]) {[self.delegate collectionView:self.collectionView layout:self willEndDraggingItemAtIndexPath:currentIndexPath];}self.selectedItemIndexPath = nil;self.currentViewCenter = CGPointZero;UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForItemAtIndexPath:currentIndexPath];__weak typeof(self) weakSelf = self;[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{ __strong typeof(self) strongSelf = weakSelf; if (strongSelf) { strongSelf.currentView.transform = CGAffineTransformMakeScale(1.0f, 1.0f); strongSelf.currentView.center = layoutAttributes.center; } } completion:^(BOOL finished) { __strong typeof(self) strongSelf = weakSelf; if (strongSelf) { [strongSelf.currentView removeFromSuperview]; strongSelf.currentView = nil; [strongSelf invalidateLayout];if ([strongSelf.delegate respondsToSelector:@selector(collectionView:layout:didEndDraggingItemAtIndexPath:)]) { [strongSelf.delegate collectionView:strongSelf.collectionView layout:strongSelf didEndDraggingItemAtIndexPath:currentIndexPath]; } } }];}} break;default: break;}}- (void)handlePanGesture:(UIPanGestureRecognizer *)gestureRecognizer {switch (gestureRecognizer.state) {case UIGestureRecognizerStateBegan:case UIGestureRecognizerStateChanged: {self.panTranslationInCollectionView = [gestureRecognizer translationInView:self.collectionView];CGPoint viewCenter = self.currentView.center = LXS_CGPointAdd(self.currentViewCenter, self.panTranslationInCollectionView);[self invalidateLayoutIfNecessary];switch (self.scrollDirection) {case UICollectionViewScrollDirectionVertical: {if (viewCenter.y < (CGRectGetMinY(self.collectionView.bounds) + self.scrollingTriggerEdgeInsets.top)) {[self setupScrollTimerInDirection:LXScrollingDirectionUp];} else {if (viewCenter.y > (CGRectGetMaxY(self.collectionView.bounds) - self.scrollingTriggerEdgeInsets.bottom)) {[self setupScrollTimerInDirection:LXScrollingDirectionDown];} else {[self invalidatesScrollTimer];}}} break;case UICollectionViewScrollDirectionHorizontal: {if (viewCenter.x < (CGRectGetMinX(self.collectionView.bounds) + self.scrollingTriggerEdgeInsets.left)) {[self setupScrollTimerInDirection:LXScrollingDirectionLeft];} else {if (viewCenter.x > (CGRectGetMaxX(self.collectionView.bounds) - self.scrollingTriggerEdgeInsets.right)) {[self setupScrollTimerInDirection:LXScrollingDirectionRight];} else {[self invalidatesScrollTimer];}}} break;}} break;case UIGestureRecognizerStateCancelled:case UIGestureRecognizerStateEnded: {[self invalidatesScrollTimer];} break;default: {// Do nothing...} break;}}#pragma mark - UICollectionViewLayout overridden methods- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {NSArray *layoutAttributesForElementsInRect = [super layoutAttributesForElementsInRect:rect];for (UICollectionViewLayoutAttributes *layoutAttributes in layoutAttributesForElementsInRect) {switch (layoutAttributes.representedElementCategory) {case UICollectionElementCategoryCell: {[self applyLayoutAttributes:layoutAttributes];} break;default: {// Do nothing...} break;}}return layoutAttributesForElementsInRect;}- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {UICollectionViewLayoutAttributes *layoutAttributes = [super layoutAttributesForItemAtIndexPath:indexPath];switch (layoutAttributes.representedElementCategory) {case UICollectionElementCategoryCell: {[self applyLayoutAttributes:layoutAttributes];} break;default: {// Do nothing...} break;}return layoutAttributes;}#pragma mark - UIGestureRecognizerDelegate methods- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {if ([self.panGestureRecognizer isEqual:gestureRecognizer]) {return (self.selectedItemIndexPath != nil);}return YES;}- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {if ([self.longPressGestureRecognizer isEqual:gestureRecognizer]) {return [self.panGestureRecognizer isEqual:otherGestureRecognizer];}if ([self.panGestureRecognizer isEqual:gestureRecognizer]) {return [self.longPressGestureRecognizer isEqual:otherGestureRecognizer];}return NO;}#pragma mark - Key-Value Observing methods- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {if ([keyPath isEqualToString:kLXCollectionViewKeyPath]) {if (self.collectionView != nil) {[self setupCollectionView];} else {[self invalidatesScrollTimer];}}}#pragma mark - Notifications- (void)handleApplicationWillResignActive:(NSNotification *)notification {self.panGestureRecognizer.enabled = NO;self.panGestureRecognizer.enabled = YES;}#pragma mark - Depreciated methods#pragma mark Starting from 0.1.0- (void)setUpGestureRecognizersOnCollectionView {// Do nothing...}@end效果图:
三、实现自己的layout
首先继承LXReorderableCollectionViewFlowLayout,让该类具有重新排序功能。
//// FWBookshelfCollectionViewLayout.h// FWPersonalApp//// Created by hzkmn on 16/2/18.// Copyright © 2016年 ForrstWoo. All rights reserved.//#import "LXReorderableCollectionViewFlowLayout.h"extern NSString * const FWBookshelfCollectionViewLayoutDecorationViewKind;@interface FWBookshelfCollectionViewLayout : LXReorderableCollectionViewFlowLayout@end
//// FWBookshelfCollectionViewLayout.m// FWPersonalApp//// Created by hzkmn on 16/2/18.// Copyright © 2016年 ForrstWoo. All rights reserved.//#import "FWBookshelfCollectionViewLayout.h"#import "FWBookShelfDecarationView.h"NSString * const FWBookshelfCollectionViewLayoutDecorationViewKind = @"FWBookshelfCollectionViewLayoutDecorationViewKind";@interface FWBookshelfCollectionViewLayout ()@property (nonatomic, strong) NSDictionary *bookShelfRectanges;@property NSInteger countOfRow;@end@implementation FWBookshelfCollectionViewLayout- (void)prepareLayout{[super prepareLayout];[self registerClass:[FWBookShelfDecarationView class] forDecorationViewOfKind:FWBookshelfCollectionViewLayoutDecorationViewKind];NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];NSInteger itemCount = [self.collectionView numberOfItemsInSection:0];self.countOfRow = ceilf(itemCount / 3.0);for (int row = 0; row < self.countOfRow; row++){dictionary[[NSIndexPath indexPathForItem:row inSection:0]] = [NSValue valueWithCGRect:CGRectMake(0, kDecorationViewHeight * row, screenSize.width, kDecorationViewHeight)];}self.bookShelfRectanges = [NSDictionary dictionaryWithDictionary:dictionary];}#pragma mark Runtime Layout Calculations- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{// call super so flow layout can return default attributes for all cells, headers, and footers// NOTE: Flow layout has already taken care of the Cell view layout attributes! :)NSArray *array = [super layoutAttributesForElementsInRect:rect];// create a mutable copy so we can add layout attributes for any shelfs that// have frames that intersect the rect the CollectionView is interested inNSMutableArray *newArray = [array mutableCopy];//NSLog(@"in rect:%@",NSStringFromCGRect(rect));// Add any decoration views (shelves) who"s rect intersects with the// CGRect passed to the layout by the CollectionView[self.bookShelfRectanges enumerateKeysAndObjectsUsingBlock:^(id key, id shelfRect, BOOL *stop) {//NSLog(@"[shelfRect CGRectValue]:%@",NSStringFromCGRect([shelfRect CGRectValue]));if (CGRectIntersectsRect([shelfRect CGRectValue], rect)){UICollectionViewLayoutAttributes *shelfAttributes =[self layoutAttributesForDecorationViewOfKind:FWBookshelfCollectionViewLayoutDecorationViewKind atIndexPath:key];[newArray addObject:shelfAttributes];}}];for (int i = 0; i < [self.collectionView numberOfItemsInSection:0]; i++){NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];[newArray addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];}return [newArray copy];}- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{//NSLog(@"%@", NSStringFromCGSize([self screenSize]));375 667UICollectionViewLayoutAttributes *attris = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];NSInteger currentRow = indexPath.item / 3;CGRect frame = CGRectMake(20 + (indexPath.item % 3) * (kCellWidth + 17.5), 35+ currentRow * (kCellHeight + 65), kCellWidth, kCellHeight);attris.frame = frame;attris.zIndex = 1;return attris;}- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind atIndexPath:(NSIndexPath *)indexPath{id shelfRect = self.bookShelfRectanges[indexPath];// this should never happen, but just in case...if (!shelfRect)return nil;UICollectionViewLayoutAttributes *attributes =[UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:decorationViewKindwithIndexPath:indexPath];attributes.frame = [shelfRect CGRectValue];//NSLog(@"UICollectionViewLayoutAttributes :.%@", NSStringFromCGRect([shelfRect CGRectValue]));attributes.zIndex = -1; // shelves go behind other viewsreturn attributes;}- (CGSize)collectionViewContentSize{CGSize contentSize = CGSizeMake(self.collectionView.bounds.size.width, self.countOfRow * kDecorationViewHeight + 20);return contentSize;}@end四、应用
//// FWAncientPoetryCollectionViewController.m// FWPersonalApp//// Created by hzkmn on 16/2/17.// Copyright © 2016年 ForrstWoo. All rights reserved.//#import "FWAncientPoetryCollectionViewController.h"#import "FWBookShelfDecarationView.h"#import "FWBookshelfCollectionViewLayout.h"#import "FWBookCategoryViewController.h"@interface FWAncientPoetryCollectionViewController () <LXReorderableCollectionViewDataSource, LXReorderableCollectionViewDelegateFlowLayout>@property (nonatomic, strong) NSMutableArray *books;@end@implementation FWAncientPoetryCollectionViewControllerstatic NSString * const cellReuseIdentifier = @"Cell";- (void)viewDidLoad {[super viewDidLoad];self.title = @"古籍";self.collectionView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"bookShelfBackground.png"]];[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:cellReuseIdentifier];self.books = [[NSMutableArray alloc] initWithArray:[self bookNameOfAllBooks]];}- (NSArray *)bookNameOfAllBooks{ return [[FWDataManager getDataForPoetry] allKeys];}#pragma mark <UICollectionViewDataSource>- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{return 1;}- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{return [self.books count];}- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellReuseIdentifier forIndexPath:indexPath];UIImage *image = [UIImage imageNamed:self.books[indexPath.item]];cell.backgroundColor = [UIColor colorWithPatternImage:image];return cell;}- (void)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath willMoveToIndexPath:(NSIndexPath *)toIndexPath{NSString *theBookName = self.books[fromIndexPath.item];[self.books removeObjectAtIndex:fromIndexPath.item];[self.books insertObject:theBookName atIndex:toIndexPath.item];}#pragma mark - UICollectionViewDelegateFlowLayout- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{return CGSizeMake(kCellWidth, kCellHeight);}- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{FWBookCategoryViewController *vc = [[FWBookCategoryViewController alloc] initWithUrlString:[[FWDataManager getDataForPoetry] objectForKey:self.books[indexPath.item]]];[self.navigationController pushViewController:vc animated:YES];}- (void)didReceiveMemoryWarning {[super didReceiveMemoryWarning];[self.books removeAllObjects];self.books = nil;}@end以上就是本文的全部内容,ios模仿实现书架效果,类似于手机上的掌上阅读软件的首页,希望本文对大家的学习有所帮助。