我想让我的UICollectionView的所有右侧单元格淡出,因为它们滚动类似于Apple的消息应用程序但不影响collectionView中其他单元格的颜色或透明度。有没有办法根据它的滚动位置调整UICollectionViewCell的透明度来实现这种效果?
答案 0 :(得分:4)
你可以为收藏视图做很多有趣的事情。我喜欢子类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)
如果我更了解用例,我可以告诉你如何修改它。
答案 1 :(得分:2)
如果您继承UICollectionViewFlowLayout,这很简单。您需要做的第一件事是确保在边界更改/滚动发生时通过返回true来重新计算可见属性
shouldInvalidateLayout(forBoundsChange newBounds:CGRect)
然后在 layoutAttributesForElements(在rect:CGRect中)委托调用,获取超类计算的属性,并根据可见边界中项目的偏移量修改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
}
}
答案 2 :(得分:1)
当然!请注意,UICollectionView是UIScrollView的子类,并且您的UICollectionViewController已经是集合视图的delegate
。这意味着它也符合UIScrollViewDelegate协议,该协议包含一系列方法来通知您滚动位置的变化。
最值得注意的是scrollViewDidScroll(_:)
,当集合视图中的contentOffset
发生变化时,将会调用该visibleCells
。您可以实现该方法来迭代集合视图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
}
}
}
}
或向单元格发送一些消息以通知它调整自己的alpha基于其框架和偏移量。
我能想出的最简单的实现方式 - 尊重您的右侧要求 - 如下所示。请注意,这可能会在视图的顶部或底部附近出现一些小故障,因为单元格的alpha仅在滚动时调整,而不是在初始出列或重新使用时调整。
Post ID