Custom Collection View layout

UICollectionView first introduced in the iOS6 also UIKit star view class . It UITableView and share a set of API design, but also on UITableView made some extensions. UICollectionView most powerful, while significantly exceed UITableView features is its fully flexible layout structure. In this article, we will achieve a fairly complex custom collection view layout, and the way to discuss important part of this class design. The sample code in the project GitHub on.

Xiao Bian here recommend a group: 691 040 931 There are plenty of books and interview data, many iOS developers are on the inside AC technology

Layout object (Layout Objects)

UITableView and UICollectionView are data-source drive and delegate to. They play in view of its children set during container only role ( dumb containers), and set the child view real content ignorant.

UICollectionViewOn top of this were further abstracted. The position of its child views it, the size and appearance of the control entrusted to a single layout object. By providing a custom layout object, you can achieve almost any layout you can imagine. Layout inherited from the UICollectionViewLayoutabstract base class. iOS6 to UICollectionViewFlowLayoutpropose a concrete implementation form layout class.

We can use flow layout to achieve a standard grid view, which may be in the collection view the most common use cases of. Although most people think so, but Apple is very smart, does not explicitly name the class UICollectionViewGridLayout, and use a more generic term flow layout, better describes the class of functions: it through a placement by one cell to create your own layout, when necessary, insert a horizontal or vertical column break. By scrolling direction between custom, cell size and spacing, flow layout cell layout may be in single row or column. In fact, UITableViewthe layout can be imagined as a special case of flow layout.

You prepare yourself to write in a UICollectionViewLayoutprevious sub-category, you need to ask yourself if you can use UICollectionViewFlowLayoutto achieve the layout of your heart. This class is very easy to customize , and can be further customized to inherit itself. Look interested in this article .

Cells and Other Views

In order to adapt to any layout, collection view the establishment of a similar, but more flexible than table view of the view hierarchy (view hierarchy). As usual, your main content is displayed in the cell, any cell can be grouped into the section. Collection view of the cell must be a UICollectionViewCellsubclass. In addition to cell, collection view additional management are two views: supplementary views and decoration views.

collection view of Supplementary views corresponding to the section header table view and footer views. Like cells, they are content driven by data source object. However, the use and the table view is not the same, supplementary view does not necessarily as a header or footer view; their number and placement is fully controlled by the position of the layout.

Decoration views purely as a decoration. They entirely layout object, the object layout and management, they are not obtained from the data source contents. When the layout object specifies the need for a decoration view of time, collection view is automatically created, layout parameters and layout objects provide the application to go above. You do not need to prepare a custom view any content.

Supplementary views and decoration views must be UICollectionReusableView subclass. Each view class layout used need to be registered in the collection view, so that when the data source so that when they are out of the line from the reuse pool, they will be able to create a new instance. If you are using Interface Builder, you can drag and drop visual editor in a cell to complete cell registered in the collection view in the collection view. The same method can also be used in the supplementary view, provided that you use UICollectionViewFlowLayout. If not, you can only call registerClass:or registerNib:registration view class manual method. You need to viewDidLoaddo these operations.

Custom layout

As an example of the layout of a defined collection view very meaningful since, we might imagine a typical calendar application week (week) view. Calendar display once a week every day of the week is displayed in the column. Each calendar event will display the location and size of the event on behalf of the start date and duration to a cell in our collection view.

There are generally two types of collection view layout:

1. independently of the content of the layout calculation . This is what you know and like UITableView UICollectionViewFlowLayout these cases. Each cell position not on the content and appearance of the display, but the display order based on the order of all the cell contents. You can set the default flow layout as an example. Each cell is based on a cell are placed before (or if there is not enough space, from the beginning of the next line). Layout objects do not have access to the actual data to calculate the layout.

2. calculated based on the layout of the content . Our calendar view is the example of such type. In order to calculate the start and end time of the event display of layout objects collection view requires direct access to data sources. In many cases, not only need to remove the data layout objects currently visible cell, but also need to remove some of the decisions which the current cell is visible from all data records.

In our calendar example, if the layout objects accessible cells within a rectangle of a property, it must iterate over all event data sources to determine which is located in the required time window. And some relatively simple, independent data source compared to the calculated flow layout, which is enough to calculate the cell index paths within a rectangle of (all cells are the same size of the grid is assumed).

If there is a content dependent layout that suggests that you need to write custom layout classes, and at the same time you can not use a custom UICollectionViewFlowLayout, so this is what we need to do.

UICollectionViewLayout document lists the subclass method needs to be rewritten.

collectionViewContentSize

Since the collection view its content did not know, so the layout of the first to provide information is the scroll area the size of this collection view to properly manage scrolling. Layout object must calculate the total size of its content at this time, including supplementary views and decoration views. Note that although most classic collection view limited to one axis direction scrolling (as UICollectionViewFlowLayoutthe same), but this is not necessary.

In our calendar example, we want to scroll the view vertical. For example, if we wanted one hour to take up 100 points on the vertical space, which displays the contents of a full day of height is 2400 points. Note that we can not scroll horizontally, which means that our collection view can only display a week. In order to be able to page during a calendar of more stars, we can scroll view of an independent (pagination) (you can use UIPageViewController ) using multiple collection view (one per week), or stick with a collection view and returns large enough content width, which would make the user feel free to slide in both directions.

- (CGSize)collectionViewContentSize
{
    // Don't scroll horizontally
    CGFloat contentWidth = self.collectionView.bounds.size.width;

    // Scroll vertically to display a full day
    CGFloat contentHeight = DayHeaderHeight + (HeightPerHour * HoursPerDay);

    CGSize contentSize = CGSizeMake(contentWidth, contentHeight);
    return contentSize;
}

For clarity, I select a layout on a very simple model: Assuming the same number of days per week, every day the same length, that is to say the number of days indicated by 0-6. In a real calendar program, the layout will be based on large-scale use for their calculation NSCalendaardate.

layoutAttributesForElementsInRect:

This is the most important class layout any way, and while perhaps most easily confusing way. collection view call this method and pass a rectangular coordinate system of its own past. This rectangle represents the visible rectangular region of this view (that is, its bounds), you need to be ready to handle any rectangle it to you.

Your implementation must contain a return UICollectionViewLayoutAttributesarray object, comprising an object for each such cell, supplementary view or decoration view in the rectangular area is visible. UICollectionViewLayoutAttributesClass contains the item in the collection view all layout properties. By default, this category includes frame, center, size, transform3D, alpha, zIndexand hiddenattributes. If you want to control the layout of the property other views (such as background color), you can build a UICollectionViewLayoutAttributessub-class, and then add your own attributes.

Layout properties of the object (layout attributes objects) by indexPathattributes and their corresponding cell supplementary view associated with or decoration view. after collection view all items request to the layout from the layout object property, it will instantiate all views, and the attribute corresponding to the application up to each view.

note! This method involves all types of views, that is, cell, supplementary views and decoration views. A naive implementation may choose to ignore incoming rectangular layout and return all property to view the collection view. In the layout prototyping and development stage, which is an effective method. However, this will have a very bad impact on performance, particularly visible cell far less than the number of all cell time, collection view and layout objects will do unnecessary extra work for those views are not visible.

You need to do to achieve this steps:

  1. Create an empty variable array to store all the layout attributes.

  2. Determining which cells of the frame entirely or partially located rectangle index paths. This calculation requires you take you need to display data from the data source collection view. Then call you realize in the cycle layoutAttributesForItemAtIndexPath:method to create and configure a suitable layout attribute object for each index path, and each object is added to the array.

  3. If your layout contains supplementary views, visible index paths supplementary view of computing within the rectangle. Call you realize in the cycle layoutAttributesForSupplementaryViewOfKind:atIndexPath:, and these objects are added to the array. By passing a different kind of character parameters of your choice, you can distinguish different types of supplementary views (such as headers and footers). When you need to create a view, collection view will be kind to your character back to the data source. Remember supplementary and decoration views of the number and types of full control by the layout. You will not be restricted headers and footers are.

  4. If the layout contains decoration views, seen in the index paths decoration views of rectangle. Call you realize in the cycle layoutAttributesForDecorationViewOfKind:atIndexPath:, and these objects are added to the array.

  5. Returns an array.

We did not use a custom layout decoration views, but uses two supplementary views (column headers and row headers):

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSMutableArray *layoutAttributes = [NSMutableArray array];
    // Cells
    // We call a custom helper method -indexPathsOfItemsInRect: here
    // which computes the index paths of the cells that should be included
    // in rect.
    NSArray *visibleIndexPaths = [self indexPathsOfItemsInRect:rect];
    for (NSIndexPath *indexPath in visibleIndexPaths) {
        UICollectionViewLayoutAttributes *attributes =
        [self layoutAttributesForItemAtIndexPath:indexPath];
        [layoutAttributes addObject:attributes];
    }

    // Supplementary views
    NSArray *dayHeaderViewIndexPaths = [self indexPathsOfDayHeaderViewsInRect:rect];
    for (NSIndexPath *indexPath in dayHeaderViewIndexPaths) {
        UICollectionViewLayoutAttributes *attributes =
        [self layoutAttributesForSupplementaryViewOfKind:@"DayHeaderView"
                                             atIndexPath:indexPath];
        [layoutAttributes addObject:attributes];
    }

    NSArray *hourHeaderViewIndexPaths = [self indexPathsOfHourHeaderViewsInRect:rect];
    for (NSIndexPath *indexPath in hourHeaderViewIndexPaths) {
        UICollectionViewLayoutAttributes *attributes =
        [self layoutAttributesForSupplementaryViewOfKind:@"HourHeaderView"
                                             atIndexPath:indexPath];
        [layoutAttributes addObject:attributes];
    }
    return layoutAttributes;
}

layoutAttributesFor…IndexPath

Sometimes, collection view layout properties requests to the layout object for a particular cell, supplementary or decoration view, but not all visible objects. That's when the other three methods come into play, you realize the layoutAttributesForItemAtIndexPath:need to create and return a separate layout properties of the object, so as to pass the correct formatting your index path corresponding to the cell.

You can call +[UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:], and then modify the properties of index path according to this method. In order to get the data in this index path of the need to show, you may need to access collection view of the data source. So far, at least be sure to set the frame property, unless all of you cell are located above each other.

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    CalendarDataSource *dataSource = self.collectionView.dataSource;
    id event = [dataSource eventAtIndexPath:indexPath];
    UICollectionViewLayoutAttributes *attributes =
    [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    attributes.frame = [self frameForEvent:event];
    return attributes;
}

If you are using automatic layout, you might be surprised that we are directly modify the frame layout attribute parameter, rather than constraints and work, but that is UICollectionViewLayout work. Although you may use automatic layout to define the layout of its collection view of the frame and within each cell, but the cells still need the frames calculated by the old-fashioned way.

Similarly, layoutAttributesForSupplementaryViewOfKind:atIndexPath:and layoutAttributesForDecorationViewOfKind:atIndexPath:methods are needed to do the same thing for the supplementary and decoration views. Only your layout contains such a view you only need to implement these two methods. UICollectionViewLayoutAttributesAlso it contains two factory methods, +layoutAttributesForSupplementaryViewOfKind:withIndexPath:and +layoutAttributesForDecorationViewOfKind:withIndexPath:to create the correct layout attributes of the object with them.

shouldInvalidateLayoutForBoundsChange:

Finally, when the collection view the bounds change, the layout needs to tell whether the collection view layout needs to be recalculated. My guess is: When changing the size of the collection view, most of the layout will be void, such as when the device is rotated. Therefore, only a naive implementation may simply return YES. While functionality is important to realize, but the scroll view's bounds will change when scrolling, which means that your layout will be dropped many times per second. Depending on the complexity of the calculations is determined, which will have a huge impact on performance.

When the width of the collection view changes, our custom layouts must be discarded, but this will not affect the layout of the scroll. Fortunately, collection view to its new bounds pass shouldInvalidateLayoutForBoundsChange:method. In this way we will be able to compare the current view of bounds and the new bounds to determine the return value:

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    CGRect oldBounds = self.collectionView.bounds;
    if (CGRectGetWidth(newBounds) != CGRectGetWidth(oldBounds)) {
        return YES;
    }
        return NO;
}

Animation

Insert and delete

UITableView cell comes in a very nice animation insertions and deletions. But when adding and deleting cell animation function is defined as UICollectionView, UIKit engineers encountered such a problem: if the collection view layout is fully variable, so there is no way pre-defined animations and developers to customize the layout well integration. They proposed an elegant approach: When a cell (or supplementary or decoration view) are inserted into the collection view, only their layouts collection view layout attributes in the request cell the normal state, while also requesting the initial layout properties, For example, we need to have inserted at the beginning of the animation cell. collection view will simply create an animation block, and in this block, all the cell attributes change from the initial (initial) status to normal (normal).

By providing different initial layout of the property, you can completely customize the insertion of animation. For example, set the initial alpha animated for a fade-0 will. It also sets a pan and zoom movement will have the effect of scaling.

The same principle applied to remove the animation from a series of normality to the final layout of the properties you set. These are the methods you need to achieve initial or final layout parameters in the layout class.

  • initialLayoutAttributesForAppearingItemAtIndexPath:

  • initialLayoutAttributesForAppearingSupplementaryElementOfKind:atIndexPath:

  • initialLayoutAttributesForAppearingDecorationElementOfKind:atIndexPath:

  • finalLayoutAttributesForDisappearingItemAtIndexPath:

  • finalLayoutAttributesForDisappearingSupplementaryElementOfKind:atIndexPath:

  • finalLayoutAttributesForDisappearingDecorationElementOfKind:atIndexPath:

Switching between layouts

A collection view layout may be dynamically switched to another layout similar manner. When sending a setCollectionViewLayout:animated:message, collection view queries for the cells in the new layout new layout parameters, and the dynamic of each cell (index path determined by the same in the old and new cell layout) transformation parameters from the old to the new layout parameters. You do not need to do anything.

in conclusion

Depending on the complexity of the custom collection view layout, write a usually very easy. Rather, it is essentially complete and write one from scratch to achieve the same layout as a custom view class difficult. Because the calculation involved need to determine what is currently visible child views, and their locations. Nevertheless, use UICollectionViewor bring you some good results, such as cell reuse, automatic support for animations, not to mention the neat layout of independence, sub-view management, as well as providing data architecture requirements (data preparation its architecture prescribes.) .

Custom collection view layout is the lightweight view controller has taken a good step, as you do not include any of the view controller layout code. As explained in the article Chris, like, all this and a separate datasource class together, collection view view controller will be hard to contain any code.

Whenever I use UICollectionViewthe time, I was impressed by its compact design. For an experienced Apple engineer, in order to come up with such a flexible class, you may need to be considered first NSTableViewand UITableView.

Further reading


Xiao Bian here recommend a group: 691 040 931 There are plenty of books and interview data, many iOS developers are on the inside AC technology

16899013-2be0faac01add191.png
Screenshot information .png

原文 Custom Collection View Layouts

Guess you like

Origin blog.csdn.net/weixin_34228662/article/details/90792838