2016-06-21 70 views
4

我想讓我的UICollectionView的右側單元格淡出,因爲它們的滾動類似於Apple的消息應用程序,但不會影響collectionView中其他單元格的顏色或透明度。有沒有辦法根據它的滾動位置調整UICollectionViewCell的透明度以達到這種效果?我怎樣才能使一個iOS collectionView的特定單元格作爲collectionView滾動而淡出?

+1

嘗試搜索漸變效果 這可能有助於http:// stackoverflow。com/questions/22726103/ios-uitableview-fade-bottom-cell-and-top-cell-as-you-scroll –

+0

我喜歡使用漸變遮罩的想法,但我相信會影響scrollView的所有內容而不僅僅是正確的細胞。 – alionthego

+0

你黯淡的底部還是兩者? – agibson007

回答

1

當然!請注意,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 
      } 
     } 
    } 

} 
3

你可以做很多有趣的東西收集意見。我喜歡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) 

我可以告訴你如何修改它,如果我知道用例更好一點。

2

這很簡單,如果你繼承UICollectionViewFlowLayout。你需要做的第一件事是確保當界限變化/滾動發生在

shouldInvalidateLayout(forBoundsChange newBounds:的CGRect)返回true可見屬性被重新計算layoutAttributesForElements

然後(直接: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 
    } 

} 
+0

謝謝!我失蹤的關鍵是返回true for shouldInvalidateLayout –

+0

我很高興我可以提供幫助。感謝您的投票! – Lukas

相關問題