Swift 3- How to get button in UICollectionViewCell work

若如初见. 提交于 2019-12-30 05:08:09

问题


I am trying to implement an Edit button inside a cell.

Please refer to image:

What I done so far:

MainController:

class MainController: UICollectionViewController, UICollectionViewDelegateFlowLayout  {
  let imgCellId = "imgCellId"

  override func viewDidLoad() {
    collectionView?.backgroundColor = .white
    collectionView?.register(ImgItemCell.self, forCellWithReuseIdentifier: imgCellId)
  }

  override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {   
      let cell = collectionView.dequeueReusableCell(withReuseIdentifier: imgCellId, for: indexPath) as! ImgItemCell
      cell.editButton.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)

      return cell
  }

  func buttonPressed(){
    print("buttonPressed !")
  }

}

ImgItemCell:

import Material

class ImgItemCell: UICollectionViewCell{

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

       setupViews()
   }
   ...
   let editButton: RaisedButton = {
       let button = RaisedButton(title: "Edit", titleColor: .black) return button
   }()

  func setupViews(){
     ...
     addSubview(editButton)
     ...
  }

}

Result: The button is not clickable. No log is printed when clicking on the button.

In android, I have done this by OnClickListener of button to perform action for each row. How can I do the same in Swift 3?

Solution: (it's working for me)

Hi all thank you for all suggestions, they’re more less the hint for me to come to the solution.

The root cause of my problem is view hierarchy (as @DatForis pointed out)

Explanation: I want a cell contains image and a layout of buttons so that I had view hierarchy as below

override func setupViews() {
    super.setupViews()

    addSubview(imgView)
    addSubview(buttonLayout)
    buttonLayout.addSubView(buttonList)
            buttonList.addSubview(editButton)
            buttonList.addSubview(shareButton)
  }

this hierarchy somehow blocked the click event of button.

Therefore, I changed a bit in hierarchy

  override func setupViews() {
    super.setupViews()

    addSubview(imgView)
    addSubview(buttonLayout)
            buttonLayout.addSubview(editButton)
            buttonLayout.addSubview(shareButton)
   }

and BAM ! it worked like a charm.

In fact, I need a proper explanation about why the hierarchy impact to children view.

By the way, I think most replies here are workable solution, but I selected @DonMag as final answer, because it’s clean and clear with a cool callback to Controller.

But again, my root problem is from view hierarchy.


回答1:


A very reliable and flexible pattern is to assign a "Callback Closure" to your cell. Put your button action handler inside the cell, and have it "call back" to the view controller.

Here is a basic example (you should be able to implement it with your custom cell with no problem):

//
//  CViewWithButtonCollectionViewController.swift
//  SWTemp2
//
//  Created by Don Mag on 6/5/17.
//  Copyright © 2017 DonMag. All rights reserved.
//

import UIKit

private let reuseIdentifier = "ImgItemCell"

class ImgItemCell: UICollectionViewCell {

    // this will be our "call back" action
    var btnTapAction : (()->())?

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

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupViews()
    }

    let editButton: UIButton = {
        let button = UIButton(type: UIButtonType.system)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.backgroundColor = .white
        button.setTitle("Edit", for: .normal)
        return button
    }()

    func setupViews(){

        // add a button
        addSubview(editButton)

        editButton.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
        editButton.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true

        // add the touchUpInside target
        editButton.addTarget(self, action: #selector(btnTapped), for: .touchUpInside)

    }

    @objc func btnTapped() {
        print("Tapped!")

        // use our "call back" action to tell the controller the button was tapped
        btnTapAction?()
    }

}

class CViewWithButtonCollectionViewController: UICollectionViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        if let layout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
            layout.itemSize = CGSize(width: 300, height: 100)
        }

    }

    override func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 10
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! ImgItemCell

        cell.backgroundColor = .red

        // set a "Callback Closure" in the cell
        cell.btnTapAction = {
            () in
            print("Edit tapped in cell", indexPath)
            // start your edit process here...
        }

        return cell
    }

}



回答2:


You might want to use a tag for a simpler approach, but I always implement a delegate pattern in the case of buttons inside cells

protocol MyCollectionViewCellDelegate: class {
    func button(wasPressedOnCell cell: MyCollectionViewCell)
}
class MyCollectionViewCell: UICollectionViewCell {
    weak var delegate: MyCollectionViewCellDelegate?
    var data: String = "DATA"
    @IBAction func buttonWasPressed(sender: UIButton){
        delegate?.button(wasPressedOnCell: self)
    }

}
class MainViewController: UIViewController, UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "reuse", for: indexPath) as! MyCollectionViewCell
        cell.delegate = self
        return cell
    }
}
extension MainViewController: MyCollectionViewCellDelegate{
    func button(wasPressedOnCell cell: MyCollectionViewCell) {
        //do what you want with the cell and data
    }
}

Using this method will allow you to have multiple buttons inside a cell. Use a different delegate method for each button




回答3:


I have created the same scenario. The only difference is that I have used UIButton instead of RaisedButton. And it is working perfectly fine.

1.ImgItemCell

class ImgItemCell: UICollectionViewCell
{
    //MARK: View Lifecycle Methods
    override func awakeFromNib()
    {
        super.awakeFromNib()
        setupViews()
    }

    let editButton: UIButton = {
        let button = UIButton(frame: CGRect(x: 0, y: 0, width: 30, height: 50))
        button.setTitle("Edit", for: .normal)
        return button
    }()

    func setupViews()
    {
        addSubview(editButton)
    }
}

2.MainController methods

//MARK: UICollectionViewDataSource
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
    return 10
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: imgCellId, for: indexPath) as! ImgItemCell
    cell.editButton.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
    return cell
}

@objc func buttonPressed()
{
    print("buttonPressed !")
}



回答4:


How your buttonpress method will know,you are selecting which cell button.So you can differentiate with tag
Add in cellForItemAtindexPath

ButtonObject.tag = indexPath.item

and

func buttonPressed(_ sender: UIButton)
    {
        print("buttonPressed ! \(sender.tag)")
    }



回答5:


func setupViews() {
    ...
    addSubview(editButton)
    editButton.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
}

func buttonPressed(sender:UIButton){
    print("buttonPressed !")
}


来源:https://stackoverflow.com/questions/44368515/swift-3-how-to-get-button-in-uicollectionviewcell-work

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!