How to add a custom accessory to a UICollectionViewListCell?

Published by donnywals on

This article covers beta technology and it's up to date for Xcode 12 beta 5

Apple provides several accessory types that you can use to apply certain affordances to a UICollectionViewListCell. However, sometimes these options don't suit your needs and you're looking for something more customizable.

To add a custom accessory to a list cell instead of a standard one, you use the .custom accessory type. The initializer for this accessory takes a UICellAccessory.CustomViewConfiguration that describes how your accessory should look and where it's positioned. Let's dive right in with an example:

// create the accessory configuration
let customAccessory = UICellAccessory.CustomViewConfiguration(
  customView: UIImageView(image: UIImage(systemName: "paperplane")),
  placement: .leading(displayed: .always))

// add the accessory to the cell
cell.accessories = [.customView(configuration: customAccessory)]

To create a custom accessory all you really need to provide is a view that you want to display, and you need to specify where and when the accessory should be displayed. In this case, I created a simple UIImageView that shows a paperplane SF Symbol that's always visible on the leading edge of the cell. It's also possible to pass .trailing to make the accessory appear on the trailing edge of the cell. For the displayed argument of placement, you can pass .whenEditing or .whenNotEditing instead of .always to control whether your accessory should or shouldn't be visible when the collection view is in edit mode.

By default your custom accessory will be shown as close to the content as possible if there are multiple accessories shown on the same side of the cell. You can customize this by passing a closure to the placement object:

let customAccessory = UICellAccessory.CustomViewConfiguration(
  customView: UIImageView(image: UIImage(systemName: "paperplane")),
  placement: .trailing(displayed: .always, at: { accessories in
    return 1
  }))

In the closure you receive the other accessories that are shown on the same side as your accessory and you can return the preferred position for your accessory. A lower value makes your accessory appear closer to the content.

While you can already build a pretty nice accessory with this, there are more arguments that you can pass to UICellAccessory.CustomViewConfiguration's initializer:

let customAccessory = UICellAccessory.CustomViewConfiguration(
  customView: UIImageView(image: UIImage(systemName: "paperplane")),
  placement: .leading(displayed: .always, at: { accessories in
    return 1
  }),
  reservedLayoutWidth: .standard,
  tintColor: .darkGray,
  maintainsFixedSize: false)

This is an example of a fully customized accessory. In addition to the required two arguments you can also pass a reservedLayoutWidth to tell iOS how much space should be used for your accessory. This will help iOS build your layout and space the cell's content appropriately. The tintColor argument is used to set a color for your accessory. The default color is a blue color, in this example I changed this color to .darkGray. Lastly you can determine if the accessory maintains a fixed size, or if it can scale if needed.


Practical Combine

Learn everything you need to know about Combine and how you can use it in your projects with my new book Practical Combine. You'll get thirteen chapters, a Playground and a handful of sample projects to help you get up and running with Combine as soon as possible.

The book is available as a digital download for just $24.99!

Get Practical Combine

Receive weekly updates about my posts