Before you rip my head off and set it on fire, I know questions like this have already been asked but they all refer to single buttons. I have a collectionView. I'm asking this question after carefully reading all the other questions and trying to combine their solutions together.
I'm creating a keyboard extension with a collectionview. In my collectionView I have custom cells and in their class I placed a button. I want to add a target to each button in the collection view. I know I could use the selectItemAtIndexRow function and ignore the buttoms but I need to handle the touchUpInside and touchDown events (because when I type with y keyboard it's super slow) and I haven't found a way to do it with collectionView cells (if something exists let me know). In order to escape this problem, I thought the best solution could have been adding a button to my cell's class and add the actions I wanted to it, but I found multiple prooblems doing so.
I have the classic keyboardViewController you get by creating a new keyboard target where I placed a view (which is the view containing the collectionView).
I have a custom view class which contains the collection view
I have a custom collectionview cell class
Here I programmatically created my collectionView.
//Closure to add action to my buttons
var insertLowercase : ((IndexPath) -> ())?
let letters = ["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"]
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
letters.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = keyView.dequeueReusableCell(withReuseIdentifier: "collectionCellId", for: indexPath) as! KeyboardKeys
cell.button.setTitle(letters[indexPath.row], for: .normal)
cell.button.addTarget(self, action: #selector(lettersKeyboard.insert(indexPath:)), for: .touchUpInside)
return cell
}
@objc func insert (indexPath: IndexPath){
insertLowercase?(indexPath)
}
This is how I add the target. However if I press the button, the keyboard crashes giving me the following error:
Thread 1: "-[Keyboarddd.KeyboardButton length]: unrecognized selector sent to instance 0x7f9bb2506740"
After putting here my custom view, in the viewDidLoad I call this:
letters.insertLowercase = { indexPath in
let text = self.letters.letters[indexPath.row]
self.textDocumentProxy.insertText(text)
} //And more stuff to handle quick insertion but this is what you need to reproduce my problem
How can I decently add the target to my button? Or is there a way to do what I want to do with the collectionView cells directly?
Your problem is that UIButton.addTarget doesn't call a method that takes an IndexPath as a parameter. Typically what you do is handle the button action in the cell, and then invoke a callback when the button is pressed.
class KeyboardKeys: UICollectionViewCell {
@IBOutlet weak var button: UIButton! {
didSet {
button.addTarget(self, action: #selector(buttonAction(_:)), for: .touchUpInside)
}
}
var didPushButton: (KeyboardKeys) -> Void = { _ in }
@objc func buttonAction(_ button: UIButton) {
didPushButton(self)
}
}
In your data source method you want to register a callback with each cell:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCellId", for: indexPath) as! KeyboardKeys
cell.button.setTitle(letters[indexPath.row], for: .normal)
cell.didPushButton = { [weak self] cell in
guard let indexPath = collectionView.indexPath(for: cell) else { return }
self?.insert(indexPath: indexPath)
}
return cell
}