IOS 集合视图指南6:创建自定义布局

Creating Custom Layouts(创建自定义布局)

Before you start building custom layouts, consider whether doing so is really necessary. The UICollectionViewFlowLayout class provides a significant amount of behavior that has already been optimized for efficiency and that can be adapted in several ways to achieve many different types of standard layouts. The only times to consider implementing a custom layout are in the following situations:

  • The layout you want looks nothing like a grid or a line-based breaking layout (a layout in which items are placed into a row until it’s full, then continue on to the next line until all items are placed) or necessitates scrolling in more than one direction.

  • You want to change all of the cell positions frequently enough that it would be more work to modify the existing flow layout than to create a custom layout.

The good news is that, from an API perspective, implementing a custom layout is not difficult. The hardest part is performing the calculations needed to determine the positions of items in the layout. When you know the locations of those items, providing that information to the collection view is straightforward.


在你创建自定义布局之前,想清楚是不是真的必要。UICollectionViewFlowLayout类提供了很过已经功能优化好的,有多重方法可是实现不同类型的标准布局。仅有的需要完成自动布局的情况大约如下:

  • 你想要的布局看起来不像网状,或者超线性布局(这种布局是排满一行,然后换行接着排)或者需要多方向滑动的。
  • 你想要频繁更改单元格的位置以至于使用已经存在的流动布局比自定义布局还要复杂
好消息是,(从API的角度看)实现一个自定义布局并不难。最难的部分是执行计算并决定元素在布局中的位置。当你知道这些元素的位置的时候,你可以很简单的给集合视图提供数据。

Subclassing UICollectionViewLayout(子类化集合视图布局类)

For custom layouts, you want to subclass UICollectionViewLayout, which provides you with a fresh starting point for your design. Only a handful of methods provide the core behavior for your layout object and are required in your implementation. The rest of the methods are there for you to override as needed to tweak the layout behavior. The core methods handle the following crucial tasks:

  • Specify the size of the scrollable content area.

  • Provide attribute objects for the cells and views that make up your layout so that the collection view can position each cell and view.

Although you can create a functional layout object that implements just the core methods, your layout is likely to be more engaging if you implement several of the optional methods as well.

The layout object uses information provided by its data source to create the collection view’s layout. Your layout communicates with the data source by calling methods on thecollectionView property, which is accessible in all of the layout’s methods. Keep in mind what your collection view knows and doesn’t know during the layout process. Because the layout process is under way, the collection view cannot track the layout or positioning of views. So even though the layout object will not restrict you from calling any of the collection view’s methods, refrain from relying on the collection view for anything other than the data necessary to compute your layout.

自定义布局中,你可以子类化UICollectionViewLayout类,这个类给你的设计提供了一个新的开始。仅仅有几个提供了你布局中对象的核心行为的方法要求你必须实现。剩下的方法你可以根据需要来重写以修改布局行为。核心的方法处理以下关键任务:
  • 指定滑动区域的大小
  • 提供组成内容视图的单元格和视图的属性对象,让你的集合视图可以布局每个单元格和视图。
尽管你的布局对象可以只实现一些核心的功能,但是如果你同时实现了一些可选的方法会让你的布局更加迷人。
布局对象使用数据源提供的信息创建集合视图的布局。你的布局对象通过调用集合视图属性的方法来联系数据源,在所有的布局对象中都是可行的。记住你集合视图在布局过程中知道的和不知道的。因为布局过程正在进行的时候,集合视图不能跟踪视图位置或布局。所以通过布局对象对象不能限制你调用任何集合视图的方法,不依赖集合视图必须数据之外任何东西来计算你的布局

Understanding the Core Layout Process(理解核心布局过程)

The collection view works directly with your custom layout object to manage the overall layout process. When the collection view determines that it needs layout information, it asks your layout object to provide it. For example, the collection view asks for layout information when it is first displayed or is resized. You can also tell the collection view to update its layout explicitly by calling the invalidateLayout method of the layout object. That method throws away the existing layout information and forces the layout object to generate new layout information.

集合视图直接和你的自定义布局对象一起管理整个布局过程。当集合视图决定需要的布局信息的时候,他会要求你的布局对象提供信息。比如,当第一次展示或者改变尺寸的时候集合视图要求布局信息。你也可以告诉集合视图通过调用这个布局对象的 invalidateLayout  方法来更新布局。这个方法扔掉现有的布局信息,强制产生新的布局信息。

Note: Be careful not to confuse the layout object’s invalidateLayout method with the collection view’s reloadData method. Calling the invalidateLayout method does not necessarily cause the collection view to throw out its existing cells and subviews. Rather, it forces the layout object to recompute all of its layout attributes as is necessary when moving and adding or deleting items. If data within the data source has changed, the reloadData method is appropriate. Regardless of how you initiate a layout update, the actual layout process is the same.

提示:注意不要把布局对象的invalidateLayout 方法和集合视图的 reloadData 方法。调用invalidateLayout方法并不一定会导致集合视图抛弃现有的单元格和子视图。相反,当移动或删除单元格的时候,它强制布局对象重新计算所有布局属性因为这是必要的。如果数据源改变了,使用reloadData 是合适的,不论你怎样初始化布局,实际的布局过程都是相同的。

During the layout process, the collection view calls specific methods of your layout object. These methods are your chance to calculate the position of items and to provide the collection view with the primary information it needs. Other methods may be called, too, but these methods are always called during the layout process in the following order:

  1. Use the prepareLayout method to perform the up-front calculations needed to provide layout information.

  2. Use the collectionViewContentSize method to return the overall size of the entire content area based on your initial calculations.

  3. Use the layoutAttributesForElementsInRect: method to return the attributes for cells and views that are in the specified rectangle.

Figure 5-1 illustrates how you can use the preceding methods to generate your layout information.

在布局的过程中,集合视图调用你布局对象的特定方法。这些方法是你重新计算元素位置同时提供给集合视图需要信息的机会。其他方法也会调用,但是下列方法在布局过程中会经常调用:

  1. 使用perpareLayout 方法来实现预先计算布局需要的信息
  2. 使用collectionViewContentSize 方法返回所有基于你初始化计算的所有内容的大小。
  3. 使用layoutAttributesForElementsInRect 方法来返回指定矩形单元格和视图的属性。
图5-1 说明了你怎样使用先前的方法产生你的布局信息。

Figure 5-1  Laying out your custom content

The prepareLayout method is your opportunity to perform whatever calculations are needed to determine the position of the cells and views in the layout. At a minimum, you should compute enough information in this method to be able to return the overall size of the content area, which you return to the collection view in step 2.

The collection view uses the content size to configure its scroll view appropriately. For instance, if your computed content size expands past the bounds of the current device’s screen both vertically and horizontally, the scroll view adjusts to allow scrolling in both directions simultaneously. Unlike the UICollectionViewFlowLayout, it does not by default adjust the layout of content to scroll in only one direction.

Based on the current scroll position, the collection view then calls your layoutAttributesForElementsInRect: method to ask for the attributes of the cells and views in a specific rectangle, which may or may not be the same as the visible rectangle. After returning that information, the core layout process is effectively complete.

After layout finishes, the attributes of your cells and views remain the same until you or the collection view invalidates the layout. Calling the invalidateLayout method of your layout object causes the layout process to begin again, starting with a new call to the prepareLayout method. The collection view can also invalidate your layout automatically during scrolling. If the user scrolls its content, the collection view calls the layout object’s shouldInvalidateLayoutForBoundsChange: method and invalidates the layout if that method returns YES.

prepareLayout方法是执行任何计算布局中决定单元格和视图的机会。至少,你可以使用这个方法计算充分的信息来返回视图内容中的尺寸,就像你在步骤二中返回的集合视图。 集合视图使用内容视图大小来设置合适你的滚动视图。比如,如果你计算内容视图的大小在水平和垂直方向上超出现在设备,滑动视图就在垂直和水平方向上一致的允许滚动。和UICollectionViewFlowLayout不同,它不是默认布局内容视图只用一个方向。 基于现有的滚动位置,集合视图会调用单元格和视图的layoutAttributesForElementsInRect:方法,这个方法要求在指定矩形中的布局元素,这些元素可能和现在可视矩形的布局元素相同或不同。在返回这些信息之后,核心的布局过程就可以有效的完成了。 在布局完成之后,你的单元格和视图的属性会保持一致直到你的集合视图无效化布局。调用你的布局对象的invalidateLayout方法返回yes,会导致布局过程中开始对象的shouldInvlidateLayoutForBoundsChange方法同时无效化布局视图。

Note: It is useful to remember that calling the invalidateLayout method does not begin the layout update process immediately. The method merely marks the layout as being inconsistent with the data and in need of being updated. During the next view update cycle, the collection view checks to see whether its layout is dirty and updates it if it is. In fact, you can call the invalidateLayout method multiple times in quick succession without triggering an immediate layout update each time.

注意: 你要记住invalidateLayout方法不会在布局更新过程中立即调用。这个方法仅仅是标记了布局对象需要更新的不一致数据。在下一个视图更新周期,集合视图查看布局是否陈旧是否需要更新,事实上,你可以在一个连续的过程中多次调用invalidateLayout 方法很多次,并不是每次调用都立即更新布局信息。

Creating Layout Attributes(创建布局属性)

The attributes objects that your layout is responsible for are instances of the UICollectionViewLayoutAttributes class. These instances can be created in a variety of different methods in your app. When your app is not dealing with thousands of items, it makes sense to create these instances while preparing the layout, because the layout information can be cached and referenced rather than computed on the fly. If the costs of computing all the attributes up front outweighs the benefits of caching in your app, it is just as easy to create attributes in the moment when they are requested.

Regardless, when creating new instances of the UICollectionViewLayoutAttributes class, use one of the following class methods:

  • layoutAttributesForCellWithIndexPath:

  • layoutAttributesForSupplementaryViewOfKind:withIndexPath:

  • layoutAttributesForDecorationViewOfKind:withIndexPath:

属性对象是你负责布局的一个UICollectionViewLayoutAttributes 类的子类。这些事例被很多不同的方法创建在你的应用中。当你的应用不足以处理上千个元素的时候,在准备布局时候创建这些事例是有意义的,因为布局信息可以被存储以参考而不是匆忙的计算。如果在之前计算所有属性的消耗超过了你应用缓存的意义,那么在使用的时候才创建这些属性也是很容易的。
不管怎样,当你创建一个UICollectionViewLayoutAttributes类的事例的时候,使用下列方法:
  • layoutAttributesForCellWithIndexPath:

  • layoutAttributesForSupplementaryViewOfKind:withIndexPath:

  • layoutAttributesForDecorationViewOfKind:withIndexPath:

You must use the correct class method based on the type of the view being displayed because the collection view uses that information to request the appropriate type of view from the data source object. Using the incorrect method causes the collection view to create the wrong views in the wrong places and your layout does not appear as intended.

After creating each attributes object, set the relevant attributes for the corresponding view. At a minimum, set the size and position of the view in the layout. In cases where the views of your layout overlap, assign a value to the zIndex property to ensure a consistent ordering of the overlapping views. Other properties let you control the visibility or appearance of the cell or view and can be changed as needed. If the standard attributes class does not suit your app’s needs, you can subclass and expand it to store other information about each view. When subclassing layout attributes, it’s required that you implement the isEqual: method for comparing your custom attributes because the collection view uses this method for some of its operations.

For more information about layout attributes, see UICollectionViewLayoutAttributes Class Reference.

你必须使用正确的基于视图展示类型的类方法,因为集合视图使用那些信息想数据源对象要求合适的视图类型。使用错误的方法会导致集合视图在错误的位置创建错误的视图,尽管你的视图并没有打算这么做。
创建每个属性对象之后,为相应的视图创建相应的属性。至少,设置视图在布局中的大小和位置。在你的布局中重叠的地方,给zindex属性指定一个值来确定重叠视图的顺序。其他属性让你控制单元格和视图的可视性和表现,可以根据需要来改变。如果标准的属性对象不能满足你应用的需要,你可以继承并拓展他来给每个视图存储其他信息。当你子类化布局属性对象的时候,要求你为你的自定义属性对象实现 isEqual:方法,因为集合视图使用这个方法来来做一些他需要的操作。
更多关于布局属性的信息,请看   UICollectionViewLayoutAttributes Class Reference

Preparing the Layout(准备布局)

At the beginning of the layout cycle, the layout object calls prepareLayout before beginning the layout process. This method is your chance to calculate information that later informs your layout. The prepareLayout method is not required to implement a custom layout but is provided as an opportunity to make initial calculations if necessary. After this method is called, your layout must have enough information to calculate the collection view’s content size, the next step in the layout process. The information, however, can range from this minimum requirement to creating and storing all the layout attributes objects your layout will use. Use of the prepareLayout method is subject to the infrastructure of your app and to what makes sense to compute up front versus what to compute upon request. For an example of what the prepareLayout method might look like, see Preparing the Layout.

在布局周期的开始,布局对象在布局过程的开始调用 perpareLayou方法。这种方法是你计算信息之后通知你的布局的一个机会。perpareLayout方法不要求实现自定义布局,而是被提供作为一个如果需要创建初始化计算的机会。你的布局必须有足够的信息来计算集合视图内容的大小,在布局过程中第二步。这些信息可以使用最低的要求来创建和存储你要使用的布局属性对象。使用 prepareLayout方法是你的应用程序的基本主题同时让在请求之前计算有意义。这个prepareLayout方法的例子,请看Preparing the Layout

Providing Layout Attributes for Items in a Given Rectangle(在给定的矩形中给元素提供布局元素)

During the final step of the layout process, the collection view calls your layout object’s layoutAttributesForElementsInRect: method. The purpose of this method is to provide layout attributes for every cell and every supplementary or decoration view that intersects the specified rectangle. For a large scrollable content area, the collection view may just ask for the attributes of items in the portion of that content area that is currently visible. In Figure 5-2, the currently visible content that your layout object needs to create attribute objects for is cells 6 through 20 along with the second header view. You must be prepared to provide layout attributes for any portion of your collection view content area. Such attributes might be used to facilitate animations for inserted or deleted items.

在布局过程的最后一步,集合视图要求你的布局对象的 layoutAttributesForElementsInRect 方法。这个方法的目的是为矩形中交错的每个单元格和每个增补视图和装饰视图提供布局属性。对一个比较大的滑动内容区域,集合视图可以要求内容视图中现在可见部分的元素的属性。在图5-2中,在当前可视内容中你的布局对象需要给6到20单元格和第二个节头视图创建属性对象。你必须准备好给任何你视图内容区域的部分提供布局属性。这些属性有助于展示插入删除元素时候的动画。
Figure 5-2  Laying out only the visible views

Because the layoutAttributesForElementsInRect: method is called after your layout object’s prepareLayout method, you should already have most of the information you need in order to return or create the required attributes. The implementation of your layoutAttributesForElementsInRect: method follows these steps:

  1. Iterate over the data generated by the prepareLayout method to either access cached attributes or create new ones.

  2. Check the frame of each item to see whether it intersects the rectangle passed to the layoutAttributesForElementsInRect: method.

  3. For each intersecting item, add a corresponding UICollectionViewLayoutAttributes object to an array.

  4. Return the array of layout attributes to the collection view.

因为layoutAttributesForElementsInRect: 方法在你的布局对象调用prepareLayout方法之后,你应该准备好你需要的大部分信息为了返回或创建需要的属性。实现你的layoutAttributesForElementsInRect方法有如下步骤:
  1. 遍历prepareLayout方法生成的数据来访问缓存或者创建新的项;
  2. 检查每个元素的frame来看是否使用LayoutAttributesForElementsInRect:方法来在矩形内交错。
  3. 为每个交叉项,创建一个相应的UICollectionViewLayoutAttributes  对象加入数组中。
  4. 给集合视图返回这个布局属性的数组。

Depending on how you manage your layout information, you might create UICollectionViewLayoutAttributes objects in your prepareLayout method or wait and do it in yourlayoutAttributesForElementsInRect: method. While forming an implementation that matches the needs of your application, keep in mind the benefits of caching layout information. Computing new layout attributes repeatedly for cells is an expensive operation, one that can have noticeably detrimental effects on your app’s performance. That said, when the amount of items your collection view manages is large, it may make more sense (for performance) to create the layout attributes when requested. It’s simply a matter of figuring out which strategy makes most sense for your app.

依据你怎样管理你的布局信息,你可以在你的preparelayout方法中创建一个 UICollectionViewLayoutAttributes 对象,或等待它在layoutAttributesForElementsInRect:方法中创建。当你构建一个匹配你应用需求的实现的时候,记住缓存布局信息的好处。反复计算单元格的新的布局属性是一种很浪费的操作,这会明显影响你的应用的表现。这就是说,当你的集合视图管理很多单元格的时候,在请求的时候创建布局属性是很必要的(对于性能)。为你的程序找到最有效的方法其实是很简单的。

Note: Layout objects also need to be able to provide layout attributes on demand for individual items. The collection view might request that information outside of the normal layout process for several reasons, including to create appropriate animations. For more information about providing layout attributes on demand, see Providing Layout Attributes On Demand.

For a specific example of how one might implement layoutAttributesForElementsInRect:, see Providing Layout Attributes.


注意:布局对象也需要能够对个别的的单元格提供布局属性。集合视图也可呢请求正常布局过程外的信息,包括创建合适的动画。更过关于根据要求提供布局属性的信息,请看   Providing Layout Attributes On Demand
一个关于如何实现layoutAttributesForElementsInRect:,方法的例子,请看 Providing Layout Attributes。

Providing Layout Attributes On Demand(按照要求提供布局属性)

The collection view periodically asks your layout object to provide attributes for individual items outside of the formal layout process. For example, the collection view asks for this information when configuring insertion and deletion animations for an item. Your layout object must be prepared to provide the layout attributes for each cell, supplementary view, and decoration view it supports. You do this by overriding the following methods:

  • layoutAttributesForItemAtIndexPath:

  • layoutAttributesForSupplementaryViewOfKind:atIndexPath:

  • layoutAttributesForDecorationViewOfKind:atIndexPath:

集合视图定期要求布局对象向个别元素提供正常布局过程之外的布局属性。比如,当集合视图插入或删除元素的时候会请求这个信息。你的布局对象必须准备好给每个单元格,增补视图,装饰视图提供布局属性。你可以通过重写下列方法来实现:

  • layoutAttributesForItemAtIndexPath:

  • layoutAttributesForSupplementaryViewOfKind:atIndexPath:

  • layoutAttributesForDecorationViewOfKind:atIndexPath:

Your implementation of these methods should retrieve the current layout attributes for the given cell or view. Every custom layout object is expected to implement thelayoutAttributesForItemAtIndexPath: method. If your layout does not contain any supplementary views, you do not need to override thelayoutAttributesForSupplementaryViewOfKind:atIndexPath: method. Similarly, if it does not contain decoration views, you do not need to override thelayoutAttributesForDecorationViewOfKind:atIndexPath: method. When returning attributes, you should not update the layout attributes. If you need to change the layout information, invalidate the layout object and let it update that data during a subsequent layout cycle.

你实现这些方法应该查看给定单元格或者视图的当前布局属性。每个自定义布局对象都被期望实现 layoutAttributesForItemAtIndexPath: 方法。如果你的布局不包括任何增补视图,你不需要重写 the layoutAttributesForSupplementaryViewOfKind:atIndexPath:  方法。类似的,如果不包括装饰视图,你不需要重写 layoutAttributesForDecorationViewOfKind:atIndexPath:  方法。当返回属性的时候,你不应该更新布局属性。如果你需要更改布局属性,无效化布局对象同时在下一个布局周期更新数据。

Connecting Your Custom Layout for Use (连接你的自定义布局来使用)

There are two ways to link your custom layout to the collection view: programmatically or through storyboards. The collection view links to its layout through a writable property,collectionViewLayout. To set the layout to your custom implementation, set your collection view’s layout property to an instance of your custom layout object. Listing 5-1 shows the line of code needed.

有两种方法连接你的自定义布局和集合视图:编程方法和通过故事板。集合视图连接他的布局对象通过一个可写的属性,collectionViewLayout。给你的实现设置布局,设置你自定义布局对象的实例到你的的集合视图的布局属性。就像5-1展示的那一行代码。

Listing 5-1  Linking your custom layout

self.collectionView.collectionViewLayout = [[MyCustomLayout alloc] init];

Otherwise, from your storyboard, open the Document Outline panel and select your collection view (it is listed in the drop-down menu for your controller). With the collection view selected, open the Attributes inspector in the Utilities pane, and underneath the section labeled Collection View change the Layout option from Flow to Custom. The option beneath it changes from Scroll Direction to Class, and you can now select your custom layout class.

IOS 集合视图指南6:创建自定义布局_第1张图片

否则,通过你的故事板,打开文档大纲视图面板选择你的集合视图(在你的控制器的点击下拉列表上)。选中集合视图,打开工具窗格中的属性检查器,把布局从flow 改成custom。在下面选择滑动方向类,这样你就选择了自定义布局类。

Making Your Custom Layouts More Engaging(让你的自定义布局更诱人)

Providing layout attributes for each cell and view during the layout process is required, but there are other behaviors that can improve the user experience with your custom layout. Implementing these behaviors is optional but recommended.

在布局过程中要求提供每个单元格和视图的布局属性,但是在你自动布局中有些其他行为可以提高用户体验。万事这些行为是可选的推荐的。

Elevating Content Through Supplementary Views(通过增补视图提升内容)

Supplementary views are separate from the collection view’s cells and have their own set of layout attributes. Like cells, these views are provided by the data source object, but their purpose is to enhance the main content of your app. For example, the UICollectionViewFlowLayout uses supplementary views for section headers and footers. Another app could use supplementary views to give each cell its own text label to display information about that cell. Like collection view cells, supplementary views undergo a recycling process to optimize the amount of resources used by the collection view. Therefore, all supplementary views used in your app should be subclassed from the UICollectionReusableView class.

The steps for adding supplementary views to your layouts are as follows:

  1. Register your supplementary view to the collection view’s layout object using either the registerClass:forSupplementaryViewOfKind:withReuseIdentifier: orregisterNib:forSupplementaryViewOfKind:withReuseIdentifier: method.

  2. In your data source, implement collectionView:viewForSupplementaryElementOfKind:atIndexPath:. Because these views are reusable, calldequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath: to dequeue, or create, a new reusable view and set any necessary data before returning it.

  3. Create layout attributes objects for your supplementary views just as you do for cells.

  4. Include these layout attributes objects in the array of attributes returned by the layoutAttributesForElementsInRect: method.

  5. Implement the layoutAttributesForSupplementaryViewOfKind:atIndexPath: method to return the attributes object for the specified supplementary view whenever queried.

The process for creating the attributes objects for supplementary views in your custom layout is nearly identical to the process for cells, but differs in that a custom layout can have multiple types of supplementary views but is restricted to one type of cell. This is because supplementary views are meant to enhance the main content and are therefore separate from it. There are many ways in which an app’s content can be supplemented, and so each of the supplementary view’s methods specifies which kind of view is being addressed to distinguish it from the others and allow your layout to compute its attributes correctly based on its type. When registering a supplementary view for use, the string you provide is used by the layout object to distinguish the view from others. For an example of incorporating supplementary views into your custom layout, see Incorporating Supplementary Views.

增补视图和集合视图的单元格有区别,有他们自己的布局属性设置。就像单元格,这些由数据源对象提供的视图。

未完待续……

本文原创,转载请注明出处:http://blog.csdn.net/zhenggaoxing/article/details/44301997


























你可能感兴趣的:(ios,UICollection)