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:
在你开始构建自定义布局前,先考虑一下这样做是否真的有必要。UICollectionViewFlowLayout 类已经提供了一系列显著量的特性(behavior),它们已经优化了效率,并且能够被以不同的方式采用以实现不同类型的标准布局。 只有在以下情况才需要考虑实现一个自定义布局:
-
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.
好消息是,从一个API角度(perspective)上看,实现一个自定义布局并不困难。 最难部分是执行各种算法来决定数据项在布局中的位置。 当你知道了那些数据项的位置之后,把这些信息直接提供给集合视图(collection view)。
Subclassing UICollectionViewLayout
一、子类化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:
对于自定义布局,你想要子类化UICollectionViewLayout,它为你的设计提供一个崭新的起点。 只有极少数的提供你的布局对象的核心特性行为(behavior)的方法是需要你自己实现。其它方法则只需要你根据需要重写它们来稍稍调整(tweak)布局行为即可。核心方法处理以下关键任务:
-
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.
尽管你可以创建一个功能性(functional)布局对象来只实现核心方法,但是如果你也实现几个可选方法可能会让你的布局更加吸引人(engaging).
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 the collectionView
propery, 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.
布局对象使用其数据源提供的信息来创建集合视图(collection view)的布局。布局通过在collectionView 特性上调用各种方法来跟数据源交流,collectionView 特性在所有布局方法中都是可访问的。 请记住在布局进程中集合视图知道什么,不知道什么。因为在布局进程运行时,集合视图不能监控布局和视图的位置。因此,即使布局对象不会限制你调用任何集合视图的方法,但是对于计算你的布局来说多信赖数据而不是集合视图的其它任何东西是很有必要的。
Understanding the Core Layout Process
1、理解核心布局进程
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:
在布局过程期间,集合视图调用布局对象的指定方法。 这些方法就是你计算数据项的位置和提供带有所需的主要信息的集合视图的机会。 其它方法也有可能被调用,但是这些方法总是以在布局过程中以以下顺序被调用。
-
Use the
prepareLayout
method to perform the up-front calculations needed to provide layout information.使用
prepareLayout 方法来执行提供布局信息所需的各种前期计算。
-
Use the
collectionViewContentSize
method to return the overall size of the entire content area based on your initial calculations.使用collectionViewContentSize 方法来返回基于你的初始计算的整个内容区的尺寸。
-
Use the
layoutAttributesForElementsInRect:
method to return the attributes for cells and views that are in the specified rectangle.使用
layoutAttributesForElementsInRect: 方法来返回指定矩形中的单元格和视图的属性。
Figure 5-1 illustrates how you can use the preceding methods to generate your layout information.
图5-1 演示了你可以如何使用前面的方法来生成你的布局信息
Laying out your custom content
图5-1 布局你的自定义内容
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.
prepareLayout 方法是你执行用来决定布局中单元格和视图的位置所需要的任何计算的好机会。 至少,你应该在该方法中计算足够的信息使其能返回内容区的整个尺寸,该尺寸返回到步骤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 theUICollectionViewFlowLayout
, it does not by default adjust the layout of content to scroll in only one direction.
集合视图使用内容尺寸来正确地配置其滚动视图。 例如,如果你计算后的内容尺寸宽度和高度超出当前设备的屏幕边界,滚动视图将同时开始两个方向上的滚动。 不像UICollectionViewFlowLayout, 它默认不调整布局的内容,只在一个方向上滚动。
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.
根据当前的滚动位置,集合视图然后调用layoutAttributesForElementsInRect: 方法来请求指定矩形内的单元格的属性和视图,它们或许跟可见矩形一样,或许不一样。 返回那个信息之后,核心布局过程就有效地完成了。
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’sshouldInvalidateLayoutForBoundsChange:
method and invalidates the layout if that method returns YES
.
布局完成后,单元格和视图的属性保持不变直到你或者集合视图无效化布局。 调用布局对象的invalidateLayout 方法导致布局进程再次开始,以调用一个新的prepareLayout方法开始。集合视图还能在滚动期间自动无效化你的布局。 如果用户滚动其内容,集合视图调用布局对象的shouldInvalidateLayoutForBoundsChange: 方法,并在该方法返回YES之后无效化布局。
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 方法并不会立即开始布局更新进程 。 该方法仅仅只是标记了当前数据和需要被更新的数据是不一样的。 在下个视图更新周期期间,集合视图检查其布局是否有变动(dirty),如果是则更新它。 实际上,你可以多次快速连续的调用invalidateLayout方法,而不用每次都触发一个即时布局更新。
Creating Layout Attributes
2、创建布局属性
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.
你的布局负责的属性对象都是 UICollectionViewLayoutAttributes 类的实例。 这些实例可以在应用程序里以一系列不同的方法创建。 当你的应用程序不处理成千上万条数据项时,在准备布局期间创建这些实例是有意义的,因为布局信息可以被缓存和引用,而不是飞快地计算。如果计算所有前期属性所花费的代价比缓存在应用程序中有价值,则只需要在它们被请求时创建属性。
Regardless, when creating new instances of the UICollectionViewLayoutAttributes
class, use one of the following class methods:
无论如何,当创建UICollectionViewLayoutAttributes 类的新实例时,使用一个以下类方法:
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.
创建完每个属性对象之后,为相应的视图设置相关的属性。至少设置视图在布局中的尺寸和位置。 当布局中的视图有重叠的情况时,给 zIndex 特性分配一个值来确保重叠视图的顺序。 其它特性可以让你根据需要控制单元格和视图的可视性和外观。如果标准属性类不符合你的需求,你可以子类化它并扩展它来存储每个视图的其它信息。 当你子类化布局属性时,它要求你实现isEqual:方法来比较自定义属性,因为集合视图在一些操作中将使用该方法。
For more information about layout attributes, see UICollectionViewLayoutAttributes Class Reference.
关于布局属性的更多信息,请看UICollectionViewLayoutAttributes Class Reference.
Preparing the Layout
3、为布局做准备
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.”
在布局周期的开始,布局对象在开始布局进程之前,调用prepareLayout 方法。 该方法可以用来计算稍候通知布局的各种信息。 prepareLayout 方法不要求实现一个自定义布局,但是提供了一个机会来做一些必要的初始化计算。 调用完该方法之后,你的布局必须有足够的信息来计算集合视图的视图内容尺寸,布局进程的下一步。 然而,信息可以从最低要求到创建并存储布局将使用的所有布局属性对象。使用 prepareLayout 方法受应用程序基础架构,以及什么对计算前面有意义与什么对计算超出要求等影响(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.)。关于prepareLayout方法的例子,请看 “Preparing the Layout.”
Providing Layout Attributes for Items in a Given Rectangle
4、给一个给定矩形中的数据项提供布局属性
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以及第二个头视图创建属性对象,它们是当前的可见内容。你必须准备好提供集合视图内容区任何部分的布局属性。 这些属性可能被用于促进插入或删除数据项的动画。
Laying out only the visible views
图5-2 只布局可见视图
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:
因为layoutAttributesForElementsInRect: 方法在布局对象的prepareLayout 方法之后调用,你应该已经有大多数你需要的信息以便返回或创建被请求的各种属性。 layoutAttributesForElementsInRect: 方法的实现遵循以下步骤:
-
Iterate over the data generated by the
prepareLayout
method to either access cached attributes or create new ones.迭代由prepareLayout 方法生成的数据,用来访问被缓存属性或创建新属性。
-
Check the frame of each item to see whether it intersects the rectangle passed to the
layoutAttributesForElementsInRect:
method.检查每个数据项的框架(frame),查看它是否跟传递给 layoutAttributesForElementsInRect: 方法的矩形相交互。
-
For each intersecting item, add a corresponding
UICollectionViewLayoutAttributes
object to an array.对于每个交互的数据项,添加一个相应的 UICollectionViewLayout 属性对象到一个数组。
-
Return the array of layout attributes to the collection view.
把布局属性的数组返回给集合视图。
Depending on how you manage your layout information, you might create UICollectionViewLayoutAttributes
objects in your prepareLayout
method or wait and do it in your layoutAttributesForElementsInRect:
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方法或者等待在layoutAttributesForElementsInRect: 方法中创建UICollectionViewLayoutAttributes 对象。当你构成(forming)一个符合你的应用程序的需要的实现时,牢记缓存布局信息的好处。为单元格重复计算新的布局属性是一个昂贵的操作,它对你的应用程序性能明显不利。就是说,当你的结合视图管理的数据项总量巨大时,在请求时创建布局属性对性能来说更有利。这是一个找出哪些策略最最适合应用程序的简单问题。
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.”
注意:布局对象还需要能够为独立数据项需求提供布局属性。 集合视图可能处于多种原因请求在正常布局进程外的信息,包括创建正确的动画。 关于根据需求提供布局属性的更多信息,请看“Providing Layout Attributes On Demand.”
For a specific example of how one might implement layoutAttributesForElementsInRect:
, see “Providing Layout Attributes.”
关于如何实现layoutAttributesForElementsRect:方法的一种可能实现,请看“Providing Layout Attributes.”
Providing Layout Attributes On Demand
5、根据需要提供布局属性
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:
集合视图周期性的请求你的布局对象为正式布局进程意外的独立数据项提供各种属性。 比如,。集合视图在为一个数据项配置插入和删除动画时会请求该信息。 你的布局对象必须准备好为每个单元格,辅助视图以及它支持的装饰视图提供各种布局属性。 你可以通过重写以下方法实现:
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 the layoutAttributesForItemAtIndexPath:
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:方法。 如果你的布局不包含任何辅助视图,你就不需要重写layoutAttributesForSupplementaryViewOfKind:atIndexPath:layoutAttributesForItemAtIndexPath: 方法。 如果你的布局不包含任何装饰视图,你就不需要冲昂鞋layoutAttributesForDecorationViewOfKind:atIndexPath: 方法。 当返回属性时,你不应该更新布局属性。 如果你需要改变布局信息,无效化布局对象并让它在一个子序列布局周期里更新数据。
Connecting Your Custom Layout for Use
6、连接你的自定义布局以备用
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 显示了需要的代码行。
Linking your custom layout
列表 5-1 连接你的自定义布局
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.
或者,从你的故事板,打开Document Outline 面板,选择你的集合视图(它在你的控制器的下拉菜单中). 选择好之后,打开Utilities 面板中的Attributes inspector, 并且把以下标记集合视图的选项从Flow改为Custom。 它下面(beneath)的选项从从滚动方向变为类,你现在就可以选择你的自定义布局类了。
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
1、通过辅助视图提升内容
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.
辅助视图是跟集合视图的单元格分离并由它们自己的布局属性集。 就像单元格,这些视图由数据源对象提供,但是它们的目的是增强应用程序的主要内容。 比如,UICollectionViewFlowLayout 使用辅助视图来做区头(section headers)和区尾。 另一个应用程序可能使用辅助视图来给每个单元格(cell)设置一个自己的文本标签来显现该单元格的内容。 就像集合视图单元格,辅助视图通过一个再循环(recycling)进程来优化集合视图使用的资源总量。 因此,应用程序中使用的所有辅助视图都应该是 UICollectionReusableView 类的子类。
The steps for adding supplementary views to your layouts are as follows:
添加辅助视图到你的布局需要完成以下步骤:
-
Register your supplementary view to the collection view’s layout object using either the
registerClass:forSupplementaryViewOfKind:withReuseIdentifier:
orregisterNib:forSupplementaryViewOfKind:withReuseIdentifier:
method.使用registerClass:forSupplementaryViewOfKind:withReuseIdentifier: 方法或registerNib:forSupplementaryViewOfKind:withReuseIdentifier: 方法在集合视图的布局对象上注册你的辅助视图
-
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.在你的数据源中实现
collectionView:viewForSupplementaryElementOfKind:atIndexPath:方法。 因为这些视图是可以重复利用的, 调用
dequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath: 方法来让一个新的可再用视图出队列或创建一个新的可再用视图,并且在返回它之前设置任何必要的数据。
-
Create layout attributes objects for your supplementary views just as you do for cells.
给你的辅助视图创建布局属性对象,就像你为单元格做的一样。 -
Include these layout attributes objects in the array of attributes returned by the
layoutAttributesForElementsInRect:
method.把这些布局属性对象放入一个属性数组,由layoutAttributesForElementsInRect: 方法返回。
-
Implement the
layoutAttributesForSupplementaryViewOfKind:atIndexPath:
method to return the attributes object for the specified supplementary view whenever queried.实现layoutAttributesForSupplementaryViewOfKind:atIndexPath: 方法来为任何时候查询的指定辅助视图返回属性对象。
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.”
为自定义布局辅助视图创建属性对象的过程和为单元格创建属性对象的过程几乎一样,除了一个自定义布局可以有多个类型的辅助属性,但是只能有一种类型的单元格。 这是因为辅助视图是为了提升主要内容,因此它被从布局中分离出来。在很多方法里面可以辅助说明应用程序的内容,每个辅助视图的方法都可以指定视图的处理类型,用它来区别别的视图,并且可以让你的布局根据该类型来正确地计算它的属性。当泽侧一个辅助视图时,你提供的字符串是布局用来跟其它视图区别的标记。 关于给你的自定义布局添加辅助视图的例子,请看“Incorporating Supplementary Views.”
Including Decoration Views in Your Custom Layouts
2、让你的自定义布局包含装饰视图
Decoration views are visual adornments that enhance the appearance of your collection view layouts. Unlike cells and supplementary views, decoration views provide visual content only and are thus independent of the data source. You can use them to provide custom backgrounds, fill in the spaces around cells, or even obscure cells if you want. Decoration views are defined and managed solely by the layout object and do not interact with the collection view’s data source object.
装饰视图是用来增强集合视图布局外观的可视化装饰。不像单元格和辅助视图,装饰视图只提供可视化内容,因此它不依赖数据源。你可以使用它们来提供自定义背景,填充单元格边上的空白,或者如果你想你甚至可以隐藏(obscure)单元格。 装饰视图仅由布局对象定义和管理,并不跟集合视图的数据源发生交互。
To add decoration views to your layouts, do the following:
要想给布局添加装饰视图,你需要完成以下步骤:
-
Register your decoration view with the layout object using the
registerClass:forDecorationViewOfKind:
orregisterNib:forDecorationViewOfKind:
method. Although this approach is similar to registering cells and supplementary views, remember that registering decoration views occurs within the layout object, not within the data source.用
registerClass:forDecorationViewOfKind:
或registerNib:forDecorationViewOfKind: 方法和布局对象一起注册你的装饰视图。 尽管该方法跟注册单元格和辅助视图相似,请记住注册装饰视图在布局对象中发生,而不是在数据源中。
-
In your layout object’s
layoutAttributesForElementsInRect:
method, create attributes for your decoration views just as you do for your cells and supplementary views.在你的布局对象的layoutAttributesForElementsInRect:方法中,为你的装饰视图创建各种属性,正如你为你的单元格和辅助视图所做。
-
Implement the
layoutAttributesForDecorationViewOfKind:atIndexPath:
method in your layout object and return the attributes for your decoration views when asked.在你的布局对象中实现
layoutAttributesForDecorationViewOfKind:atIndexPath: 方法,并在请求时为你的装饰视图返回各种属性。
-
Optionally, implement the
initialLayoutAttributesForAppearingDecorationElementOfKind:atIndexPath:
andfinalLayoutAttributesForDisappearingDecorationElementOfKind:atIndexPath:
methods to handle animations for the appearance and disappearance of your decoration views. For more information, see “Making Insertion and Deletion Animations More Interesting”可选择地实现
initialLayoutAttributesForAppearingDecorationElementOfKind:atIndexPath:
和finalLayoutAttributesForDisappearingDecorationElementOfKind:atIndexPath: 方法来处理装饰视图的出现和隐藏动画。 更多信息,请看“Making Insertion and Deletion Animations More Interesting”
The creation process for decoration views is different from the process for cells and supplementary views. Registering a class or nib file is all you have to do to ensure that decoration views are created when they are needed. Because they are purely visual, decoration views are not expected to need any configuration beyond what is already done in the provided nib file or by the object’s initWithFrame:
method. For this reason, when a decoration view is needed, the collection view creates it for you and applies the attributes provided by the layout object. Any decoration views should still be a subclass of UICollectionReusableView
because the layout object employs a recycling mechanism for its decoration views.
装饰视图的创建过程和创建单元格和辅助视图的过程不一样。 你只需要在需要时为装饰视图注册一个类或nib文件。 因为它们是纯粹的视觉,装饰视图不需要在提供的nib文件或对象的initWithFrame: 方法中已经提供的任何
配置。 因此,当需要一个装饰视图时,集合视图为你创建并由布局对象提供各种属性。 任何装饰视图也都应该是 UICollectionReusableView 类的子类,因为布局对象为它的装饰视图采用了一套回收机制。
Note: When creating the attributes for your decoration views, don’t forget to take into account the zIndex
property. You can use the zIndex attribute to layer your decoration views behind (or, if you prefer, in front of) the displayed cells and supplementary views.
注意:当你为装饰视图创建各种属性时,不要忘记考虑zIndex特性。 你可以使用zIndex 属性让装饰视图置于显示的单元格和辅助视图后面,或者前面。
Making Insertion and Deletion Animations More Interesting
3、让插入和删除动画更加有趣
Inserting and deleting cells and views poses an interesting challenge during layout. Inserting a cell can cause the layout for other cells and views to change. Even though the layout object knows how to animate existing cells and views from their current locations to new locations, it has no current location for the cell being inserted. Rather than insert the new cell without animations, the collection view asks the layout object to provide a set of initial attributes to use for the animation. Similarly, when a cell is deleted, the collection view asks the layout object to provide a set of final attributes to use for the endpoint of any animations.
在布局过程中插入和删除单元格和视图构成了一个有趣的挑战。 插入一个单元格可以导致布局上的其它单元格和视图发生改变。 尽管布局对象了解如何动画已经存在的单元格和视图从当前位置到新的位置,但是它没有刚被插入的单元格的位置。 集合视图请求布局对象提供一组初始属性供动画使用,而不是不用动画插入新单元格。 相似地,当一个单元格被删除时,集合视图请求布局对象提供一组结束属性用于任何动画的结束。
To understand how initial attributes work, it helps to see an example. The starting layout (Figure 5-3) shows a collection view that initially contains only three cells. When a new cell is inserted, the collection view asks the layout object to provide initial attributes for the cell being inserted. In this case, the layout object sets the initial position of the cell to the middle of the collection view and sets its alpha value to 0 to hide it. During the animations, this new cell appears to fade in and move from the center of the collection view to its final position in the lower-right corner.
要想理解初始属性如何工作,看个例子会有帮助。 图5-3中开始布局显示了一个最初包含三个单元格的集合视图。 当一个新单元格被插入时,集合视图请求布局对象为即将插入的单元格提供初始属性。在这种情况下,布局对象把单元格的初始位置设置为集合视图的中心,并且设置它的透明纸(alpha value)为0,把它隐藏。 在动画过程中,该新单元格开始淡入(fade in)并从集合视图的中心点移动到它的最终位置-即屏幕的右下角。
Specifying the initial attributes for an item appearing onscreen
图5-3 为一个出现在屏幕上的数据项指定初始属性
Listing 5-2 shows the code you might use to specify the initial attributes for the inserted cell from Figure 5-3. This method sets the position of the cell to the center of the collection view and makes it transparent. The layout object would then provide the final position and alpha for the cell as part of the normal layout process.
列表5-2 显示了实现图5-3所需要使用的代码。 该方法把单元格的位置设置为集合视图的中心,并且把它设置为透明。 然后作为正常布局进程的一部分,布局对象将为单元格提供最终位置和透明度。
Specifying the initial attributes for an inserted cell
列表 5-2 为一个插入单元格指定初始属性
- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath { |
UICollectionViewLayoutAttributes* attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath]; |
attributes.alpha = 0.0; |
CGSize size = [self collectionView].frame.size; |
attributes.center = CGPointMake(size.width / 2.0, size.height / 2.0); |
return attributes; |
} |
Note: Listing 5-2 would animate all cells when one is inserted, so the three cells that were already present before the fourth was inserted would also pop out from the center of the collection view. To animate only the cell being inserted, check to see if the index path of the item matches the index path of an item passed to the prepareForCollectionViewUpdates:
method and only perform the animation if a match is found. Otherwise, return the attributes returned by calling the super
method of initialLayoutAttributesForAppearingItemAtIndexPath:
.
注意: 当一个单元格被插入时,列表5-2 可能动画所有单元格,因此已经被呈现的三个单元格也可能从集合视图的中心弹出。要想只动画被插入的单元格,检查数据项的索引路径是否跟传递到prepareForCollectionViewUpdates: 方法的索引路径相匹配,并且只在匹配成功时执行动画。 另外,调用
initialLayoutAttributesForAppearingItemAtIndexPath:方法的super方法来返回返回的属性。
The process for handling deletions is identical to the process for insertions except that you specify the final attributes instead of the initial attributes. From the previous example, if you used the same attributes that you used when inserting the cell, deleting the cell would cause it to fade out while moving to the center of the collection view. There are six methods available to you within the UICollectionViewLayout
class—two separate methods (for initial and final attributes) for items, supplementary views, and decoration views.
处理删除的过程和处理插入的过程是一样的,除了你需要指定最终属性而不是初始属性。 从前一个例子,如果你使用跟插入单元格时相同的属性,删除单元格将导致单元格移动到集合视图中心过程中淡出(fade out)。 在UICollectionViewLayout 类中有6种方法你可以使用--两种分离的方法(用于初始和最终属性)用于数据项,辅助视图,以及装饰视图。
Improving the Scrolling Experience of Your Layout
4、增进布局的滚动体验
Your custom layout object can influence the scrolling behavior of the collection view to create a better user experience. When scrolling-related touch events end, the scroll view determines the final resting place of the scrolling content based on the current speed and deceleration rate in effect. When the collection view knows that location, it asks its layout object if the location should be modified by calling itstargetContentOffsetForProposedContentOffset:withScrollingVelocity:
method. Because it calls this method while the underlying content is still moving, your custom layout can affect the final resting point of the scrolling content.
自定义布局对象可以影响集合视图的滚动行为以创建一个更好的用户体验。 当滚动相关的触摸事件结束时,滚动视图根据当前的速度以及影响的减速率(deceleration rate)决定滚动内容的最终剩余位置。当集合视图知道那个位置,它询问它的布局对象是否应该调用targetContentOffsetForProposedContentOffset:withScrollingVelocity: 方法来修改该位置。因为在底层内容还在移动的过程中调用该方法,所以自定义布局可以影响滚动内容的最终剩余点。
Figure 5-4 demonstrates how you might use your layout object to change the scrolling behavior of the collection view. Suppose the collection view offset starts at (0, 0) and the user swipes left. The collection view computes where the scrolling would naturally stop and provides that value as the “proposed” content offset value. Your layout object might change the proposed value to ensure that when scrolling stops, an item is centered precisely in the visible bounds of the collection view. This new value becomes the target content offset and is what you return from yourtargetContentOffsetForProposedContentOffset:withScrollingVelocity:
method.
图5-4 演示了你如何使用布局对象来改变集合视图的滚动行为。 假设集合视图的偏移量开始于(0,0),用户向左轻扫屏幕。 集合视图计算滚动应该在哪自然地停止并把该值作为"建议的"内容偏移量值提供给视图。你的布局对象可能改变该建议值,以确保滚动停止时,一个项目正是在集合视图的可见边界居中。 这个新值变成目标内容偏移量,正是你从targetContentOffsetForProposedContentOffset:withScrollingVelocity:方法中返回的值。
Changing the proposed content offset to a more appropriate value
图 5-4 更改建议内容偏移量到一个更恰当的值
Tips for Implementing Your Custom Layouts
三、实现自定义布局的小提示
Here are some tips and suggestions for implementing your custom layout objects:
以下是一些实现自定义布局对象的提示和建议
-
Consider using the
prepareLayout
method to create and store theUICollectionViewLayoutAttributes
objects you need for later. The collection view is going to ask for layout attribute objects at some point, so in some cases it makes sense to create and store them up front. This is especially true if you have a relatively small number of items (several hundred) or the actual layout attributes for those items change infrequently.考虑使用prepareLayout方法来创建并存储你以后需要的UICollectionViewLayoutAttributes 对象。 集合视图将会在一些点请求布局属性对象,所以在一些情况下最好提前创建和存储它们。 这点在你有一个相对少量的数据项(几百)或者那些数据项的实际布局属性不经常改变时尤其正确。
If your layout needs to manage thousands of items, though, you need to weigh the benefits of caching versus recomputing. For variable-size items whose layout changes infrequently, caching generally eliminates the need to recompute complex layout information regularly. For large numbers of fixed-size items, it may just be simpler to compute attributes on demand. And for items whose attributes change frequently, you might be recomputing all the time anyway so caching may just take up extra space in memory.
如果你的布局需要管理成千上万的数据项,你就需要权衡缓存和重复计算之间的好处。 对于数量可变布局不经常改变的数据项,缓存通常不需要定期重复计算复杂的布局信息。 对于固定数量的大数量数据项,在需要的时候计算属性更简便。 对于属性经常改变的数据项,你或许需要总是重复计算,因此缓存可能只是占用内存的额外空间。
-
Avoid subclassing
UICollectionView
. The collection view has little or no appearance of its own. Instead, it pulls all of its views from your data source object and all of the layout-related information from the layout object. If you are trying to lay out items in three dimensions, the proper way to do it is to implement a custom layout that sets the 3D transform of each cell and view appropriately.避免子类化UICollectionView。 集合视图只有一点或没有它自己的外观。 相反,它从你的数据源拉取所有它的视图,并且从布局对象拉取布局相关的所有信息。 如果你想尝试以3D方式布局数据项,正确的方法是实现一个自定义布局,在该布局上为每个单元格设置正确地3D变换和视图。
-
Never call the
visibleCells
method ofUICollectionView
from thelayoutAttributesForElementsInRect:
method of your custom layout object. The collection view knows nothing of where items are positioned, other than what the layout object tells it. So asking for the visible cells is just going to forward the request to your layout object.Your layout object should always know the location of items in the content area and should be able to return the attributes of those items at any time. In most cases, it should do this on its own. In a limited number of cases, the layout object might rely on information in the data source to position items. For example, a layout that displays items on a map might retrieve the map location of each item from the data source.
-
决不要从自定义布局对象的
layoutAttributesForElementsInRect:中
调用UICollectionView的visibleCells 方法。 集合视图对数据项在哪一无所知,除了布局对象告诉它的信息之外。 因此请求可视单元格仅仅是把请求转发给你的布局对象。
你的布局对象应该总是知道在内容区的数据项的位置,并且应该能够在任何时候返回那些数据项的属性。 在大多数情况下,它应该自己做到这点。 在极少数情况下,布局对象可能依赖数据源中的信息来定位数据项。 比如,一个在地图上显示数据项的布局可能从数据源那取回每个数据项的地图位置。