简体   繁体   中英

How to have a view controller with two collection views, but only one has header/footer views?

I have two collection views in my view controller, which is set as the delegate and data source for both. One of those collection views has a registered supplementary view as its header, which it dequeues and displays properly before I added the second collection view. Now with the second collection view, the viewForSupplementaryElementOfKind causes an error since the second collection view doesn't have any registered headers. How can I ensure that the function gets called only on the first collection view?

Create a simple and short custom subclass for a UICollectionView like this:

class CustomCollectionView : UICollectionView {
    let showFooter : Bool
    let showHeader : Bool

    init(showHeader:Bool,showFooter:Bool) {
        self.showFooter = showFooter
        self.showHeader = showHeader
        //Create a custom UICollectionViewLayout and frame according to your requirement here (You can send parent ViewControllers frame as a param in the init above too)
        super.init(frame: CGRect.zero, collectionViewLayout: UICollectionViewLayout.init())
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Now just initialise the first CollectionView with showHeader and/or showFooter as true and other accordingly.You would now need to do the following in the delegate callback that asks for a supplementary view:

func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
    guard let collectionView = collectionView as? CustomCollectionView else {
        return UICollectionReusableView.init(frame: .zero)
    }

    switch kind {
    case UICollectionView.elementKindSectionFooter:
        if collectionView.showFooter {
                //Dequeue a footer and return
        }
    case UICollectionView.elementKindSectionHeader:
        if collectionView.showHeader {
                //Dequeue a header and return
        }
    default:
        return UICollectionReusableView.init(frame: .zero)
    }
    return UICollectionReusableView.init(frame: .zero)
}

The first parameter to the various data source and delegate methods is the collection view.

I do not recommend using view tags as the other poster suggested. That approach is fragile.

Instead, have your view controller keep pointers to each and then have the methods check which one is being called:

@IBOulet collectionView1: UICollectionView!
@IBOulet collectionView2: UICollectionView!

Then in your viewForSupplementaryElementOfKind method:

func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
    switch collectionView {
        case collectionView1:
            //code to return a view for collection view 1
        case collectionView2:
            //code to return a view for collection view 2
        default: 
            //Can't happen, but keep the compiler happy:
            return UIView(frame: CGRect.zeroRect)
    }
}

Alternately, set up separate objects to serve as the data sources of each collection view. Have your view controller create the data sources and hook them up in its viewDidLoad method.

If one collection view has a header and the other does not, you need to configure your layout objects separately for each collection view. (You need to have code like the above that handles the two collection views separately for ALL of your collection view methods.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM