当collectionView滚动时,如何使iOS collectionView的特定单元格淡出?
Posted
技术标签:
【中文标题】当collectionView滚动时,如何使iOS collectionView的特定单元格淡出?【英文标题】:How can I make a particular cell of an iOS collectionView fade out as the collectionView scrolls? 【发布时间】:2016-10-22 19:39:00 【问题描述】:我想让 UICollectionView 的所有右侧单元格在滚动时淡出,类似于 Apple 的消息应用程序,但不影响 collectionView 中其他单元格的颜色或透明度。有没有办法根据 UICollectionViewCell 的滚动位置调整其透明度以实现该效果?
【问题讨论】:
尝试搜索渐变效果这可能会有所帮助***.com/questions/22726103/… 我喜欢使用渐变蒙版的想法,但我相信这会影响 scrollView 的所有内容,而不仅仅是正确的单元格。 你是从上往下掉还是两者兼而有之? 【参考方案1】:如果你继承 UICollectionViewFlowLayout,这很简单。您需要做的第一件事是确保在发生边界更改/滚动时重新计算可见属性,方法是在
中返回 trueshouldInvalidateLayout(forBoundsChange newBounds: CGRect)
然后在layoutAttributesForElements(in rect: CGRect)委托调用中,获取超类计算出来的属性,根据item在可见范围内的偏移量修改alpha值,就这样。 左侧/右侧项目之间的区别可以在控制器中使用您拥有的任何逻辑进行处理,并传达给布局类以避免将此效果应用于左侧项目。 (我使用 'CustomLayoutDelegate' 来实现在控制器中实现的功能,它只是将带有奇数 indexPath.row 的项目识别为左侧单元格)
这是一个演示,它将此效果应用于具有偶数 indexPath.row 跳过奇数行的项目
import UIKit
class ViewController: UIViewController
/// Custom flow layout
lazy var layout: CustomFlowLayout =
let l: CustomFlowLayout = CustomFlowLayout()
l.itemSize = CGSize(width: self.view.bounds.width / 1.5, height: 100)
l.delegate = self
return l
()
/// The collectionView if you're not using UICollectionViewController
lazy var collectionView: UICollectionView =
let cv: UICollectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: self.layout)
cv.backgroundColor = UIColor.lightGray
cv.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
cv.dataSource = self
return cv
()
override func viewDidLoad()
super.viewDidLoad()
view.addSubview(collectionView)
extension ViewController: UICollectionViewDataSource, CustomLayoutDelegate
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
return 30
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
cell.backgroundColor = UIColor.black
return cell
// MARK: CustomLayoutDelegate
func cellSide(forIndexPath indexPath: IndexPath) -> CellSide
// TODO: Your implementation to distinguish left/right indexPath
// Even rows are .right and Odds .left
if indexPath.row % 2 == 0
return .right
else
return .left
public enum CellSide
case right
case left
protocol CustomLayoutDelegate: class
func cellSide(forIndexPath indexPath: IndexPath) -> CellSide
class CustomFlowLayout: UICollectionViewFlowLayout
/// Delegates distinguishing between left and right items
weak var delegate: CustomLayoutDelegate!
/// Maximum alpha value
let kMaxAlpha: CGFloat = 1
/// Minimum alpha value. The alpha value you want the first visible item to have
let kMinAlpha: CGFloat = 0.3
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
guard let cv = collectionView, let rectAtts = super.layoutAttributesForElements(in: rect) else return nil
for atts in rectAtts
// Skip left sides
if delegate.cellSide(forIndexPath: atts.indexPath) == .left
continue
// Offset Y on visible bounds. you can use
// ´cv.bounds.height - (atts.frame.origin.y - cv.contentOffset.y)´
// To reverse the effect
let offset_y = (atts.frame.origin.y - cv.contentOffset.y)
let alpha = offset_y * kMaxAlpha / cv.bounds.height
atts.alpha = alpha + kMinAlpha
return rectAtts
// Invalidate layout when scroll happens. Otherwise atts won't be recalculated
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool
return true
【讨论】:
谢谢!我缺少的关键位是为 shouldInvalidateLayout 返回 true【参考方案2】:您可以对集合视图做很多有趣的事情。我喜欢继承 UICollectionViewFlowLayout。这是一个示例,它根据与中心的距离淡化集合视图的顶部和底部。我可以将其修改为仅淡化边缘,但您应该在查看代码后弄清楚。
import UIKit
class FadingLayout: UICollectionViewFlowLayout,UICollectionViewDelegateFlowLayout
//should be 0<fade<1
private let fadeFactor: CGFloat = 0.5
private let cellHeight : CGFloat = 60.0
required init?(coder aDecoder: NSCoder)
super.init(coder: aDecoder)
init(scrollDirection:UICollectionViewScrollDirection)
super.init()
self.scrollDirection = scrollDirection
override func prepare()
setupLayout()
super.prepare()
func setupLayout()
self.itemSize = CGSize(width: self.collectionView!.bounds.size.width,height:cellHeight)
self.minimumLineSpacing = 0
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool
return true
func scrollDirectionOver() -> UICollectionViewScrollDirection
return UICollectionViewScrollDirection.vertical
//this will fade both top and bottom but can be adjusted
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
let attributesSuper: [UICollectionViewLayoutAttributes] = super.layoutAttributesForElements(in: rect) as [UICollectionViewLayoutAttributes]!
if let attributes = NSArray(array: attributesSuper, copyItems: true) as? [UICollectionViewLayoutAttributes]
var visibleRect = CGRect()
visibleRect.origin = collectionView!.contentOffset
visibleRect.size = collectionView!.bounds.size
for attrs in attributes
if attrs.frame.intersects(rect)
let distance = visibleRect.midY - attrs.center.y
let normalizedDistance = abs(distance) / (visibleRect.height * fadeFactor)
let fade = 1 - normalizedDistance
attrs.alpha = fade
return attributes
else
return nil
//appear and disappear at 0
override func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes?
let attributes = super.layoutAttributesForItem(at: itemIndexPath)! as UICollectionViewLayoutAttributes
attributes.alpha = 0
return attributes
override func finalLayoutAttributesForDisappearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes?
let attributes = super.layoutAttributesForItem(at: itemIndexPath)! as UICollectionViewLayoutAttributes
attributes.alpha = 0
return attributes
在你的控制器中的集合视图设置中,它看起来像这样。
let layout = FadingLayout(scrollDirection: .vertical)
collectionView.delegate = self
collectionView.dataSource = self
self.collectionView.setCollectionViewLayout(layout, animated: false)
如果我更了解用例,我可以告诉你如何修改它。
【讨论】:
只有当用户开始滚动时才能淡出?另外,当用户到达滚动视图的末尾时,底部单元格不会褪色?【参考方案3】:当然!请注意,UICollectionView 是 UIScrollView 的子类,并且您的 UICollectionViewController 已经是集合视图的delegate
。这意味着它也符合 UIScrollViewDelegate 协议,该协议包含一组通知您滚动位置变化的方法。
对我来说最值得注意的是scrollViewDidScroll(_:)
,当集合视图中的contentOffset
发生变化时会调用它。您可以实现该方法来迭代集合视图的visibleCells
,或者自己调整单元格的alpha
,或者向单元格发送一些消息以通知它根据其框架和偏移量调整自己的alpha。
我能想出的最简单的实现方式(尊重您的仅右侧要求)如下所示。请注意,这可能会在视图顶部或底部附近出现一些故障,因为单元格的 alpha 仅在滚动时调整,而不是在初始出列或重用时调整。
class FadingCollectionViewController: UICollectionViewController
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
return 500
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
return cell
override func scrollViewDidScroll(_ scrollView: UIScrollView)
guard let collectionView = collectionView else
return
let offset = collectionView.contentOffset.y
let height = collectionView.frame.size.height
let width = collectionView.frame.size.width
for cell in collectionView.visibleCells
let left = cell.frame.origin.x
if left >= width / 2
let top = cell.frame.origin.y
let alpha = (top - offset) / height
cell.alpha = alpha
else
cell.alpha = 1
【讨论】:
以上是关于当collectionView滚动时,如何使iOS collectionView的特定单元格淡出?的主要内容,如果未能解决你的问题,请参考以下文章
如何在ios objective-c 应用程序中使屏幕的全部内容滚动
CollectionViewCell 顶部的按钮使 collectionView 停止滚动
当scrollview快速滚动时如何滚动collectionView?
ios collectionview 怎么使cell滚动到指定的位置