版本记录
版本号 | 时间 |
---|---|
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布局的简单示例,感兴趣的给个赞或者关注~~~