简体   繁体   中英

UIButton's .touchUpInside event not working

I am working on adding buttons to UICollectionViewCell at runtime and having the button registered to the event according to its type, at runtime. The type of the button can be, for instance, utterance button and action button, and both should be registered to separate events.

The hierarchy of UI elements is like UICollectionView -> UICollectionViewCell -> StackView -> Multiple ( UIView -> UIButton ).

The problem is that I am unable to register the .touchUpInside events.

What I have tried yet is calling becomeFirstResponder() through button's instance, moving the event method inside the UIViewController and adding target in cellForItemAt method after having the view hierarchy set up, trying isUserInteractionEnabled with values true and false on UICollectionView , UICollectionViewCell , UIStackView , UIView and UIButton .

EDIT 1: So the code looks like the following:

class AssistantViewController: UIViewController {
    private var assistantManager: AssistantManager
    private var conversationSection: UICollectionView
    private let layout:UICollectionViewFlowLayout = UICollectionViewFlowLayout.init()

    private var multipleChoiceMessageCell = MultipleChoiceMessageCell()
    private var conversationCells: [BaseCollectionViewCell] {
        return [
            multipleChoiceMessageCell
        ]
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        construct()
        assistantManager.performGreetingUtterance()
    }

}

extension AssistantViewController: UICollectionViewDataSource {
    @objc func didTapUtteranceButton() {
        print ("elo I a m here")
    }

    func collectionView(_ collectionView: UICollectionView,
                        cellForItemAt indexPath: IndexPath) -> 
  UICollectionViewCell {
        let conversationItem = assistantManager.conversationItems[indexPath.row]
        let cellId = self.getCellIdentifier(for: conversationItem)
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId,
                                                      for: indexPath) as? ConversationItemCell

        if let conversationCell = cell {
            conversationCell.layoutIfNeeded()

            conversationCell.configure(with: conversationItem)

            switch conversationItem {
            case let .multipleChoiceMessage(chatMessage, multipleChoice): do {

                for item in multipleChoice.multipleChoice.items {
                    switch item {
                    case .utteranceButton(let utteranceButton): do {
                        utteranceButton.button.isUserInteractionEnabled = true
                        utteranceButton.button.addTarget(self,
                                                         action: #selector(didTapUtteranceButton),
                                                         for: .touchUpInside)
                        //utteranceButton.button.becomeFirstResponder()
                        print("added")
                        }
                    }
                }
                }
            default: print("Todo")
            }
        }
        return cell!
    }

class AssistantManager {
    public func parseAssistantSnippets(
        snippets: [Snippet]) -> ConversationCellModel {
        var interactableItems: [MessageContent] = []
        var utteredText:String = ""

        for snippet in snippets {
            switch snippet {
            case .text(let text): utteredText = text.displayText
            case .utteranceButton(let utteranceButton): do {
                let button = UtteranceButton(buttonTitle: utteranceButton.displayText,
                                               maxWidth: ConversationCell.elementMaxWidth,
                                               utteranceText: utteranceButton.utteranceText,
                                               onPress: performUserUtterance)
                let messageContent = MessageContent.utteranceButton(button)

                interactableItems.push(messageContent)
                }
            default: print("TODO")
            }
        }
        return  ConversationCellModel.init(message: utteredText,
                                           multipleChoice: interactableItems)
    }

    public func performGreetingUtterance() {
        self.addTypingIndicator()

        performUtteranceOperation(utterance: "hi")
    }

    public func performUtteranceOperation(utterance: String) {
        let context = UtteranceRequest.UserContext(cid: "1",
                                                   location: CLLocationCoordinate2D(),
                                                   storeID: "1",
                                                   timeZoneOffset: 0)
        let request = UtteranceRequest(utterance: utterance,
                                       context: context)

        provider.conveyUtterance(request) { [weak self] result in
            switch result {
            case .success(let snippetResponse): do {
                let snippetResponseTransformed = SnippetResponse.init(snippetResponse)
                let ConversationCellModel = self?.parseAssistantSnippets(
                    snippets: snippetResponseTransformed.snippets)

                if let model = ConversationCellModel {
                    self?.addMessageFromAssistant(interactableItems: model.multipleChoice,
                                                       utteredText: model.message)
                }
                }
            case .failure(let error): return
            }
        }
    }
}

class MultipleChoiceMessageCell: ConversationCell {
    public func configure(with conversationItem: ConversationItem) {
        switch conversationItem {
        case let .multipleChoiceMessage(chatMessage, multipleChoice): do {
            let isCellFromUser = chatMessage.metadata.isFromUser
            let avatarBackgroundColor = chatMessage.metadata.avatarBackgroundColor
            let avatarTintColor = chatMessage.metadata.avatarTintColor
            let messageTextBackgroundColor = chatMessage.metadata.textContainerBackgroundColor
            let messageTextColor = chatMessage.metadata.textColor

            self.messageTextLabel.text = chatMessage.message
            self.avatarImageView.image = chatMessage.metadata.avatarImage
            self.messageTextContainer.backgroundColor = messageTextBackgroundColor
            self.messageTextLabel.textColor = messageTextColor
            self.avatarImageView.backgroundColor = avatarBackgroundColor
            self.avatarImageView.tintColor = avatarTintColor
            self.rightMessageHorizontalConstraint?.isActive = isCellFromUser
            self.leftMessageHorizontalConstraint?.isActive = !isCellFromUser
            self.rightAvatarHorizontalConstraint?.isActive = isCellFromUser
            self.leftAvatarHorizontalConstraint?.isActive = !isCellFromUser

            configureCellWith(interactablesSection: multipleChoice.multipleChoice.itemsContainer)
        }
        default:
            logIncorrectMapping(expected: "message", actual: conversationItem)
        }
    }

    func configureCellWith(interactablesSection: UIStackView) {
        let stackViewTop = NSLayoutConstraint(
            item: interactablesSection,
            attribute: .top,
            relatedBy: .equal,
            toItem: self.messageTextContainer,
            attribute: .bottom,
            multiplier: 1,
            constant: 10)
        let stackViewLeading = NSLayoutConstraint(
            item: interactablesSection,
            attribute: .leading,
            relatedBy: .equal,
            toItem: self,
            attribute: .leading,
            multiplier: 1,
            constant: ConversationCell.avatarMaxSize +
                ConversationCell.messageContainerMaxLeadingMargin +
                ConversationCell.avatarMaxMargin)

        self.addAutoLayoutSubview(interactablesSection)

        self.addConstraints([stackViewTop,
                             stackViewLeading])
    }
}

class InteractableItem {
    let contentView: UIView
    var height: CGFloat

    init() {
        contentView = UIView()
        height = 0
    }
}

class CellPrimaryButton: InteractableItem {
    let button: CorePrimaryButton

    init(buttonTitle titleLabel: String, maxWidth: CGFloat) {
        button = CorePrimaryButton()
        button.setTitle(titleLabel, for: .normal)
        button.setBackgroundColor(CoreColor.white, for: .normal)
        button.setTitleColor(CoreColor.black, for: .normal)
        button.layer.borderColor = CoreColor.black.cgColor
        button.layer.borderWidth = 1
        //button.becomeFirstResponder()
        super.init()
        contentView.isUserInteractionEnabled = true
//        button.addTarget(self, action: #selector(didTapUtteranceButton), for: .touchUpInside)
        setConstraints(maxWidth: maxWidth)
    }

    func setConstraints(maxWidth: CGFloat) {
        let buttonHorizontal = NSLayoutConstraint(item: self.button,
                                                  attribute: .leading,
                                                  relatedBy: .equal,
                                                  toItem: self.contentView,
                                                  attribute: .leading,
                                                  multiplier: 1,
                                                  constant: 0)

        let buttonWidth = NSLayoutConstraint(item: self.button,
                                              attribute: .width,
                                              relatedBy: .lessThanOrEqual,
                                              toItem: nil,
                                              attribute: .notAnAttribute,
                                              multiplier: 1,
                                              constant: maxWidth)

        self.contentView.addConstraints([buttonHorizontal,
                                         buttonWidth])
        self.contentView.backgroundColor = UIColor.purple
        self.height = self.button.intrinsicContentSize.height
        self.contentView.addAutoLayoutSubview(self.button)
    }
}

class UtteranceButton: CellPrimaryButton {
    let utteranceText: String
    let performUtterance: (String) -> Void

    init(buttonTitle titleLabel: String,
         maxWidth: CGFloat,
         utteranceText: String,
         onPress: @escaping (String) -> Void) {

        self.utteranceText = utteranceText
        self.performUtterance = onPress

        super.init(buttonTitle: titleLabel, maxWidth: maxWidth)
    }
}

Try this way of calling an action.

UIApplication.shared.sendAction(#selector(didTapUtteranceButton), to: self, from: nil, for: nil)

instead of this one:

utteranceButton.button.addTarget(self, #selector(didTapUtteranceButton), for: .touchUpInside)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM