Collection View as Custom Keyboard not working

Hello,


I am building application where you have custom keyboard. I used Collection View to display set of stickers, but for some reason it's not working, and throws errors related to constraints when switching to that keyboard.


After that, I've use same coding approach and created Table View and it's working just fine.


Question: Are you able to use Collection View inside Custom Keyboard?


Here's my code in Keyboard class:

It's working fine, if I use it in UIView, or in UIViewController.

class KeyboardViewController: UIInputViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, UICollectionViewDelegate {

let stickerImages = [
  UIImage(named: "Image-1"),
  UIImage(named: "Image-2"),
  UIImage(named: "Image-3"),
  UIImage(named: "Image-4"),
  UIImage(named: "Image-5")
]

@IBOutlet var nextKeyboardButton: UIButton!
@IBOutlet var collectionView: UICollectionView!

override func updateViewConstraints() {
  super.updateViewConstraints()
}

override func viewDidLoad() {
  super.viewDidLoad()

  let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
  layout.scrollDirection = UICollectionView.ScrollDirection.vertical
  layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
  layout.itemSize = CGSize(width: 50, height: 50)

  collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
  collectionView.dataSource = self
  collectionView.delegate = self
  collectionView.register(StickersCell.self, forCellWithReuseIdentifier: StickersCell.reuseIdentifier)
  collectionView.backgroundColor = UIColor.white
  collectionView.showsHorizontalScrollIndicator = false
  collectionView.backgroundColor = UIColor.red

  collectionView.translatesAutoresizingMaskIntoConstraints = false

  self.view.addSubview(collectionView)

  self.nextKeyboardButton = UIButton(type: .system)

  self.nextKeyboardButton.setTitle(NSLocalizedString("Next Keyboard", comment: "Title for 'Next Keyboard' button"), for: [])
  self.nextKeyboardButton.sizeToFit()
  self.nextKeyboardButton.translatesAutoresizingMaskIntoConstraints = false
  self.nextKeyboardButton.backgroundColor = UIColor.white

  self.nextKeyboardButton.addTarget(self, action: #selector(handleInputModeList(from:with:)), for: .allTouchEvents)

  self.view.addSubview(self.nextKeyboardButton)

  self.nextKeyboardButton.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
  self.nextKeyboardButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true

  self.collectionView.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
  self.collectionView.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
  self.collectionView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
  self.collectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
}

override func textWillChange(_ textInput: UITextInput?) {
  // The app is about to change the document's contents. Perform any preparation here.
}

override func textDidChange(_ textInput: UITextInput?) {
  // The app has just changed the document's contents, the document context has been updated.

  var textColor: UIColor
  let proxy = self.textDocumentProxy
  if proxy.keyboardAppearance == UIKeyboardAppearance.dark {
  textColor = UIColor.white
  } else {
  textColor = UIColor.black
  }
  self.nextKeyboardButton.setTitleColor(textColor, for: [])
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

  return stickerImages.count
}

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

  cell.setImage(stickerImages[indexPath.item]!)

  return cell
}
}


And that's how I create Collection View Cell


class StickersCell: UICollectionViewCell {

static let reuseIdentifier: String = "StickersCell"

lazy var imageView: UIImageView = {
  let imageView = UIImageView(frame: .zero)
  imageView.contentMode = .scaleAspectFit
  imageView.translatesAutoresizingMaskIntoConstraints = false
  return imageView
}()

override init(frame: CGRect) {
  super.init(frame: frame)
  contentView.clipsToBounds = true
  contentView.addSubview(imageView)

  imageView.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
  imageView.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
  imageView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
  imageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
}

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

func setImage(_ image: UIImage) {
  imageView.image = image
}
}

Accepted Reply

After battling with this issue for 2 weeks finally found working workaround:


For some reason you can't use

UIImageView
or
MSStickerView
inside
UICollectionViewCell
same as in iMessage Extension, so instead I just added transparent
UIButton
with
UIImage
inside this button and it worked!


Still don't know why you can't use images or views and couldn't find any specific info about it, but my solutions works and I hope this will help someone in future.

Replies

After battling with this issue for 2 weeks finally found working workaround:


For some reason you can't use

UIImageView
or
MSStickerView
inside
UICollectionViewCell
same as in iMessage Extension, so instead I just added transparent
UIButton
with
UIImage
inside this button and it worked!


Still don't know why you can't use images or views and couldn't find any specific info about it, but my solutions works and I hope this will help someone in future.