ios开发那些事

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

2019-04-27  本文已影响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

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

打开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
}

以下是上面代码的作用:

在构建运行应用程序之前,将以下两个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教程的最后一部分中,您将添加以下视觉效果:

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

1. Affine Transforms

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

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

仿射变换背后的数学真的很酷。 但是,解释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
}

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

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

添加以下内容:

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)
    }
  }

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

现在是时候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)
}

下面进行细分:

为了实现对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布局的简单示例,感兴趣的给个赞或者关注~~~

上一篇下一篇

猜你喜欢

热点阅读