美文网首页ios开发那些事
UIKit框架(十六) —— 基于自定义UICollection

UIKit框架(十六) —— 基于自定义UICollection

作者: 刀客传奇 | 来源:发表于2019-04-27 15:59 被阅读62次

版本记录

版本号 时间
V1.0 2019.04.27 星期六

前言

iOS中有关视图控件用户能看到的都在UIKit框架里面,用户交互也是通过UIKit进行的。感兴趣的参考上面几篇文章。
1. UIKit框架(一) —— UIKit动力学和移动效果(一)
2. UIKit框架(二) —— UIKit动力学和移动效果(二)
3. UIKit框架(三) —— UICollectionViewCell的扩张效果的实现(一)
4. UIKit框架(四) —— UICollectionViewCell的扩张效果的实现(二)
5. UIKit框架(五) —— 自定义控件:可重复使用的滑块(一)
6. UIKit框架(六) —— 自定义控件:可重复使用的滑块(二)
7. UIKit框架(七) —— 动态尺寸UITableViewCell的实现(一)
8. UIKit框架(八) —— 动态尺寸UITableViewCell的实现(二)
9. UIKit框架(九) —— UICollectionView的数据异步预加载(一)
10. UIKit框架(十) —— UICollectionView的数据异步预加载(二)
11. UIKit框架(十一) —— UICollectionView的重用、选择和重排序(一)
12. UIKit框架(十二) —— UICollectionView的重用、选择和重排序(二)
13. UIKit框架(十三) —— 如何创建自己的侧滑式面板导航(一)
14. UIKit框架(十四) —— 如何创建自己的侧滑式面板导航(二)
15. UIKit框架(十五) —— 基于自定义UICollectionViewLayout布局的简单示例(一)

Step 3: Adoptng the CustomLayout

在构建和运行项目之前,您需要:

  • 使集合视图采用CustomLayout类。
  • 使JungleCupCollectionViewController支持自定义补充视图supplementary views

打开Main.storyboard并在Jungle Cup Collection View Controller Scene中选择Collection View Flow Layout,如下所示:

接下来,打开Identity Inspector并将Custom Class更改为CustomLayout,如下所示:

接下来,打开JungleCupCollectionViewController.swift

添加计算属性customLayout以避免详细代码重复。

您的代码应如下所示:

var customLayout: CustomLayout? {
  return collectionView?.collectionViewLayout as? CustomLayout
}

接下来,使用以下内容替换setUpCollectionViewLayout()

 
  private func setupCollectionViewLayout() {
    guard let collectionView = collectionView,
      let customLayout = customLayout else {
        return
    }
    // 1
    collectionView.register(
        UINib(nibName: "HeaderView", bundle: nil),
        forSupplementaryViewOfKind: CustomLayout.Element.header.kind,
        withReuseIdentifier: CustomLayout.Element.header.id
    )
    collectionView.register(
        UINib(nibName: "MenuView", bundle: nil),
        forSupplementaryViewOfKind: CustomLayout.Element.menu.kind,
        withReuseIdentifier: CustomLayout.Element.menu.id
    )
    
    // 2
    customLayout.settings.itemSize = CGSize(width: collectionView.frame.width, height: 200)
    customLayout.settings.headerSize = CGSize(width: collectionView.frame.width, height: 300)
    customLayout.settings.menuSize = CGSize(width: collectionView.frame.width, height: 70)
    customLayout.settings.sectionsHeaderSize = CGSize(width: collectionView.frame.width, height: 50)
    customLayout.settings.sectionsFooterSize = CGSize(width: collectionView.frame.width, height: 50)
    customLayout.settings.isHeaderStretchy = true
    customLayout.settings.isAlphaOnHeaderActive = true
    customLayout.settings.headerOverlayMaxAlphaValue = CGFloat(0)
    customLayout.settings.isMenuSticky = true
    customLayout.settings.isSectionHeadersSticky = true
    customLayout.settings.isParallaxOnCellsEnabled = true
    customLayout.settings.maxParallaxOffset = 60
    customLayout.settings.minimumInteritemSpacing = 0
    customLayout.settings.minimumLineSpacing = 3
}

以下是上面代码的作用:

  • 1) 首先,注册用于弹性标题和自定义菜单的自定义类。 这些是已在初始项目中实现的UICollectionReusableView子类。
  • 2) 最后,设置CustomLayout设置的大小,行为和间距sizes, behaviours and spacings

在构建运行应用程序之前,将以下两个case选项添加到viewForSupplementaryElementOfKind(_:viewForSupplementaryElementOfKind:at :)以处理自定义补充视图类型:

case CustomLayout.Element.header.kind:
  let topHeaderView = collectionView.dequeueReusableSupplementaryView(
    ofKind: kind,
    withReuseIdentifier: CustomLayout.Element.header.id,
    for: indexPath)
  return topHeaderView
      
case CustomLayout.Element.menu.kind:
  let menuView = collectionView.dequeueReusableSupplementaryView(
    ofKind: kind,
    withReuseIdentifier: CustomLayout.Element.menu.id,
    for: indexPath)
  if let menuView = menuView as? MenuView {
    menuView.delegate = self
  }
  return menuView

做得好! 这是一段漫长的旅程,但你差不多完成了。

构建并运行项目! 您应该看到类似于以下内容的内容:

入门项目中的UICollectionView现在有一些额外的功能:

  • 在顶部有一个巨大的标题显示丛林杯的标志。
  • 在这之下,有一个带有四个按钮的菜单,每个团队一个。 如果点击按钮,集合视图将重新加载相应的团队。

你已经做得很好,但你可以做得更好。 现在是时候为你的UICollectionView打造一些漂亮的视觉效果了。


Adding Stretchy, Sticky and Parallax Effects

在本UICollectionViewLayout教程的最后一部分中,您将添加以下视觉效果:

  • 1) 使header有弹性。
  • 2) 在菜单和section headers中添加粘性效果。
  • 3) 实现平滑的视差效果,使用户界面更具吸引力。

注意:UICollectionViewLayout教程的以下部分暗示了仿射变换的基本知识。

1. Affine Transforms

Core Graphics CGAffineTransform API是将视觉效果应用于UICollectionView元素的最佳方式。

由于各种原因,仿射变换非常有用:

  • 1) 它们允许您在极少数代码行中创建复杂的视觉效果,如平移,缩放和旋转,或三者的组合。
  • 2) 它们以完美的方式与UIKit组件和AutoLayout进行互操作。
  • 3) 它们可帮助您在复杂情况下保持最佳性能。

仿射变换背后的数学真的很酷。 但是,解释CGATransform幕后矩阵的工作方式超出了本UICollectionViewLayout教程的范围。

如果您对此主题感兴趣,可以在 Apple’s Core Graphic Framework Documentation中找到更多详细信息。

2. Transforming Visible Attributes

打开CustomLayout.swift并将layoutAttributesForElements(in :)更新为以下内容:

override public func layoutAttributesForElements(
  in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {

    guard let collectionView = collectionView else {
      return nil
    }
    visibleLayoutAttributes.removeAll(keepingCapacity: true)
    // 1
    let halfHeight = collectionViewHeight * 0.5
    let halfCellHeight = cellHeight * 0.5
    // 2
    for (type, elementInfos) in cache {
      for (indexPath, attributes) in elementInfos {
        // 3
        attributes.parallax = .identity
        attributes.transform = .identity
        // 4
        updateSupplementaryViews(
          type,
          attributes: attributes,
          collectionView: collectionView,
          indexPath: indexPath)
        if attributes.frame.intersects(rect) {
          // 5
          if type == .cell,
            settings.isParallaxOnCellsEnabled {
              updateCells(attributes, halfHeight: halfHeight, halfCellHeight: halfCellHeight)
          }
          visibleLayoutAttributes.append(attributes)
        }
      }
    }
    return visibleLayoutAttributes
}

以下是对上述情况的逐步说明:

  • 1) 您存储一些有用的值以避免在循环中计算它们。
  • 2) 这与此方法的先前版本相同。 您迭代所有缓存的属性。
  • 3) 重置为默认值视差parallax变换和元素属性transform
  • 4) 目前,您只需调用一种方法来更新不同类型的补充视图(supplementary views)。 您将在此代码块之后实现它。
  • 5) 检查当前属性是否属于一个单元格。 如果在布局设置中激活了视差效果,请调用方法以更新其属性。 如上所述,您将在此代码块之后实现此方法。

接下来,是时候实现上面循环中调用的两个方法了:

  • updateSupplementaryViews(_:attributes:collectionView:indexPath:)
  • updateCells(_:halfHeight:halfCellHeight:)

添加以下内容:

private func updateSupplementaryViews(_ type: Element,
                                      attributes: CustomLayoutAttributes, 
                                      collectionView: UICollectionView,
                                      indexPath: IndexPath) {
    // 1
    if type == .sectionHeader,
      settings.isSectionHeadersSticky {
        let upperLimit = 
           CGFloat(collectionView.numberOfItems(inSection: indexPath.section))
           * (cellHeight + settings.minimumLineSpacing)
        let menuOffset = settings.isMenuSticky ? menuSize.height : 0
        attributes.transform =  CGAffineTransform(
          translationX: 0,
          y: min(upperLimit,
          max(0, contentOffset.y - attributes.initialOrigin.y + menuOffset)))
    }
    // 2
    else if type == .header,
      settings.isHeaderStretchy {
        let updatedHeight = min(
          collectionView.frame.height,
          max(headerSize.height, headerSize.height - contentOffset.y))
        let scaleFactor = updatedHeight / headerSize.height
        let delta = (updatedHeight - headerSize.height) / 2
        let scale = CGAffineTransform(scaleX: scaleFactor, y: scaleFactor)
        let translation = CGAffineTransform(
          translationX: 0,
          y: min(contentOffset.y, headerSize.height) + delta)
        attributes.transform = scale.concatenating(translation)
        if settings.isAlphaOnHeaderActive {
          attributes.headerOverlayAlpha = min(
            settings.headerOverlayMaxAlphaValue,
            contentOffset.y / headerSize.height)
        }
    }
    // 3
    else if type == .menu,
      settings.isMenuSticky {
        attributes.transform = CGAffineTransform(
          translationX: 0,
          y: max(attributes.initialOrigin.y, contentOffset.y) - headerSize.height)
    }
  }

依次记录每个编号的注释:

  • 1) 测试当前元素是否为section header。 然后,如果在布局设置中激活粘性行为,则计算transform。 最后将计算值分配给属性的transform属性。
  • 2) 与上面相同的例程,但这次检查元素是否是top header。 如果激活了弹性效果,请执行变换计算。
  • 3) 同样的例程。 这次执行粘性菜单的变换计算。

现在是时候transform集合视图单元格了:

private func updateCells(_ attributes: CustomLayoutAttributes,
                         halfHeight: CGFloat,
                         halfCellHeight: CGFloat) {
  // 1
  let cellDistanceFromCenter = attributes.center.y - contentOffset.y - halfHeight
    
  // 2
  let parallaxOffset = -(settings.maxParallaxOffset * cellDistanceFromCenter)
    / (halfHeight + halfCellHeight)
  // 3 
  let boundedParallaxOffset = min(
    max(-settings.maxParallaxOffset, parallaxOffset),
    settings.maxParallaxOffset)
  // 4
  attributes.parallax = CGAffineTransform(translationX: 0, y: boundedParallaxOffset)
}

下面进行细分:

  • 1) 计算单元格与集合视图中心center的距离。
  • 2) 在最大视差parallax值(在布局设置中设置)中按比例映射单元格与中心的距离
  • 3) 绑定parallaxOffset以避免视觉故障。
  • 4) 使用计算的视差parallax值创建CAAffineTransform转换。 最后,将translation分配给单元格的属性的transform属性。

为了实现对PlayerCell的视差效果,图像的frame应具有顶部和底部负间距。 在初始项目中,为您设置了这些约束。 您可以在Constraint检查器中查看它们(见下文)。

在构建之前,您必须修复一个最终细节。 打开JungleCupCollectionViewController.swift。 在setupCollectionViewLayout()内部更改以下值:

customLayout.settings.headerOverlayMaxAlphaValue = CGFloat(0)

为下面

customLayout.settings.headerOverlayMaxAlphaValue = CGFloat(0.6)

此值表示布局可以分配给headerView上的黑色叠加层的最大不透明度值。

构建并运行项目以欣赏所有视觉效果。 滚动吧! 滚动吧!

如果您想了解有关自定义UICollectionViewLayout的更多信息,请考虑阅读 Collection View Programming Guide for iOS中的Creating Custom Layouts部分,该部分详细介绍了此主题。

后记

本篇主要讲述了基于自定义UICollectionViewLayout布局的简单示例,感兴趣的给个赞或者关注~~~

相关文章

网友评论

    本文标题:UIKit框架(十六) —— 基于自定义UICollection

    本文链接:https://www.haomeiwen.com/subject/ifzqnqtx.html