conner.xyz
conner.xyz

Reputation: 7275

UICollectionView: Adding single Tap Gesture Recognizer to supplementary view

I have a UICollectionView with a supplementary view -- in essence a header for the collection. Whenever I add a gesture recognizer to a UILabel within the headerView.xib using the interface builder, the app crashes giving me

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'invalid nib registered for identifier (MY_HEADER) - nib must contain exactly one top level object which must be a UICollectionReusableView instance'

What is preventing me from adding a gesture recognizer to a UILabel within a supplementary view of a UICollectionView?

Upvotes: 11

Views: 11389

Answers (4)

AechoLiu
AechoLiu

Reputation: 18368

I add gesture recognizer when the supplementary view has been loaded from nib.

class MySuppleMentaryView: UICollectionReusableView
{

    @IBOutlet var label: UILabel!

    weak var delegate: MySuppleMentaryViewDelegate!

    override func awakeFromNib() {
        super.awakeFromNib()

        // NOTE: UILabel MUST enable user interaction to receive touch events.
        // label.isUserInteractionEnabled = true

        let tap = UITapGestureRecognizer(target: self, action: #selector(onClickLabel))
        tap.delaysTouchesBegan = true
        tap.numberOfTapsRequired = 1

        self.label.addGestureRecognizer(tap)
    }

    @objc func onClickLabel() {
        self.delegate.didOnLabel(cell: self)
    }
}

protocol MySuppleMentaryViewDelegate: NSObjectProtocol {
    func didOnLabel(cell: DCScheduleHourLabel)
}

// Configure supplementary cell
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {

    if (kind == UICollectionElementKindSectionHeader) { 
        // NOTE: The cell might be reused.
        // If gesture recognizer is added HERE, 
        // then maybe multiple gesture recognizers are added when reusing the cell. 
        let cell = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: DCScheduleDummyBlock.Identifier, for: indexPath) as! MySuppleMentaryView

        // configure cell
        cell.delegate = self
        return cell                
    }

    return UICollectionReusableView()
}     

Upvotes: 1

Carl Carlson
Carl Carlson

Reputation: 540

I too have not been able to add a gesture to a cell via IB.

However, my experience has been that using IB you CAN add a gesture recognizer to the collectionView itself by dragging one to the collectionView item in the outline view NOT to the scrollView that lies on top of the collectionView in the graphical representation.

So far I can only get a single tap through the cell and into the collectionView.

Upvotes: 1

conner.xyz
conner.xyz

Reputation: 7275

So it looks like you cannot use the interface builder to add a gesture recognizer to a supplementary view of a UICollectionView.

I believe this is because when the .xib's are loaded, the UICollectionView must appear as one thing to the superview -- and when you add the gesture recognizer to that UICollectionView you end up with two things at the superview level, which both correspond to the UICollectionView.

You can however implement your gesture recognizer programmatically using the definition of your supplementary view inside of the UICollectionViewReusableView Protocol. (The if is being used to distinguish between a header supplementary view and an footer supplementary view later in the code)

if (kind == UICollectionElementKindSectionHeader) {
    MyHeaderView *headerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"MY_HEADER" forIndexPath:indexPath];

    // call headerView methods (to put things into the header's UI objects for example)
    [headerView ...];
    [headerView ...];
    [headerView ...];

    // add gesture recognition for tapping on a UIlabel within the header (UICollectionView supplementary view)
    UITapGestureRecognizer *bioTap = [[UITapGestureRecognizer alloc] initWithTarget:headerView action:@selector(handleUILabelTap:)];
    // make your gesture recognizer priority
    bioTap.delaysTouchesBegan = YES;
    bioTap.numberOfTapsRequired = 1;
    [headerView.UILabelName addGestureRecognizer:UILabelTap];

    reusableview = headerView;
}

Upvotes: 20

pickwick
pickwick

Reputation: 3154

How about adding it programmatically after the nib's loaded? alternatively, in IB have you tried moving the location of the icon that represents the recognizer above or below that which represents the view

Upvotes: 0

Related Questions