My issues with new collection view list cells is that I'm not able to add action handlers to a custom accessory view.

I've been trying to do the following:

protocol TappableStar: class { func onStarTapped(_ cell: UICollectionViewCell) } class TranslationListCell: UICollectionViewListCell { let starButton: UIButton = { let starButton = UIButton() let starImage = UIImage(systemName: "star")!.withRenderingMode(.alwaysTemplate) starButton.setImage(starImage, for: .normal) starButton.setContentHuggingPriority(.defaultHigh, for: .horizontal) starButton.addTarget(self, action: #selector(starButtonPressed(_:)), for: .touchUpInside) starButton.tintColor = .systemOrange return starButton }() var translation: TranslationModel? weak var link: TappableStar? override init(frame: CGRect) { super.init(frame: frame) accessories = [.customView(configuration: .init(customView: starButton, placement: .trailing(displayed: .always)))] } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @objc private func starButtonPressed(_ sender: UIButton) { link?.onStarTapped(self) } override func updateConfiguration(using state: UICellConfigurationState) { // Create new configuration object and update it base on state var newConfiguration = TranslationContentConfiguration().updated(for: state) // Update any configuration parameters related to data item newConfiguration.inputText = translation?.inputText newConfiguration.outputText = translation?.outputText contentConfiguration = newConfiguration } } 

I subclass UICollectionViewListCell, create a button with target-action handler and add it to accessories array. I also have my own implementation of cell configuration.

Now, I create a protocol where I delegate action handling to my view controller (I also implemented new cell registration API and set cell.link = self).

My problem here is that my accessory button doesn't call starButtonPressed although this accessory view is responsive (it changes color when highlighted).

My idea is that there might be something wrong with the way I implement my action handling with a custom accessory but there seems to be little to none information about this new api.

Moreover, when choosing between predefined accessories, some of them have actionHandler closures of type UICellAccessory.ActionHandler but I don't seem to understand how to properly implement that.

Any ideas would be much appreciated.

3 Answers

iOS 14, using UIActions

Since iOS 14 we can initialise UIButton and other UIControls with primary actions. It becomes similar to handlers of native accessories. With this we can use any parametrized method we want. And parametrising is important, because usually we want to do some action with specific cell. #selector's cannot be parametrised, so we can't pass any information to method about which cell is to be updated. But this solution works only for iOS 14+.

Creating UIAction:

let favoriteAction = UIAction(image: UIImage(systemName: "star"), handler: { [weak self] _ in guard let self = self else { return } self.handleFavoriteAction(for: your_Entity) }) 

Creating UIButton:

let favoriteButton = UIButton(primaryAction: favoriteAction) 

Creating accessory:

let favoriteAccessory = UICellAccessory.CustomViewConfiguration( customView: favoriteButton, placement: .leading(displayed: .whenEditing) ) 

Using

cell.accessories = [.customView(configuration: favoriteAccessory)] 
1

I solved my issue by adding tap gesture recognizer to my accessory's custom view. So it works like this:

let customAccessory = UICellAccessory.CustomViewConfiguration( customView: starButton, placement: .trailing(displayed: .always)) let tapGesture = UITapGestureRecognizer(target: self, action: #selector(starButtonPressed(_:))) customAccessory.customView.addGestureRecognizer(tapGesture) accessories = [.customView(configuration: customAccessory)] 

Haven't seen it documented anywhere so hope it helps somebody.

I followed a similar approach to yours, but instead of a UITapGestureRecognizer, I added a target to the button.

var starButton = UIButton(type: .contactAdd) starButton.addTarget(self, action: #selector(self.starButtonTapped), for: .touchUpInside) let customAccessory = UICellAccessory.CustomViewConfiguration(customView: starButton, placement: .trailing(displayed: .always)) cell.accessories = [.customView(configuration: customAccessory)] 

I first tried the tap gesture recognizer and it didn't work for me.

1

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy