我有一个集合视图,我希望它有两种状态:折叠和展开。
以下是两种状态:
集合视图的背景为红色,单元格为紫色背景,集合视图的包含视图为灰色背景。
我正在使用UICollectionViewFlowLayout的自定义子类来处理单元格的分页,缩放和不透明度。布局应该确保其中一个单元始终居中。
两个状态之间的平滑动画,其中单元格通过集合视图简单地增长/缩小。
由于单元格的宽度在两种状态之间变化,我不仅会看到单元格之间的默认淡入淡出动画,而且还会看到以一种状态为中心的单元格不再居中。
我尝试按照UPCarouselLayout进行初始布局。我尝试了this objc.io post关于动画的想法,特别是有关设备轮换的部分。我无法使用他们的方法,因为layoutAttributesForElement(at indexPath: IndexPath)
返回的帧不正确。
protocol TrayCarouselFlowLayoutDelegate: class {
func sizeForItemInCarousel(_ collectionView: UICollectionView, _ layout: TrayCarouselFlowLayout) -> CGSize
}
class TrayCarouselFlowLayout: UICollectionViewFlowLayout {
fileprivate var collectionViewSize: CGSize = .zero
fileprivate var peekItemScale: CGFloat = 0.95
fileprivate var peekItemAlpha: CGFloat = 0.65
fileprivate var peekItemShift: CGFloat = 0.0
var spacing = 16
weak var delegate: TrayCarouselFlowLayoutDelegate!
fileprivate var indexPathsToAnimate: [IndexPath] = []
override func prepare() {
super.prepare()
guard let size = collectionView?.bounds.size, collectionViewSize != size else { return }
setUpCollectionView()
updateLayout()
collectionViewSize = size
}
fileprivate func setUpCollectionView() {
guard let collectionView = collectionView else { return }
if collectionView.decelerationRate != UIScrollViewDecelerationRateFast {
collectionView.decelerationRate = UIScrollViewDecelerationRateFast
}
}
fileprivate func updateLayout() {
guard let collectionView = collectionView else { return }
itemSize = delegate.sizeForItemInCarousel(collectionView, self)
let yInset = (collectionView.bounds.height - itemSize.height) / 2
let xInset = (collectionView.bounds.width - itemSize.width) / 2
sectionInset = UIEdgeInsets(top: yInset, left: xInset, bottom: yInset, right: xInset)
let side = itemSize.width
let scaledItemOffset = (side - side * peekItemScale) / 2
minimumLineSpacing = spacing - scaledItemOffset
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
let oldBounds = collectionView?.bounds ?? .zero
if oldBounds != newBounds {
return true
}
return false
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard let superAttributes = super.layoutAttributesForElements(in: rect),
let attributes = NSArray(array: superAttributes, copyItems: true) as? [UICollectionViewLayoutAttributes]
else { return nil }
return attributes.map { self.transformLayoutAttributes($0) }
}
fileprivate func transformLayoutAttributes(_ attributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
guard let collectionView = collectionView else { return attributes }
let collectionCenter = collectionView.frame.size.width / 2
let offset = collectionView.contentOffset.x
let normalizedCenter = attributes.center.x - offset
let maxDistance = itemSize.width + minimumLineSpacing
let distance = min(abs(collectionCenter - normalizedCenter), maxDistance)
let ratio = (maxDistance - distance) / maxDistance
let alpha = ratio * (1 - peekItemAlpha) + peekItemAlpha
let scale = ratio * (1 - peekItemScale) + peekItemScale
let shift = (1 - ratio) * peekItemShift
attributes.alpha = alpha
attributes.transform3D = CATransform3DScale(CATransform3DIdentity, scale, scale, 1)
attributes.zIndex = Int(alpha * 10)
attributes.center.y += shift
return attributes
}
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint,
withScrollingVelocity velocity: CGPoint) -> CGPoint {
guard let collectionView = collectionView, !collectionView.isPagingEnabled,
let layoutAttributes = self.layoutAttributesForElements(in: collectionView.bounds)
else { return super.targetContentOffset(forProposedContentOffset: proposedContentOffset) }
let midSide: CGFloat = collectionViewSize.width / 2
let proposedContentOffsetCenterOrigin = proposedContentOffset.x + midSide
let closest = layoutAttributes
.sorted { abs($0.center.x - proposedContentOffsetCenterOrigin) < abs($1.center.x - proposedContentOffsetCenterOrigin) }
.first ?? UICollectionViewLayoutAttributes()
let targetContentOffset = CGPoint(x: floor(closest.center.x - midSide), y: proposedContentOffset.y)
return targetContentOffset
}
}
我的目标是简单地增加集合视图和单元格。由于我在代码中更改了部分插入和单元格的大小,因此最具挑战性的部分是在生长/收缩期间保持细胞居中。任何帮助将不胜感激!
编辑:如果在展开/折叠时我没有更改单元格的宽度,则表明我的行为几乎是我想要的。仍然有一个淡入淡出的动画,但如果它是唯一的标记,那将是可以接受的。出于某种原因,在布局单元格增长时重新定义宽度是导致GIF中出现问题的原因。
这是宽度为静态的GIF: