The basic use of UICollectionView

Waterfall stream (WaterFlow) is a common layout of iPad in the process of project development, a waterfall flow (WaterFlow) implementation: before UICollectionView does not appear, realize the waterfall flow mostly by UIScrollView or UITableView. For those of us who are used to the table view, UICollectionView is a little strange. This article mainly introduces how to implement the waterfall stream (WaterFlow) layout with pull-down and refreshing using UICollectionView in pure code.

How to simply use UICollectionView and how to add headerView, so that he also has the effect of headerView similar to tableview

First, UICollectionView integrated drop-down refresh

(1) a brief introduction to UICollectionView

UICollectionView and UITableView are very similar, using it need to set up the corresponding data source DataSource (UICollectionViewDataSoure) protocol and Delegate (UICollectionViewDelegate) event protocol, and realize the corresponding protocol method. It differs from UITableView in that it has UICollectionViewLayout and can customize the layout through its subclasses. The default layout of the system is UICollectionViewDelegateFlowLayout, and the implementation of the corresponding protocol (UICollectionViewDelegateFlowLayout) method allows you to achieve the default layout effect.

The use of UICollectionViewCell is similar to that of UITableViewCell, and the same as ReuseIdentifier can be reused. When using UICollectionViewCell, you first need to register, and then use the column.

Static NSString * const YYPShopId @ = "shop"; static NSString * const collectionHeaderViewID @ = "collectionHeaderView"; @property (nonatomic, weak) UICollectionView *collectView; / / *layout = YYPWaterflowLayout + YYPWaterflowLayout to create the layout of alloc] init]; layout.delegate = self; *collectView = [[UICollectionView UICollectionView / / create a CollectionView alloc] initWithFrame:CGRectMake (0, CGRectGetMaxY (self.headerView.frame), kContentViewWidth self.view.height, CGRectGetMaxY (self.headerView.frame)) collectionViewLayout:layout] collectView.backgroundColor; whiteColor] = [UIColor; collectView.dataSource = self; collectView.delegate = self; [collectView addSubview:self.headerView]; [self.view a DdSubview:collectView]; self.collectView = collectView; / / registered head view: kind Supplementary view types, UICollectionElementKindSectionHeader [collectView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind: UICollectionElementKindSectionHeader head withReuseIdentifier:collectionHeaderViewID] [collectView registerNib:[UINib nibWithNibName:NSStringFromClass; / / registration ([XMGShopCell class]) bundle:nil] forCellWithReuseIdentifier:YYPShopId];

(2) drop ups on Integration

general UITableView add drop-down refresh the way, is the header attribute pull to refresh the view drop-down or placement of UI UITableView (tableHeaderView) or tail (tableFooterView) attribute table. However, there are no two attributes in UICollectionView. So how do we integrate drop UPS? This involves talking about three display content views in the
UICollectionView layout: Cells, Supplementary, views, and Decoration views. We usually place the drop refresh UI on the Supplementary views (official explanation: it can display data, but it is different from Cells. Unlike Cell, Supplementary views is not selected by the user. Instead, you can use it to achieve similar to a specified section or the CollectionView headerView add a header and footer view features like the footerView) above, in fact, Supplementary views and TableView section of the head and tail view view almost. But the usage is quite different. It must take account in the custom waterfall flow layout, otherwise there will be abnormal display.

The registered head and tail view headerView view footerView cell type / head view headerView:kind Supplementary view registered representative types, UICollectionElementKindSectionHeader [self registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind said the group head:UICollectionElementKindSectionHeader withReuseIdentifier:@ "collectionHeaderView"]; / / registered tail view footerView:UICollectionElementKindSectionHeader tail [self registerClass:[UICollectionReusableView class] said the group forSupplementaryViewOfKind: UICollectionElementKindSectionFooter withReuseIdentifier:@ "collectionFooterView"];
To achieve a custom set of views viewForSupplementaryElementOfKind agent method (UICollectionReusableView *) collectionView: (* UICollectionView) collectionView viewForSupplementaryElementOfKind: (NSString * kind) atIndexPath: (NSIndexPath * indexPath) {//kind represents Supplementary view type: head or tail view (header OR footer) if ([kind isEqual:UICollectionElementKindSectionHeader]) {UICollectionReusableView *collectionHeaderView = [collectionView dequeueReusableSupplementaryViewOfKind: UICollectionElementKindSectionHeader withReuseIdentifier:@ "bGCollectionHeaderView" forIndexPath:indexPath]; initialize the drop-down refresh or custom header / view UI interface headerView return collectionHeaderView else if...} [(; Kind isEqual:UICollectionElementKindSectionFooter] *collectionFooterView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter) {UICollectionReusableView withReuseIdentifier:@ "bGCollectionFooterView" forIndexPath:indexPath]; / / initialize pull refresh or custom footer view interface UI footerView... Return collectionFooterView return nil;}};

Complete the above two steps, integrated drop down refresh function finally completed. Next, you can run the project to see if the results are in line with expectations. If you are using the UICollectionViewFlowLayout CollectionView system layout, must return to the head or tail set high, otherwise there will be a group of head or tail (the group was unable to display the drop-down refresh the view where the group head does not return to the height is not affected, because it is the ordinate (y value) is negative). In addition, there is one thing to note is that if you exist in the CollectionView group, it is best to pull the head or refresh group group group (section) and the other end of the group header or footer separately, the corresponding need to register a group head or tail group points of view judge.

Method 2: use MJRefresh.h to quickly integrate the drop down refresh mode, which is convenient and quick

- (void) setupRefresh [MJRefreshNormalHeader headerWithRefreshingTarget:self {self.collectView.mj_header = refreshingAction:@selector (loadNewShops)]; self.collectView.mj_header.automaticallyChangeAlpha = YES; [self.collectView.mj_header = beginRefreshing]; self.collectView.mj_footer [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector (loadMoreShops)]; self.collectView.mj_footer.hidden = YES;} - {dispatch_after (void) loadNewShops (dispatch_time (DISPATCH_TIME_NOW (int64_t) (1 * NSEC_PER_SEC)), dispatch_get_main_queue (). ^{/ / the 1. read data 2. to remove all 3. data add data to the array of *shops / / NSArray = [YYPShop objectArrayWithFilename:@ "1.plist"]; / / [self.shops RemoveAllObjects]; / / [self.shops / / [self.collectView reloadData] addObjectsFromArray:shops]; refresh data; / / the end of the top refresh control [self.collectView.mj_header endRefreshing];});} - {dispatch_after (void) loadMoreShops (dispatch_time (DISPATCH_TIME_NOW (int64_t) (1 * NSEC_PER_SEC)), dispatch_get_main_queue (1.), ^{/ / read data 2. add data to the array / / NSArray = *shops [YYPShop objectArrayWithFilename:@ "1.plist"]; / / [self.shops addObjectsFromArray: shops]; / / [self.collectView / / reloadData] to refresh the data; the end of the tail refresh control [self.collectView.mj_footer endRefreshing];}});

Two, custom Waterfall (WaterFlow) layout

(1) a brief introduction to UICollectionView layout

Generally speaking, the layout of UICollectionView is divided into two kinds:

The layout is independent of the content, and the layout does not need to be calculated based on the contents of the displayed cells. The display order of the Cell is consistent with the content order. The rows of Cell are shifted to the next row and continue to be arranged. That’s the system UICollectionViewFlowLayout.

Layout requires calculation, and when the UICollectionViewFlowLayout system layout is a newline, each item is highly different, resulting in a layout that is out of reach for your needs. So, at this point, you need to calculate the height of each item and eventually determine the location of the item display.

From the above we can see that if the general layout needs to calculate the content of the time, we should not directly use UICollectionViewFlowLayout layout, but should be a subclass of UICollectionViewLayout, so as to achieve the purpose of private custom.


Created by QTX on / / 16/8/8. / / Copyright / / reserved. rights All 2016 zhangli. #import < UIKit/UIKit.h> waterfall flow layout;; @class YYPWaterflowLayout; @protocol YYPWaterflowLayoutDelegate < NSObject> @required - (CGFloat) waterflowLayout: (XMGWaterflowLayout *) waterflowLayout heightForItemAtIndex: (NSUInteger) index itemWidth: (CGFloat) itemWidth; @optional (CGFloat) columnCountInWaterflowLayout: (YYPWaterflowLayout *) waterflowLayout; - (CGFloat) columnMarginInWaterflowLayout: (YYPWaterflowLayout * waterflowLayout); - (CGFloat) rowMarginInWaterflowLayout: (YYPWaterflowLayout * waterflowLayout); - (UIEdgeInsets) edgeInsetsInWaterflowLayout: (YYPWaterflowLayout * waterflowLayout); @end @interface YYPWaterflowLayout: UICollectionView FlowLayout / * * * / @property agent (nonatomic, weak) id< YYPWaterflowLayoutDelegate> delegate; @end


Created by QTX on / / 16/8/8. / / Copyright zhangli. All rights reserved. 2016 "YYPWaterflowLayout.h" / / #import / * * default number of columns * / static const NSInteger YYPDefaultColumnCount = 2; / * * * / each column spacing between static const CGFloat YYPDefaultColumnMargin = 10; / * * * / each row spacing between the static const CGFloat YYPDefaultRowMargin = 10; / * * edge const UIEdgeInsets / static spacing YYPDefaultEdgeInsets = {10, 10, 10, 10}; @interface (YYPWaterflowLayout) / * * store all cell layout attributes (nonatomic, strong) / @property NSMutableArray *attrsArray; / * * * / @property hold the height of all columns (nonatomic, strong) NSMutableArray *columnHeights; / * * * / @property content height (nonatomic, assign) CGFloat contentHeight; - (CGFloat) - (CGFloat) rowMargin; columnMargin; columnCount; - (NSInteger) - (UIEdgeInsets) edgeInsets; @end @implementation YYPWaterflowLayout common data processing #pragma - mark - rowMargin (CGFloat) {if ([self.delegate respondsToSelector:@selector (rowMarginInWaterflowLayout:)] return) {[self.delegate rowMarginInWaterflowLayout:self];} else {return}} (CGFloat - YYPDefaultRowMargin; columnMargin if ([self.delegate) {respondsToSelector:@selector (columnMarginInWaterflowLayout:)] return) {[self.delegate columnMarginInWaterflowLayout:self];} else {return YYPDefaultColumnMargin;}} - {(NSInteger) columnCount if ([self.delegate respondsToSelector:@selector (columnCountInWater FlowLayout:)] [self.delegate columnCountInWaterflowLayout:self]) {return} else {return; YYPDefaultColumnCount;}} - {(UIEdgeInsets) edgeInsets if ([self.delegate respondsToSelector:@selector (edgeInsetsInWaterflowLayout:)] return) {[self.delegate edgeInsetsInWaterflowLayout:self];} else {return YYPDefaultEdgeInsets}}; #pragma mark - - lazy loading (NSMutableArray *) columnHeights {if {_columnHeights (_columnHeights!) [NSMutableArray = array];} return _columnHeights;} - (NSMutableArray * attrsArray) {if (_attrsArray! = [NSMutableArray) {_attrsArray array]}; return _attrsArray;} / * * * * / - initialization (void) {[super P prepareLayout RepareLayout]; self.contentHeight = 0; / / remove all previously calculated height [self.columnHeights removeAllObjects]; for (NSInteger I = 0; I < self.columnCount; i++) {[self.columnHeights addObject:@ (];} / / clear before all the layout attributes [self.attrsArray removeAllObjects]; / / to create each cell corresponding to count = [self.collectionView NSInteger layout attributes numberOfItemsInSection:0]; for (NSInteger I = 0; I < count; i++) {/ / *indexPath = [NSIndexPath indexPathForItem:i NSIndexPath create a position inSection:0]; / / get the corresponding position of indexPath cell UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItem layout attributes AtIndexPath:indexPath]; [self.attrsArray addObject:attrs];}} / * * * * - arrangement of decision cell (NSArray *) layoutAttributesForElementsInRect: (CGRect) rect {return self.attrsArray;} / * * * returns the position of the indexPath cell corresponds to the layout attributes - * / (UICollectionViewLayoutAttributes *) layoutAttributesForItemAtIndexPath: (NSIndexPath * indexPath) {/ / *attrs = [UICollectionViewLayoutAttributes to create the layout attributes of UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; / / collectionView the width of CGFloat collectionViewW = self.collectionView.frame.size.width; / / set the layout attributes of frame CGFloat (w = collectionViewW - self.edgeInsets.left - self.edgeInsets.right - self.columnCount - ( 1) * self.columnMargin / self.columnCount); CGFloat H = [self.delegate waterflowLayout:self heightForItemAtIndex:indexPath.item itemWidth:w]; / / find the height of a column of NSInteger destColumn = the shortest 0; minColumnHeight = CGFloat [self.columnHeights [0] doubleValue] for (NSInteger I = 1; I; < self.columnCount; i++) {/ / get the I column height CGFloat columnHeight = [self.columnHeights[i] doubleValue] if (minColumnHeight; > columnHeight) {minColumnHeight = columnHeight; destColumn = I;}} CGFloat x = self.edgeInsets.left + destColumn * (W + self.columnMargin); CGFloat y = minColumnHeight; if (Y! = {y = self.row Margin;} attrs.frame = CGRectMake (x, y, W, H); / / update the shortest column height self.columnHeights[destColumn] = @ (CGRectGetMaxY (attrs.frame)); / / CGFloat columnHeight records height = [self.columnHeights[destColumn] doubleValue]; if (self.contentHeight < columnHeight) {self.contentHeight = columnHeight;}} - (return attrs; CGSize collectionViewContentSize = maxColumnHeight CGFloat) {/ / [self.columnHeights[0] / / for (NSInteger doubleValue]; I = 1; I < self.columnCount; i++) {/ / / / get the I column height columnHeight = [self.columnHeights[i] / / CGFloat / / doubleValue] / / if (maxColumnHeight; < columnHeight) {/ / maxColumnHeight = columnHei Ght;}} / / return / / CGSizeMake (0, self.contentHeight + self.edgeInsets.bottom);} @end

(2) create custom layouts

As mentioned in the official document, CollectionView calls the concrete method of layout objects in the layout process. These methods give you the opportunity to calculate the location of these items and to provide the CollectionView with the main information it needs. Other methods may also be invoked, but in the layout process, these methods are always called in the following order:

1. use the “prepareLayout” method to perform pre calculated operations on the layout information needed by the CollectionView.
2. uses the “collectionViewContentSize” method to return the overall size of the entire content area based on your initial calculation.
3. uses the “layoutAttributesForElementsInRect:” method to return the cells and view properties of the specified area.

Rewrite the "prepareLayout" method to calculate layout information. - (void) prepareLayout{[super prepareLayout]; / / calculated for each display width, horizontal spacing between horizontalItemSpacing representative items, columnNum represents the total number of columns, contentInset represents the next filled. See the following figure shows the self.itemWidth = (self.collectionView.frame.size.width - (self.horizontalItemSpacing * (self.columnNum - 1)) - self.contentInset.left - self.contentInset.right / self.columnNum); / / if there is demand pull to refresh the need to provide the first view (Supplementary view) layout attributes. If (self.headerHeight > 0) {/ / through the layoutAttributesForSupplementaryViewOfKind:withIndexPath: method to create Supplementary view layout attribute objects, kind is used to distinguish the Supplementary view type (header or footer). Self.headerLayoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader withIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; / / modify the layout parameters of frame attribute self.headerLayoutAttributes.frame = CGRectMake (0, 0, self.collectionView.frame.size.width, self.headerHeight);} / / initialize save layout attribute dictionary NSMutableDictionary *cellLayoutInfoDic = [NSMutableDictionary dictionary]; / / NSMutableArray *columnInfoArray high group initialization list = [self columnInfoArray]; NSInteger numSections = [self.collectionView numberOfSections]; for (NSInteger section = 0 section; < numSections; section++) {NSInteger numItem S [self.collectionView for (NSInteger = numberOfItemsInSection:section]; item = 0; item < numItems; item++) {/ / get column high minimum model, with its high as y *firstModel coordinates ZLWaterFlowModel = columnInfoArray.firstObject; CGFloat y = firstModel.height; CGFloat = self.contentInset.left + X (self.horizontalItemSpacing + self.itemWidth) * firstModel.column; NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:section] through a proxy method; / / item high degree of incoming correspondence. CGFloat itemHeight = [((id< BGWaterFlowLayoutDelegate> self.collectionView.delegate)) collectionView:self.collectionView layout:self heightForItemAtIndexPath:indexPath]; / / through the layoutAttributesForCellWithIndexPath: method to create cell layout attribute objects. UICollectionViewLayoutAttributes *itemAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; / / item calculation itemAttributes.frame layout attributes = CGRectMake (x,, self.itemWidth, itemHeight); / / calculation of the current column, vertical spacing between verticalItemSpacing representative items. FirstModel.height = (itemHeight + self.verticalItemSpacing); / / save the new column, and sort: after the move from search, find the object height smaller than it is inserted into the object. [self sortArrayByHeight:columnInfoArray]; / / save computing good item layout attributes cellLayoutInfoDic[indexPath] = itemAttributes;}} / / save the local layout attribute dictionary into the global dictionary in self.cellLayoutInfoDic = [cellLayoutInfoDic copy]; / / according to the sequencing logic in front of the column, the last element in the array is high, the maximum height of a column. ZLWaterFlowModel *lastModel = columnInfoArray.lastObject; / / if pull refresh needs the need to provide rear view layout attributes. If (self.footerHeight > 0) [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter withIndexPath:[NSIndexPath indexPathForItem:0 {self.footerLayoutAttributes = inSection:0]]; / / the layout of the view properties calculation of wake self.footerLayoutAttributes.frame = CGRectMake (0,, self.collectionView.frame.size.width, self.footerHeight);} / / collectionView contentSize calculated self.contentSize = CGSizeMake (self.collectionView.frame.size.width,;}
The basic use of UICollectionView

Rewrite the “collectionViewContentSize” method to return the content height of collectionView.

- (CGSize) collectionViewContentSize{collectionView return self.contentSize / / return height calculated;}

The rewrite layoutAttributesForElementsInRect method returns the cell or other type of view layout attribute in the specified rectangle region.

- (NSArray) layoutAttributesForElementsInRect: (CGRect rect) {/ / returns a set of calculation has good layout properties. NSMutableArray *attributesArrs = [NSMutableArray array]; [self.cellLayoutInfoDic enumerateKeysAndObjectsUsingBlock:^ (NSIndexPath *indexPath, UICollectionViewLayoutAttributes *attributes, BOOL *stop) {/ / traverse layout attributes save dictionary, add layout attributes to an array of if (CGRectIntersectsRect (rect, attributes.frame)) {[attributesArrs addObject:attributes];} / /}]; if the head or tail view view add view head and tail the layout of the view properties of if (self.headerLayoutAttributes & amp; & CGRectIntersectsR ECT (rect, self.headerLayoutAttributes.frame)) {[attributesArrs addObject:self.headerLayoutAttributes]} (self.footerLayoutAttributes & if; & CGRectIntersectsRect (rect, self.footerLayoutAttributes.frame)) {[attributesArrs addObject:self.footerLayoutAttributes]}; return attributesArrs;}
The executive agent method like tableview #pragma mark - #pragma mark collectionViewDelegate - (NSInteger) numberOfSectionsInCollectionView: (UICollectionView * collectionView) {//section} - number (NSInteger) collectionView: (UICollectionView *) collectionView numberOfItemsInSection: (NSInteger section) {/ / / / the number of data set} element size - (CGSize) collectionView: (UICollectionView * layout: (collectionView) UICollectionViewLayout collectionViewLayout sizeForItemAtIndexPath: (NSIndexPath) * * Return (indexPath{) returns a Item wide high)} - (UICollectionViewCell *) collectionView: (* UICollectionView) collectionView cellForItemAtIndexPath: (NSIndexPath * indexPath) {/ / this is good custom cell} - (void) collectionView: (UICol LectionView collectionView didSelectItemAtIndexPath: (NSIndexPath) * *)} indexPath {//UICollectionView calling method is selected - (UICollectionReusableView *) collectionView: (* UICollectionView) viewForSupplementaryElementOfKind: collectionView (NSString * kind) atIndexPath: (NSIndexPath * indexPath) {ZLHeaderView *headReusableView; / / here is headerView (kind if = = UICollectionElementKindSectionHeader) {headReusableView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:HeaderIdentifier forIndexPath:indexPath]; headReusableView.frame = CGRectZero; headReusableView.delegate = self; headReusableView.modeNumber = 1; [((ZLHeaderView) headReusableView) setData:self.h EaderDict] return headReusableView;};} / / execute headerView agent returns the height of the headerView - (CGSize) collectionView: (UICollectionView *) collectionView layout: (UICollectionViewLayout*) collectionViewLayout referenceSizeForHeaderInSection: (NSInteger section) {CGFloat height = [self.topView getHeight]; return CGSizeMake (320, height);}

Two, test pit:

2.1 initialize the layout when initializing the controller

When the following error, Terminating app due to uncaught * exception’NSInvalidArgumentException’, reason:’UICollectionView must be initialized with a non-nil layout parameter’
to see if the controller is initialized from the initial layout for bug reasons, even don’t forget to take the frame layout initialization initialization add sub controller

The basic use of UICollectionView

2.2, when the waterfall stream needs headerView and footerView when the pit

Using UIView comes from defining headerView and footerView, and don’t forget to add headerView and to CollectionView and footerView at the top and bottom of the waterfall stream, otherwise it will be obscured.

- (UIView * headerView) {if (_headerView! = [[UIView) {_headerView alloc] init]; _headerView.height = headerViewH; _headerView.backgroundColor = YYPBackgroundColor; [self setupHeaderView]; / / here. According to the demand of custom settings headerView layout return _headerView}};
[collectView addSubview:self.headerView];
- (UIEdgeInsets) edgeInsetsInWaterflowLayout: (YYPWaterflowLayout * waterflowLayout) {return UIEdgeInsetsMake (kNavBarHeight + headerViewH + 30, 20, 30, 20); / / this is nav64, the height is headerView} / / headerViewH returns the size of the HeaderView - (CGSize) collectionView: (UICollectionView * collectionView) layout: (UICollectionViewLayout *) collectionViewLayout referenceSizeForHeaderInSection: (NSInteger) section {return CGSizeMake (kContentViewWidth, headerViewH);}

Running the waterfall stream effect screenshot is shown below:

The basic use of UICollectionView

The later will be constantly updated, optimized, and further improved. Order it if you like…