SpaceX
SpaceX

Reputation: 2890

Unable to tap a button in CollectionViewCell

I have a button embedded in a UICollectionViewCell with the below structure, when I try to tap button, neither the button UI changes nor the associated function gets called, this behaviour is seen in iOS 12 (iPhone 8) and not in iOS 10 (iPhone 5)

UITableView > UITableViewCell > UICollectionView > UICollectionViewCell > UIButton

The following screenshot shows the tableViewCell with collectionViewCells. enter image description here

Below is the code:

TableView class AppointmentTVC: UITableViewController {

var appointmentTimesCollectionTVCell    = AppointmentTimesCollectionTVCell()

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

            appointmentTimesCollectionTVCell.appointmentTimesToDisplay  = self.appointmentTimesToDisplay
            appointmentTimesCollectionTVCell.isUserInteractionEnabled   = true
            //appointmentTimesCollectionTVCell.selectionStyle             = .none
            return appointmentTimesCollectionTVCell
}

}

The below is the tableview cell that holds the collectionViewCells

class AppointmentTimesCollectionTVCell: UITableViewCell {

    var notificationCenter = NotificationCenter.default

    let collectionView:UICollectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout.init())

    var layout:UICollectionViewFlowLayout = UICollectionViewFlowLayout.init()

    let collectionViewCellId    =   "Cell"

    var cellDimensionsArray     =   [CGSize]()

    var appointmentTimes        =   [Date]()

    var indexOfSelectedItem : Int?



    var appointmentTimesToDisplay : [Date]{
        set(appointmentTimesToSet){   // This method is called whenever appointmentTimesToDisplay is set
            appointmentTimes = appointmentTimesToSet
            print("appointmentTimesBeingSet: \(appointmentTimes)")
            collectionView.reloadData() // This is used to reload images
        }
        get{
            return appointmentTimes
        }
    }


    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
    }

    override init(style: UITableViewCellStyle, reuseIdentifier: String!) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)



        // Configure CollectionView
        collectionView.register(ButtonCVCell.self, forCellWithReuseIdentifier: collectionViewCellId)

        collectionView.isScrollEnabled  = true  // To scroll collectionview inside cell

        layout.scrollDirection          = .horizontal

        layout.minimumInteritemSpacing  = CGFloat(10.0)  // The minimum spacing to use between items in the same row.
        layout.minimumLineSpacing       = CGFloat(10.0)       // The minimum spacing to use between lines of items in the grid.


        collectionView.setCollectionViewLayout(layout, animated: true)
        collectionView.backgroundColor = Colors.white

        collectionView.delegate     = self as UICollectionViewDelegate
        collectionView.dataSource   = self as UICollectionViewDataSource

        collectionView.translatesAutoresizingMaskIntoConstraints = false


        contentView.addSubview(collectionView)

        let viewsDict = [
            "collectionView"    : collectionView
            ] as [String : Any]

        // collectionView Constraints
        contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-5-[collectionView]-5-|", options: [], metrics: nil, views: viewsDict))
        contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-5-[collectionView]-5-|", options: [], metrics: nil, views: viewsDict))

    }

}


extension AppointmentTimesCollectionTVCell: UICollectionViewDelegateFlowLayout {

    // FOR SETTING FIXED SIZES
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

        // setting the standard widths of 80 and height 30.0 as default to ensure correct collection view cell size
        // These have been obtained by printing intrinsicContent size width in function cellforItem at
        if cellDimensionsArray.count == 0   { return CGSize(width:80.0, height: 30.0) }

        let _cellDimension = cellDimensionsArray[indexPath.item]

        return _cellDimension
    }


}



extension AppointmentTimesCollectionTVCell: UICollectionViewDataSource {
    // MARK: UICollectionViewDataSource

    func numberOfSections(in collectionView: UICollectionView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }


    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of items
        return appointmentTimes.count
    }


    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        let item = indexPath.item

        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: collectionViewCellId, for: indexPath) as? ButtonCVCell

        let appointmentTimeObject   =   appointmentTimes[item]
        let appointmentTimeString   = " " + DateFormatter().standardDateFormatter.string(from: appointmentTimeObject) + " "

        print("indexOfSelectedItem: \(indexOfSelectedItem), item: \(item)")

        // This is to ensure that only the selected item color stays and remainder are set to default
        if indexOfSelectedItem == item{
            cell?.button.backgroundColor    =   Colors.curieBlue
            cell?.button.layer.borderColor  =   Colors.curieBlue.cgColor
            cell?.button.setTitleColor(Colors.white, for: .normal)

        } else{
            cell?.button.layer.borderWidth      =   1
            cell?.button.layer.borderColor      =   Colors.lightGrey.cgColor
            cell?.button.backgroundColor       =   Colors.white
            cell?.button.setTitleColor(Colors.curieBlue, for: .normal)
        }

        cell?.button.tag                    =   item
        cell?.button.setTitle(appointmentTimeString , for: .normal)
        //cell?.button.addTarget(self, action: #selector(setSelectedTime(sender:)), for: .touchUpInside)
        cell?.button.addTarget(self, action: #selector(setSelectedTime(sender:)), for: .touchUpInside)
        print("buttonTagSetForTheButton: \(cell)")

        if let intrinsicContentSize    = cell?.button.intrinsicContentSize {
            //let intrinsicContentSize    = cell?.button.intrinsicContentSize
            let cellWidth   = intrinsicContentSize.width
            let cellHeight  = intrinsicContentSize.height
            let cellDimension = CGSize(width: cellWidth, height: cellHeight)

            if cellDimensionsArray.count <= appointmentTimes.count{ // Setting dimensions for new cells
                cellDimensionsArray.append(cellDimension)

            }else{  // Replacing dimensions for existing cells
                cellDimensionsArray[item] = cellDimension
            }
        }


        return cell ?? UICollectionViewCell()

        //return cell
    }


    func setSelectedTime(sender: UIButton){
        print("setSelectedTimeFuncCalled")
        let selectedTime            =   appointmentTimes[sender.tag]
        self.indexOfSelectedItem    =   sender.tag
        let timestamp               =   selectedTime.timeIntervalSince1970    // Get the Unix timestamp

        // Posting a local notification
        let selectedAppointmentTimeDict = ["selectedAppointmentTime" : Int(timestamp)]
        notificationCenter.post(name: Notification.Name(NotificationNames.appointmentTimeSelected), object: nil, userInfo: selectedAppointmentTimeDict)

        self.collectionView.reloadData()
    }


}

The below code is used to produce collection view cells with buttons.

class ButtonCVCell: UICollectionViewCell {

    let button : UIButton = {
        let btn                 = UIButton(type: .system)
        btn.tintColor           = Colors.curieBlue
        btn.frame               = CGRect(x: 0, y: 0, width: 20, height: 20)
        btn.layer.cornerRadius  = 5
        btn.sizeToFit()

        return btn
    }()

    var cellWidthConstraint : NSLayoutConstraint!   


    var cellWidth : CGFloat{       
        set(width){

            self.cellWidthConstraint.constant = width
            self.cellWidthConstraint.isActive = true

            print("SettingCellsWidth: \(width)")
        }
        get{
            return self.cellWidthConstraint.constant
        }
    }



    override func awakeFromNib() {
        super.awakeFromNib()

    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
        fatalError("init(coder:)")
    }

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

        self.contentView.translatesAutoresizingMaskIntoConstraints = false            

        self.cellWidthConstraint = NSLayoutConstraint(item: contentView, attribute: .width, relatedBy: .equal, toItem: nil, attribute:.notAnAttribute, multiplier: 0, constant: 0.0)

        button.translatesAutoresizingMaskIntoConstraints = false

        contentView.addSubview(button)


        let viewsDict = [
            "button"    :   button
            ] as [String : Any]

        // Button Constraints
        contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[button(30)]|", options: [], metrics: nil, views: viewsDict))

        contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[button]", options: [], metrics: nil, views: viewsDict))

    }

}

Upvotes: 0

Views: 979

Answers (2)

canister_exister
canister_exister

Reputation: 607

Each collection view cell is button. Use collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) instead

Upvotes: 1

Vicente Garcia
Vicente Garcia

Reputation: 6380

I think the problem is you are trying to use AutoLayout in the CollectionViewCell's ContentView, but CollectionViews use frames.

It is fine if you use AutoLayout for your Button inside the Cell, but the ContentView should resize by itself.

Try removing these lines:

// ButtonCVCell

var cellWidthConstraint : NSLayoutConstraint!   


var cellWidth : CGFloat{       
    set(width){

        self.cellWidthConstraint.constant = width
        self.cellWidthConstraint.isActive = true

        print("SettingCellsWidth: \(width)")
    }
    get{
        return self.cellWidthConstraint.constant
    }
}

And these:

// ButtonCVCell override init(frame: CGRect)

self.contentView.translatesAutoresizingMaskIntoConstraints = false            

self.cellWidthConstraint = NSLayoutConstraint(item: contentView, attribute: .width, relatedBy: .equal, toItem: nil, attribute:.notAnAttribute, multiplier: 0, constant: 0.0)

Upvotes: 0

Related Questions