UICollectionViewListCell и пользовательский аксессуар для ячеек в iOS 14

#ios #swift

#iOS #swift

Вопрос:

Мои проблемы с новыми ячейками списка просмотра коллекции заключаются в том, что я не могу добавлять обработчики действий в пользовательский аксессуар.

Я пытался сделать следующее:

 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
    }
}
  

Я создаю подкласс UICollectionViewListCell, создаю кнопку с обработчиком целевого действия и добавляю ее в массив аксессуаров. У меня также есть собственная реализация конфигурации ячейки.

Теперь я создаю протокол, в котором я делегирую обработку действий своему контроллеру представления (я также внедрил новый API регистрации ячеек и set cell.link = self ).

Моя проблема здесь в том, что моя кнопка аксессуара не вызывается starButtonPressed , хотя это представление аксессуаров отзывчиво (оно меняет цвет при выделении).

Моя идея заключается в том, что может быть что-то не так с тем, как я реализую обработку своих действий с помощью пользовательского аксессуара, но, похоже, информации об этом новом API практически нет.

Более того, при выборе между предопределенными аксессуарами некоторые из них имеют actionHandler замыкания типа UICellAccessory.ActionHandler но я, кажется, не понимаю, как правильно это реализовать.

Любые идеи будут высоко оценены.

Ответ №1:

iOS 14 с использованием UIActions

Начиная с iOS 14, мы можем инициализировать UIButton и другие элементы управления UIControl с помощью основных действий. Он становится похож на обработчики собственных аксессуаров. С помощью этого мы можем использовать любой параметризованный метод, который мы хотим. И параметризация важна, потому что обычно мы хотим выполнить какое-то действие с конкретной ячейкой. #селектор не может быть параметризован, поэтому мы не можем передать методу какую-либо информацию о том, какая ячейка должна быть обновлена. Но это решение работает только для iOS 14 .

Создание uiAction:

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

Создание UIButton:

 let favoriteButton = UIButton(primaryAction: favoriteAction)
  

Создание аксессуара:

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

Использование

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

Комментарии:

1. Это решение работает, но кнопка не выделяется.

2. Использование UIButton(type: .system, primaryAction ...)

Ответ №2:

Я решил свою проблему, добавив распознаватель жестов касания в пользовательский вид моего аксессуара. Итак, это работает следующим образом:

 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)]
  

Нигде не видел, чтобы это было задокументировано, поэтому надеюсь, что это кому-то поможет.

Ответ №3:

Я последовал аналогичному вашему подходу, но вместо UITapGestureRecognizer я добавил цель к кнопке.

 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)]
  

Сначала я попробовал распознаватель жестов касания, и у меня это не сработало.

Комментарии:

1. Настраиваете ли вы свою ячейку внутри контроллера просмотра? Я делаю это внутри пользовательского класса ячеек списка, и все работает нормально.