本文完成了相似電子書首頁,用來展現圖書或小說的規劃頁面,書架列表【iPhone6模仿器】,屏幕尺寸還沒停止適配,只是做個復雜的demo【純代碼完成方式】
完成采用的是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; } @end
FWBookShelfDecarationView類十分復雜只是定義了Decarationview的背景圖片,圖上。
二、下載及導入可重新排序的第三方layout,用於我們挪動圖書後重新規劃
在實踐項目中你或許會遇到在一個集合視圖中挪動一項到另外一個地位,那麼此時我們需求對視圖中的元素停止重新排序,明天引薦一個很好用的第三方類LXReorderableCollectionViewFlowLayout【點此鏈接進入GITHUB】
上面附上完成代碼
// // 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 CGPoint LXS_CGPointAdd(CGPoint point1, CGPoint point2) { return CGPointMake(point1.x + point2.x, point1.y + point2.y); } #endif typedef 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:self action:@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:self action:@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 performDosBat target=_blank class=infotextkey>BatchUpdates:^{ __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 = CGAff.netransformMakeScale(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 = CGAff.netransformMakeScale(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 in NSMutableArray *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 667 UICollectionViewLayoutAttributes *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:decorationViewKind withIndexPath:indexPath]; attributes.frame = [shelfRect CGRectValue]; // NSLog(@"UICollectionViewLayoutAttributes :.%@", NSStringFromCGRect([shelfRect CGRectValue])); attributes.zIndex = -1; // shelves go behind other views return 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 FWAncientPoetryCollectionViewController static 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模擬完成書架效果,相似於手機上的掌上閱讀軟件的首頁,希望本文對大家的學習有所協助。
【iOS模擬電子書首頁完成書架規劃款式】的相關資料介紹到這裡,希望對您有所幫助! 提示:不會對讀者因本文所帶來的任何損失負責。如果您支持就請把本站添加至收藏夾哦!