引用自:http://www.lxweimin.com/p/c59a5c92f859
一、UICollectionVIew基礎
第一步:創建布局對象:
如果使用系統的布局類,我們通常是使用其UICollectionViewFlowLayout子類,通過這個子類來創建我們的布局對象。實例:UICollectionVIewFlowLayout *flowLayout = [UICollectionVIewFlowLayout new];
通過這個實例化的對象提供了一些基本的屬性,用于幫助我們控制布局的方式,當然這個類也有其自身的協議,通過簽署協議可以實現協議中的一些方法,這些方法可以幫助我們實現更加完善的布局。協議名字叫做:“UICollectionVIewDelegateFlowLayout”,這個協議也是實現基本CollectionVIew的三個協議中必須用到的,其他另外兩個協議分別是:UICollectionViewDataSource(數據源)、UICollectionViewDelegate。
下面簡單介紹一些關于這個類對象用于實現布局的屬性有哪些:
@property(nonatomic)CGFloatminimumLineSpacing;// 這個用于指定每個Item的行間距,默認間距是10.
@property(nonatomic)CGFloatminimumInteritemSpacing;//用于指定每個單元的列間距。
@property(nonatomic)CGSizeitemSize;//指定每個cell的size.
@property(nonatomic)CGSizeestimatedItemSizeNS_AVAILABLE_IOS(8_0);//defaults to CGSizeZero - setting a non-zero size enables cells that self-size via -preferredLayoutAttributesFittingAttributes:
@property(nonatomic)UICollectionViewScrollDirectionscrollDirection;// default is UICollectionViewScrollDirectionVertical
@property(nonatomic)CGSizeheaderReferenceSize;
@property(nonatomic)CGSizefooterReferenceSize;//頭部增添視圖的size
@property(nonatomic)UIEdgeInsetssectionInset;//設置分區的內邊距距離上左下右的距離,所以這個參數有四個參數。
// Set these properties to YES to get headers that pin to the top of the screen and footers that pin to the bottom while scrolling (similar to UITableView).
@property(nonatomic)BOOLsectionHeadersPinToVisibleBoundsNS_AVAILABLE_IOS(9_0);
@property(nonatomic)BOOLsectionFootersPinToVisibleBoundsNS_AVAILABLE_IOS(9_0);c//用于控制是否在當前窗口顯示下一個分區的尾增添
第二步:通過布局對象創建UICollectionView,并設置相關屬性,重點是給代理屬性給定對象。
初始化UICollectionVIew的方法:
Frame:指定UICollectionView的Frame
collectionViewLayout:指定UICollectView的布局對象。
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout*)layoutNS_DESIGNATED_INITIALIZER;
第三步:設置好各個重用標識
這里就不多說了,在代碼的全局位置指定三個全局字符常量。
第四步:使用重用標識注冊相關的重用池。
cell的重用池的注冊方法:
Class:指定用哪個類作為cell,所以我們可以通過新建繼承自UICollectionViewLayout的類來自定義cell.
- (void)registerClass:(nullableClass)cellClass forCellWithReuseIdentifier:(NSString*)identifier;
補充視圖重用池的注冊方法:
forSupplementaryViewOfKind:指定補充視圖的類型
withReuseIdentifier:指定一個重用標識符
- (void)registerClass:(nullableClass)viewClass forSupplementaryViewOfKind:(NSString*)elementKind withReuseIdentifier:(NSString*)identifier;
第五步:實現UICollectionViewDataSource的幾個代理方法(這里只提幾個常用的)
#pragma?mark?----?UICollectionViewDataSource
// ?指定在一個CollectionView上分區的數量。
-?(NSInteger)numberOfSectionsInCollectionView:(UICollectionView?*)collectionView
{
return?1;
}
// 指定在每個分區上Item的數量。
-?(NSInteger)collectionView:(UICollectionView?*)collectionView?numberOfItemsInSection:(NSInteger)section
{
return?_section0Array.count;
}
// 從重用池中取出UICollectionVIew的celle來給每個Item賦值。
-?(UICollectionViewCell?*)collectionView:(UICollectionView?*)collectionView?cellForItemAtIndexPath:(NSIndexPath?*)indexPath
{
UICollectionViewCell?*cell?=?[_collectionView?dequeueReusableCellWithReuseIdentifier:cellId?forIndexPath:indexPath];
cell.backgroundColor?=?[UIColor?purpleColor];
return?cell;
}
//?和UITableView類似,UICollectionView也可設置段頭段尾
這個方法中的參數分別是:
collectionView:這個就不解釋了,每個協議中必須傳入的跟類。
viewForSupplementaryElementOfKind:根據傳入的Kind值來判斷到底添加的是那種類型的視圖。
atIndexPath:這個參數通過傳值來確定到底給那個分區補充視圖。
-?(UICollectionReusableView?*)collectionView:(UICollectionView?*)collectionView?viewForSupplementaryElementOfKind:(NSString?*)kind?atIndexPath:(NSIndexPath?*)indexPath
{
if([kind?isEqualToString:UICollectionElementKindSectionHeader])
{
UICollectionReusableView?*headerView?=?[_collectionView?dequeueReusableSupplementaryViewOfKind:kind?withReuseIdentifier:headerId?forIndexPath:indexPath];
if(headerView?==?nil)
{
headerView?=?[[UICollectionReusableView?alloc]?init];
}
headerView.backgroundColor?=?[UIColor?grayColor];
return?headerView;
}
else?if([kind?isEqualToString:UICollectionElementKindSectionFooter])
{
UICollectionReusableView?*footerView?=?[_collectionView?dequeueReusableSupplementaryViewOfKind:kind?withReuseIdentifier:footerId?forIndexPath:indexPath];
if(footerView?==?nil)
{
footerView?=?[[UICollectionReusableView?alloc]?init];
}
footerView.backgroundColor?=?[UIColor?lightGrayColor];
return?footerView;
}
return?nil;
}
// 暫時理解為:能夠移動的Item,有待驗證。
-?(BOOL)collectionView:(UICollectionView?*)collectionView?canMoveItemAtIndexPath:(NSIndexPath?*)indexPath
{
return?YES;
}
// ?暫時理解為:給能夠移動的Item指定移動路徑,有待驗證。
moveItemAtIndexPath:能夠一定的Item路徑。
toIndexPath:移動到的目標路徑。
-?(void)collectionView:(UICollectionView?*)collectionView?moveItemAtIndexPath:(NSIndexPath?*)sourceIndexPath?toIndexPath:(NSIndexPath*)destinationIndexPath
{
}
第六步:實現UICollectionViewDelegateFlowLayout協議方法(這里的很多方法用屬性的點語法同樣可以實現,建議使用點語法。)
-?(CGSize)collectionView:(UICollectionView?*)collectionView?layout:(UICollectionViewLayout*)collectionViewLayout?sizeForItemAtIndexPath:(NSIndexPath?*)indexPath
{
return?(CGSize){cellWidth,cellWidth};
}
-?(UIEdgeInsets)collectionView:(UICollectionView?*)collectionView?layout:(UICollectionViewLayout*)collectionViewLayout?insetForSectionAtIndex:(NSInteger)section
{
return?UIEdgeInsetsMake(5,?5,?5,?5);
}
-?(CGFloat)collectionView:(UICollectionView?*)collectionView?layout:(UICollectionViewLayout*)collectionViewLayout?minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
return?5.f;
}
-?(CGFloat)collectionView:(UICollectionView?*)collectionView?layout:(UICollectionViewLayout*)collectionViewLayout?minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
return?5.f;
}
-?(CGSize)collectionView:(UICollectionView?*)collectionView?layout:(UICollectionViewLayout*)collectionViewLayout?referenceSizeForHeaderInSection:(NSInteger)section
{
return?(CGSize){ScreenWidth,44};
}
-?(CGSize)collectionView:(UICollectionView?*)collectionView?layout:(UICollectionViewLayout*)collectionViewLayout?referenceSizeForFooterInSection:(NSInteger)section
{
return?(CGSize){ScreenWidth,22};
}
第七步:實現UICollectionViewDelegate方法
// 指定那些Item需要高亮顯示
-?(BOOL)collectionView:(UICollectionView?*)collectionView?shouldHighlightItemAtIndexPath:(NSIndexPath?*)indexPath
{
return?YES;
}
//?點擊高亮
-?(void)collectionView:(UICollectionView?*)collectionView?didHighlightItemAtIndexPath:(NSIndexPath?*)indexPath
{
UICollectionViewCell?*cell?=?[collectionView?cellForItemAtIndexPath:indexPath];
cell.backgroundColor?=?[UIColor?greenColor];
}
//?選中某item
-?(void)collectionView:(UICollectionView?*)collectionView?didSelectItemAtIndexPath:(NSIndexPath?*)indexPath
{
}
//?長按某item,彈出copy和paste的菜單
-?(BOOL)collectionView:(UICollectionView?*)collectionView?shouldShowMenuForItemAtIndexPath:(NSIndexPath?*)indexPath
{
return?YES;
}
//?使copy和paste有效
-?(BOOL)collectionView:(UICollectionView?*)collectionView?canPerformAction:(SEL)action?forItemAtIndexPath:(NSIndexPath?*)indexPath?withSender:(nullable?id)sender
{
if?([NSStringFromSelector(action)?isEqualToString:@"copy:"]?||?[NSStringFromSelector(action)?isEqualToString:@"paste:"])
{
return?YES;
}
return?NO;
}
//
-?(void)collectionView:(UICollectionView?*)collectionView?performAction:(SEL)action?forItemAtIndexPath:(NSIndexPath?*)indexPath?withSender:(nullable?id)sender
{
if([NSStringFromSelector(action)?isEqualToString:@"copy:"])
{
//? ? ? ? NSLog(@"-------------執行拷貝-------------");
[_collectionView?performBatchUpdates:^{
[_section0Array?removeObjectAtIndex:indexPath.row];
[_collectionView?deleteItemsAtIndexPaths:@[indexPath]];
}?completion:nil];
}
else?if([NSStringFromSelector(action)?isEqualToString:@"paste:"])
{
NSLog(@"-------------執行粘貼-------------");
}
}
二、自定義布局
UICollectionView自定義布局
要自定義UICollectionView布局,就要子類化UICollectionViewLayout,然后重寫它的一些方法以達到我們自定義布局的需求。下來我們來看看UICollectionViewLayout類里一些比較重要的方法:
- (void)prepareLayout;為layout顯示做準備工作,你可以在該方法里設置一些屬性。
- (CGSize)collectionViewContentSize;返回layout的size。
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect;返回在collectionView的可見范圍內(bounds)所有item對應的layoutAttrure對象裝成的數組。collectionView的每個item都對應一個專門的UICollectionViewLayoutAttributes類型的對象來表示該item的一些屬性,比如bounds,size,transform,alpha等。
- (UICollectionViewLayoutAttributes)layoutAttributesForItemAtIndexPath:(NSIndexPath)indexPath;傳入indexPath,返回該indexPath對應的layoutAtture對象。
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds;當當前layout的布局發生變動時,是否重寫加載該layout。默認返回NO,若返回YES,則重新執行這倆方法:
- (void)prepareLayout;
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect;
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity;返回layout“最終”的偏移量,何謂“最終”,手指離開屏幕時layout的偏移量不是最終的,因為它有慣性,當它停止時才是“最終”偏移量。
自定義插入刪除動畫
插入刪除的操作
添加在哪觸發:
UIBarButtonItem?*btnItem?=?[[UIBarButtonItem?alloc]?initWithTitle:@"添加"
style:UIBarButtonItemStylePlain
target:self
action:@selector(addItemBtnClick:)];
self.navigationItem.rightBarButtonItem?=?btnItem;
添加的實現:
//?添加(插入item)
-?(void)addItemBtnClick:(UIBarButtonItem?*)btnItem
{
[_collectionView?performBatchUpdates:^{
//?構造一個indexPath
NSIndexPath?*indePath?=?[NSIndexPath?indexPathForItem:_section0Array.count?inSection:0];
[_collectionView?insertItemsAtIndexPaths:@[indePath]];?//?然后在此indexPath處插入給collectionView插入一個item
[_section0Array?addObject:@"x"];?//?保持collectionView的item和數據源一致
}?completion:nil];
}
因為是練習Demo,所以暫時把刪除的觸發源寫在了長按某Item彈出菜單的copy按鈕里。實際中你可以自定義UICollectionViewCell,添加長按手勢,長按抖動出現叉號,然后刪除等,隨你怎么做。
//?copy?and?paste?的實現
-?(void)collectionView:(UICollectionView?*)collectionView?performAction:(SEL)action?forItemAtIndexPath:(NSIndexPath?*)indexPath?withSender:(nullable?id)sender
{
if([NSStringFromSelector(action)?isEqualToString:@"copy:"])
{
//? ? ? ? NSLog(@"-------------執行拷貝-------------");
[_collectionView?performBatchUpdates:^{
[_section0Array?removeObjectAtIndex:indexPath.row];
[_collectionView?deleteItemsAtIndexPaths:@[indexPath]];
}?completion:nil];
}
else?if([NSStringFromSelector(action)?isEqualToString:@"paste:"])
{
NSLog(@"-------------執行粘貼-------------");
}
}
自定義轉場動畫