iOS Modern Collection View

Posted Westwindrest

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS Modern Collection View相关的知识,希望对你有一定的参考价值。

TL;DR

  • 使用的技术: Compositional layout + Diffable data source。iOS 14+。
  • 创建 layout 以描述布局;
  • 创建 dataSource 以提供数据和 view:
    • 使用 CellRegistration 和 dequeueConfiguredReusableCell 来获取 cell;
    • 使用 SupplementaryRegistration 和 dequeueConfiguredReusableSupplementary 来获取 header、footer 等 supplementary views。

Compositional Layout

如上图所示,一个 compositional layout 由若干个 section 组成,每个 section 又由若干个 group 组成,每个 group 又包含若干个 item,因此,创建一个 layout 的过程即先创建最小展示单元 item 开始,再创建 group,创建 section,创建 layout:

func createBasicListLayout() -> UICollectionViewLayout 
  let itemSize = NSCollectionLayoutSize(
    widthDimension: .fractionalWidth(1.0),
    heightDimension: .fractionalHeight(1.0)
  )
  let item = NSCollectionLayoutItem(layoutSize: itemSize)

  let groupSize = NSCollectionLayoutSize(
    widthDimension: .fractionalWidth(1.0),
    heightDimension: .absolute(44)
  )
  let group = NSCollectionLayoutGroup.horizontal(
    layoutSize: groupSize,
    subitems: [item]
  )

  let section = NSCollectionLayoutSection(group: group)

  let layout = UICollectionViewCompositionalLayout(section: section)
  return layout

SectionProvider

如果需要多个布局不同的 sections,可以用下面的构造函数来创建 layout:

// UICollectionViewCompositionalLayout
init(
    sectionProvider: @escaping UICollectionViewCompositionalLayoutSectionProvider,
    configuration: UICollectionViewCompositionalLayoutConfiguration
)

其中闭包 sectionProvider 用于创建并返回 layout 的各个 secitons,声明如下:

typealias UICollectionViewCompositionalLayoutSectionProvider = (Int, NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection?
// arguments meaning:  sectionIndex, layoutEnvironment in ... 

该闭包也会在一些系统事件触发时被调用,如横竖屏切换、系统字体变更、iPad multitasking 引发的 size class 变更等,可以访问 layoutEnvironment 来获取相关信息,以适配这些场景。

LayoutConfiguration

UICollectionViewCompositionalLayoutConfiguration

  • scrollDirection: UICollectionView.ScrollDirection
    • 滚动方向:.vertical | .horizontal
  • interSectionSpacing: CGFloat
    • Sections 之间的间距
  • contentInsetsReference: UIContentInsetsReference
    • 定义 content inset 时的参考边界
    • .automatic | .none | .safeArea | .layoutMargins | .readableContent
      • 红色为 safeArea,绿色为 layoutMargins,蓝色为 readableContent
  • boundarySupplementaryItems: [NSCollectionLayoutBoundarySupplementaryItem]
    • 在整个 layout 边界处的一些附加 items,如全局 header、全局 footer、badge 等

LayoutEnvironment

包含 layout container 和 environment traits 的信息

  • container: NSCollectionLayoutContainer
    • contentSize: CGSize
    • effectiveContentSize: CGSize:content insets 生效之后的 size
    • contentInsets: NSDirectionalEdgeInsets
    • effectiveContentInsets: NSDirectionalEdgeInsets
  • traitCollection: UITraitCollection

List layout

Compositional layout 专门提供了一个接口来创建 list layout:

static func list(using: UICollectionLayoutListConfiguration) -> UICollectionViewCompositionalLayout

UICollectionLayoutListConfiguration 用于配置 list layout 的一些属性,构造函数中需要指定一个 appearance,如 .plain.sidebar.grouped 等。参见 Apple 文档 来查看可配置的属性,下面列出几种。

  • headerMode
    • .none:不展示 header;
    • .supplementary:使用 supplementary views 来展示 headers;
    • .firstItemInSection:使用 section 中的第一个 item 来作为 header。

Cell view 和 supplementary view 的获取

创建 dataSource 的时候,需要传入一个 cellProvider 来提供 cell view:

dataSource = UICollectionViewDiffableDataSource<Int, UUID>(collectionView: collectionView) 
    (collectionView: UICollectionView, indexPath: IndexPath, itemIdentifier: UUID) -> UICollectionViewCell? in
    // Configure and return cell.

之前我们需要调用 register 方法来注册所需要的 cell 类,然后在 cellProvider 中调用 dequeueReusableCell 方法来获取复用的 cell。iOS 14 提供了一组新的方法,通过 CellRegistration 来配置 cell,然后使用 dequeueConfiguredReusableCell 方法来获取复用的 cell,在调用该方法的时候会自动将 cell 注册到 collectionView 上。如:

let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Int>  cell, indexPath, item in
    var contentConfiguration = cell.defaultContentConfiguration()
    
    contentConfiguration.text = "\\(item)"
    contentConfiguration.textProperties.color = .lightGray
    
    cell.contentConfiguration = contentConfiguration


dataSource = UICollectionViewDiffableDataSource<Section, Int>(collectionView: collectionView) 
    (collectionView: UICollectionView, indexPath: IndexPath, itemIdentifier: Int) -> UICollectionViewCell? in
    
    return collectionView.dequeueConfiguredReusableCell(using: cellRegistration,
                                                        for: indexPath,
                                                        item: itemIdentifier)

注意不要在 cellProvider 里面创建 cell registration,会造成 cell 无法复用,且在 iOS 15+ 上直接报异常

对 cell 的配置通过 UIListContentConfiguration 来完成,调用 cell 的 defaultContentConfiguration 来获取一个包含一些预设属性的 configuration,根据传入的数据对其进行配置,然后再赋值回 cell 的 contentConfiguration。设置 contentConfiguration 会替换掉 cell 现有的 contentView。Cell configuration 具体可参见 Modern cell configuration (WWDC20 10027)

Header、footer 等 supplementary views 的配置和 cell 类似,设置 dataSource 的 supplementaryViewProvider 属性来提供用于创建 view 的闭包,在闭包里面调用 dequeueConfiguredReusableSupplementary 来获取一个复用的 view,该方法接受一个闭包参数 SupplementaryRegistration 来配置 view。

References

Java学习---Collection的学习

Collection基本方法的使用

package com.huawei.ftl;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.Reader;
import java.io.Serializable;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Observable;
import java.util.Observer;
import java.util.TimerTask;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.management.timer.Timer;

import com.huawei.testperson.Menu;

import java.util.*;




public class HelloFtl {
	public static void main(String[] args) throws Exception {
		// TODO 自动生成的方法存根
		System.out.println("--------------------------------------");
		System.out.println("--------------------------------------");
		System.out.println("--------------------------------------");
		System.out.println("--------------------------------------");
		SortedMap<String ,String> map = null;
		map = new TreeMap<String, String>();
		map.put("04122077", "FTL1012");
		map.put("04122076", "FTL");
		map.put("04122078", "1012");
		System.out.println("第一个元素对应的Key元素:" + map.firstKey());
		System.out.println("第一个元素对应的Value元素:" + map.get(map.firstKey()));
		System.out.println("最后一个元素对应的Key元素:" + map.lastKey());
		System.out.println("最后一个元素对应的Value元素:" + map.get(map.lastKey()));
		System.out.println("返回小于指定范围的集合");
		System.out.println("返回小于指定范围的集合");
		for(Map.Entry<String, String> me : map.headMap("04122078").entrySet())
		{
			System.out.println("	|-" + me.getKey() + ":" + me.getValue());
		}
		System.out.println("--------------------------------------");
	}
};

  

以上是关于iOS Modern Collection View的主要内容,如果未能解决你的问题,请参考以下文章

iOS11.3暗示iPad采用 Face ID

在 Relay Modern 中设置多个网络层

简单好用的C++ json库——JSON for Modern C++

使用 Relay Modern 创建-react-native-app / expo

iOS开发-UI Collection

Java学习---Collection的学习