Search code examples
iosswiftuicollectionviewuicollectionviewcelluicollectionviewcompositionallayout

How to set correct tintColor with selected but not focused UICellAccessory.customView


In a UICollectionView within a UISplitViewController layout, I have a UICollectionView.CellRegistration that shows under certain conditions a warning badge as UICellAccessory.

This warning badge accessory is based on the following code:

func warningBadge(cell: UICollectionViewListCell) -> UICellAccessory  {

    return UICellAccessory.customView(configuration: 
    .init(customView: UIImageView(image: UIImage(systemName: "exclamationmark.triangle")), 
           placement: .trailing(),
 reservedLayoutWidth: .custom(18), 
           tintColor: UIColor(dynamicProvider: { collection in

        if cell.isSelected {
            return .lightText
        }
        else {
            return .systemRed
        }
    })))
}

This works fine, except when the focus in the app moves to another part in the Split View control.

In that case the selected cell show a light grey background, instead of a tintColor background (which is correct), but the warning badge still has a selected appearance, which makes it hard to see (lightText on top of light grey background).

In case when the cell is selected but not focused, the image should have the non selected tintColor (.systemRed in this example code).

I tried to use cell.isFocused instead of, or in addition to cell.isSelected, but this doesn't work.

Interestingly, cell.isFocused does work correctly for UICellAccessory.label accessories, but apparently not for a UICellAccessory.customView with an image control.

How to give a custom accessory the correct tintColor when the cell is selected, but not focused?


Solution

  • The solution turned out to make a custom view based on UILabel instead of UIImageView. Normally you don't need a custom view for a label accessory, but I already have a UICellAccessory.label in my cell registration, and you're not allowed to use system accessory twice.

    This is the working code I now use:

    func warningBadge(cell: UICollectionViewListCell) -> UICellAccessory  {
        let label = UILabel()
        let attachment = NSTextAttachment()
        attachment.image = UIImage(systemName: "exclamationmark.triangle")?.withTintColor( UIColor(dynamicProvider: { collection in
            return cell.isFocused ? .lightText : .systemRed
        }))
        label.attributedText = NSAttributedString(attachment: attachment)
        return UICellAccessory.customView(configuration: .init(customView: label, placement: .trailing(), reservedLayoutWidth: .custom(18)))
    }