2016-06-21 8 views
4

내 UICollectionView의 모든 오른쪽면 셀을 Apple의 메시지 앱과 비슷한 방식으로 스크롤하지만 콜렉션 뷰의 다른 셀의 색상이나 투명도에 영향을 미치지 않도록 페이드 아웃합니다. 해당 효과를 얻기 위해 스크롤 위치를 기반으로 UICollectionViewCell의 투명도를 조정하는 방법이 있습니까?collectionView가 스크롤 될 때 iOS collectionView의 특정 셀을 페이드 아웃 할 수 있습니까?

+1

그래디언트 효과 검색 시도 이것은 http : // stackoverflow에 도움이 될 수 있습니다.com/questions/22726103/ios-uitableview-fade-bottom-cell-and-cell-as-you-scroll –

+0

그라디언트 마스크를 사용하는 아이디어가 마음에 들지만 모든 내용이 scrollView의 내용에 영향을 미칠 것이라고 생각합니다. 올바른 세포 만이 아닙니다. – alionthego

+0

당신은 위 바닥 또는 둘 다 퇴색 하는가? – agibson007

답변

1

확실! UICollectionView는 UIScrollView의 하위 클래스이며 UICollectionViewController는 이미 컬렉션 뷰의 delegate임을 유의하십시오. 이것은 UIScrollViewDelegate 프로토콜을 준수한다는 것을 의미합니다. UIScrollViewDelegate 프로토콜에는 스크롤 위치 변경에 대해 알려주는 여러 메서드가 포함되어 있습니다.

저에게 가장 주목할만한 사람은 scrollViewDidScroll(_:)입니다. 콜렉션보기의 contentOffset이 변경되면이 아이콘이 호출됩니다. 이 메서드를 구현하여 콜렉션 뷰의 visibleCells을 반복 할 수 있습니다. 셀의 alpha을 직접 조정하거나 셀에 메시지를 보내 프레임 및 오프셋을 기반으로 자체 알파를 조정하도록 알릴 수 있습니다.

내가 구현할 수있는 가장 간단한 구현 방법은 오른쪽 측면 만 요구 사항을 존중하는 것입니다. 다음과 같습니다. 셀의 알파는 초기 dequeue 나 재사용이 아닌 스크롤에서만 조정되기 때문에 뷰의 상단 또는 하단 근처에 약간의 글리치가 나타날 수 있습니다.

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을 서브 클래 싱하는 경우 매우 간단합니다. (layoutAttributesForElements에

그런 : 당신이해야합니다 우선은 눈에 보이는 속성을 경계 변경/스크롤

shouldInvalidateLayout (CGRect forBoundsChange newBounds)에서 진정한 반환하여 발생했을 때 재 계산되어 있는지 확인하다 in : rect : CGRect) 대리자 호출, 수퍼 클래스에 의해 계산 된 속성을 가져 와서 가시 범위에있는 항목의 오프셋을 기준으로 알파 값을 수정합니다. 좌우측 항목을 구별하는 것은 컨트롤러에있는 모든 논리를 가지고 컨트롤러에서 처리 할 수 ​​있으며 레이아웃 클래스에 전달하여 왼쪽 항목에이 효과를 적용하지 않을 수 있습니다. (왼쪽 셀에 이상한 indexPath.row가있는 항목을 식별하는 컨트롤러에서 구현 된 'CustomLayoutDelegate '를 사용했습니다.)

다음은 홀수 인 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

감사합니다. 내가 누락 된 핵심 비트는 shouldInvalidateLayout에 대해 true를 반환했습니다. –

+0

내가 도와 드릴 수있어서 기쁩니다. 투표에 감사드립니다! – Lukas