Search code examples
iosswiftuicollectionviewuinavigationcontroller

UICollectionView in navigation title view is unresponsive to selection


I have a UICollectionView inside the UINavigationItem's title view acting as a menu. I am unable to select items within the collection view. If I place the UICollectionView outside of the navigation item and into my view controller, everything works as intended.

UIViewController

let menuView = HomeMenuView()

override func viewDidLoad() {
    navigationItem.titleView = menuView
    menuView.delegate = self
}

HomeMenuView (UICollectionView)

class HomeMenuView: UIView, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {

var sections = ["Recent", "Following"]

var delegate: HomeMenuDelegate!
var selectedIndex = 0

var collectionView: UICollectionView!

override init(frame: CGRect) {
    super.init(frame: frame)
    create()
}

func create() {
    let layout = UICollectionViewFlowLayout()
    layout.scrollDirection = .horizontal
    layout.minimumLineSpacing = 0
    layout.minimumInteritemSpacing = 0
    layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
    addSubview(collectionView)
    collectionView.anchor(top: nil, left: nil, bottom: nil, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: UIScreen.main.bounds.width/2, height: 30)
    collectionView.center(x: centerXAnchor, y: centerYAnchor)
    collectionView.backgroundColor = .clear
    collectionView.isUserInteractionEnabled = true
    collectionView.clipsToBounds = true
    collectionView.allowsSelection = true
    collectionView.delegate = self
    collectionView.dataSource = self
    collectionView.register(HomeMenuCell.self, forCellWithReuseIdentifier: "cellID")
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return sections.count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellID", for: indexPath) as! HomeMenuCell
    cell.title = sections[indexPath.row]
    cell.isSelected = selectedIndex == indexPath.row
    return cell
}

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    guard let currentCell = collectionView.cellForItem(at: IndexPath(row: selectedIndex, section: 0)) as? HomeMenuCell else { return }
    guard let newCell = collectionView.cellForItem(at: indexPath) as? HomeMenuCell else { return }
    currentCell.isSelected = false
    newCell.isSelected = true
    selectedIndex = indexPath.row
    delegate.didSelectSection(at: indexPath.row)
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return CGSize(width: (collectionView.frame.width)/CGFloat(sections.count), height: collectionView.frame.height)
}

override func layoutSubviews() {
    super.layoutSubviews()
    collectionView.layer.cornerRadius = collectionView.bounds.height/2
}

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

Solution

  • Your collection view cells cannot be selected because your collection view is outside the bounds of its super view.

    Change the middle part of your create() func as follows (you didn't include your "constraint helpers" so I used standard constraint syntax):

        addSubview(collectionView)
        
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
    
            collectionView.topAnchor.constraint(equalTo: topAnchor, constant: 0.0),
            collectionView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),
            collectionView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),
            collectionView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0.0),
    
            collectionView.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width * 0.5),
            collectionView.heightAnchor.constraint(equalToConstant: 30.0),
        ])
        
        //collectionView.anchor(top: nil, left: nil, bottom: nil, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: UIScreen.main.bounds.width/2, height: 30)
        //collectionView.center(x: centerXAnchor, y: centerYAnchor)
        
        collectionView.backgroundColor = .clear