Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.3k views
in Technique[技术] by (71.8m points)

swift - UICollectionView CompositionalLayout not calling UIScrollDelegate

Here is my problem:

1. ScrollViewDelegate does not get called once UICollectionView implements compositionalLayout.

With flowLayout and UICollectionViewDataSource the scrollView delegates get called. Once I implement the diffable datasource and CompositionalLayout the scrollView delegates don't get called anymore.

2. collectionView.decelerationRate = .fast gets ignored when implementing CompositionalLayout

My understanding is that UICollectionViewDelegate should call UIScrollViewDelegate, I have looked wide and far but no luck. Can someone point me out what am I missing? Am I integrating compositionalLayout wrong?

here is my code:



import UIKit

class ViewController: UIViewController, UICollectionViewDelegate, UIScrollViewDelegate {
    
    enum Section {
        case weekdayHeader
    }
    
    @IBOutlet weak var collectionView: UICollectionView!
    
    let weekdays = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday","Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
    
    var weekdaysArr = [Weekdays]()
    
    var dataSource: UICollectionViewDiffableDataSource<Section, Weekdays>! = nil
    
    private let cellReuseIdentifier = "myCell"
    
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.collectionView.delegate = self
        self.collectionView.register(UINib(nibName: "MyCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: cellReuseIdentifier)
        self.collectionView.decelerationRate = .normal
        
        configureCollectionView()
        configureDataSource()
        configureWeekdays()
        updateCollectionView()
        
        // Do any additional setup after loading the view.
    }
    
    func configureCollectionView(){
        self.collectionView.delegate = self
        self.collectionView.collectionViewLayout = generateLayout()
    }
    
    func generateLayout() -> UICollectionViewLayout {
        
        let itemSize = NSCollectionLayoutSize(
            widthDimension: .estimated(10),
            heightDimension: .fractionalHeight(1))
        let weekdayItem = NSCollectionLayoutItem(layoutSize: itemSize)
        //        weekdayItem.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 50, bottom: 5, trailing: 50)
        
        let groupSize = NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(0.3),
            heightDimension: .fractionalHeight(1.0))
        let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [weekdayItem])
        
        //        let spacing = CGFloat(10)
        //        group.interItemSpacing = .fixed(spacing)
        
        let section = NSCollectionLayoutSection(group: group)
        section.interGroupSpacing = CGFloat(20)
        section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 500)
        section.orthogonalScrollingBehavior = .continuousGroupLeadingBoundary
        
        
        
        
        let layout = UICollectionViewCompositionalLayout(section: section)
        
        return layout
        
    }
    
    func configureDataSource() {
        dataSource = UICollectionViewDiffableDataSource
            <Section, Weekdays>(collectionView: collectionView)
            { (collectionView: UICollectionView, indexPath: IndexPath, weekday: Weekdays) -> UICollectionViewCell? in
                guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: self.cellReuseIdentifier, for: indexPath) as? MyCollectionViewCell else {
                    fatalError("Cannot create a new cell") }
                cell.titleLabel.text = weekday.name
                print("weekday.name = (weekday.name)")
                return cell
        }
        //      let snapshot = snapshotForCurrentState()
        //      dataSource.apply(snapshot, animatingDifferences: false)
    }
    
    func configureWeekdays(){
        
        weekdaysArr.append(Weekdays(name: "Monday"))
        weekdaysArr.append(Weekdays(name: "Tuesday"))
        weekdaysArr.append(Weekdays(name: "Wednesday"))
        weekdaysArr.append(Weekdays(name: "Thursday"))
        weekdaysArr.append(Weekdays(name: "Friday"))
        weekdaysArr.append(Weekdays(name: "Saturday"))
        weekdaysArr.append(Weekdays(name: "Sunday"))
        
    }
    
    func updateCollectionView(){
        var snapshot = NSDiffableDataSourceSnapshot<Section, Weekdays>()
        snapshot.appendSections([.weekdayHeader])
        snapshot.appendItems(weekdaysArr, toSection: .weekdayHeader)
        dataSource.apply(snapshot, animatingDifferences: false)
    }
    
    
    
    //MARK: Scroll View Delegate
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        print("ScrollView decel rate: (scrollView.decelerationRate)")
    }
    
    public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        print("Begin")
    }
    
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        print("End")
    }
    
    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        print("END")
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
          
          print("Did select a cell here")
          
         }
    
    
}

struct Weekdays: Hashable {
    let identifier: UUID = UUID()
    let name: String
    
    func hash(into hasher: inout Hasher){
        return hasher.combine(identifier)
    }
    
    static func == (lhs: Weekdays, rhs: Weekdays) -> Bool {
        return lhs.identifier == rhs.identifier
    }
}


Thank you

Edit: Updated Code -

Edit: Comparing against Apples sample code from WWDC I found out this behaviour.

If I use the sample codes Grid layout

func gridLayout() -> UICollectionViewLayout {
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.2),
                                             heightDimension: .fractionalHeight(1.0))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)

        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                              heightDimension: .fractionalWidth(0.2))
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize,
                                                         subitems: [item])

        let section = NSCollectionLayoutSection(group: group)

        let layout = UICollectionViewCompositionalLayout(section: section)
        return layout
    }

When having only 7 Items the Scroll Delegate methods never get called.

I can still scroll though and the expected behavior should be that the scroll delegate methods do get called as I have vertical bounce.

When having about 40 items though and the content is obviously beyond the screen size the scroll delegate does get called.

Now interestingly when loading my own layout:

func generateLayout() -> UICollectionViewLayout {
        
        let itemSize = NSCollectionLayoutSize(
            widthDimension: .estimated(10),
            heightDimension: .fractionalHeight(1))
        let weekdayItem = NSCollectionLayoutItem(layoutSize: itemSize)
        //        weekdayItem.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 50, bottom: 5, trailing: 50)
        
        let groupSize = NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(0.3),
            heightDimension: .fractionalHeight(1.0))
        let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [weekdayItem])
        
        //        let spacing = CGFloat(10)
        //        group.interItemSpacing = .fixed(spacing)
        
        let section = NSCollectionLayoutSection(group: group)
        section.interGroupSpacing = CGFloat(20)
        section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 500)
        section.orthogonalScrollingBehavior = .continuousGroupLeadingBoundary
        
        
        
        
        let layout = UICollectionViewCompositionalLayout(section: section)
        
        return layout
        
    }

The Scroll delegates never get called. Even with 40+ Items.

Any ideas as to how to force UICollectionView to communicate with its ScrollViewDelegates?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The answer is using the visibleItemsInvalidationHandler closure on the section (NSCollectionLayoutSection).

This handler receives updates whenever an event results in an animation. It operates similar to scrollViewDidScroll and on horizontal scrolling groups it will be passed all the items, the scroll view offset and the NSCollectionLayoutEnvironment.

https://developer.apple.com/documentation/uikit/nscollectionlayoutsection/3199096-visibleitemsinvalidationhandler

Example:

section.visibleItemsInvalidationHandler = { [weak self] visibleItems, point, environment in
    self?.currentIndex = visibleItems.last?.indexPath.row
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...