Swift 4:按钮在另一个 UITableViewCell 中触发相同的操作

Posted

技术标签:

【中文标题】Swift 4:按钮在另一个 UITableViewCell 中触发相同的操作【英文标题】:Swift 4: Button triggers the same action in another UITableViewCell 【发布时间】:2018-10-20 18:55:14 【问题描述】:

我有一个带有 2 个按钮(用于听力和语音识别)、标签和文本字段的单元格。我正在尝试实现的是选择语音识别按钮时,用户会说出标签中显示的内容。 我的问题是听按钮根据 indexPath.row 可以正常工作,但说话按钮不能。当它处于活动状态时,另一个单元格中的按钮也将变为活动状态。它在这些单元格中记录相同。You can see the picture of what I am talking about here

收听(即音频合成器)和语音识别的方法在 UITableViewCell 中。我已经尝试了所有可以在网上找到的解决方案,但都没有成功。试过了

protocol RepeatCellDelegate: class 
    func buttonTapped(cell: RepeatCell)

但问题仍然存在。另外,创建了另一个项目,而不是使用按钮进行语音识别,我只是使用直接 textField 输入,仍然出现同样的问题。

TableViewCell 类中的按钮:

@IBAction func speakButtonPressed(_ sender: Any) 
self.delegate?.buttonTapped(cell: self)

我的cellForRowAt indexPath

let cell = tableView.dequeueReusableCell(withIdentifier: "RepeatCell") as! RepeatCell

cell.delegate = self

cell.conditionlabel.text = repeatTask[indexPath.row].conditionLabel

return cell

检测单元格索引并记录语音输入的 buttonTapped 函数。点击按钮后它会打印正确的单元格索引,但该操作也会在另一个单元格中触发。

func buttonTapped(cell: RepeatCell) 
guard let indexPath = self.repeatTV.indexPath(for: cell) else 
    return


cell.speakButton.isSelected = !cell.speakButton.isSelected

if (cell.speakButton.isSelected)
    self.recordAndRecognizeSpeech()
 else 
    audioEngine.inputNode.removeTap(onBus: 0)
    recognitionTask?.cancel()


print("Button tapped on row \(indexPath.row)")

//语音输入识别函数:

// variables for speech recognizer

让 audioEngine = AVAudioEngine() 让speechRecognizer:SFSpeechRecognizer? = SFSpeechRecognizer(语言环境:Locale.init(标识符:“en-US”)) 让请求 = SFSpeechAudioBufferRecognitionRequest() var recognitionTask: SFSpeechRecognitionTask?

// 语音函数 func recordAndRecognizeSpeech()

let node = audioEngine.inputNode
let recordingFormat = node.outputFormat(forBus: 0)
node.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat)  buffer, _ in
    self.request.append(buffer)

audioEngine.prepare()

do 
    try audioEngine.start()
 catch 
    return print(error)


guard let myRecognizer = SFSpeechRecognizer() else 
    return

if !myRecognizer.isAvailable 
    return


recognitionTask = speechRecognizer?.recognitionTask(with: request, resultHandler:  (result, error) in
    if result != nil  //
        if let result = result
            let cell = self.repeatTV.dequeueReusableCell(withIdentifier: "RepeatCell") as! RepeatCell
            let bestString = result.bestTranscription.formattedString
            if cell.speakButton.isSelected == true 
                cell.userInput.text = bestString
            

        else if let error = error
            print(error)
        
    
)

我从本地 JSON 文件中获取数据,这是一个模型:

struct RepeatTask: Codable 
let name: String
let label: String
let conditionWord: String

也许有人可以帮助我解决这个问题?

【问题讨论】:

【参考方案1】:

这里没有足够的代码来重新创建您的问题,以后请提供Minimal, Complete, and Verifiable example。不幸的是,如果他们无法重现问题,没有人可以为您提供有效的解决方案来帮助您解决问题。

但我相信我理解您想要完成的工作:

    模型对象,即struct。 一种协议,其默认实现对所有单元都相同。 符合调用协议方法的协议的TableViewCell 类。 一个 TableViewDelegate 和 Datasource 来管理来自 1 的对象。

考虑以下几点:

import UIKit

/// 1.
/// Data model for "Repeat Cell Objects"
struct RepeaterModel 
    var outputText:String?
    var inputAudio:Data?


/// 2.
/// Allows a cell to delegate listening and repeating (speaking)
protocol RepeatableCell 
    func listen()
    func speak()


// Extend your protocol to add a default implementation,
// that way you can just confrom to the protocol
// without implementing it every time, in every cell class.
extension RepeatableCell 
    func listen() 
        print("default implementation for listen")
    

    func speak()
        print("default implementation for speak")
    





/// 3.
final class RepeatCell: UITableViewCell, RepeatableCell 

    // MARK: - Properties
    var model:RepeaterModel? 
        didSet 
            DispatchQueue.main.async 
                self.titleLabel.text = self.model?.outputText
            
        
    

    // MARK: - Views
    lazy var listenButton: UIButton = 
        let btn = UIButton(type: .system)
        btn.setTitle("Listen", for: .normal)
        btn.addTarget(self, action: #selector(activateListen), for: .touchUpInside)
        btn.setTitleColor(.white, for: .normal)
        btn.backgroundColor = .blue
        btn.translatesAutoresizingMaskIntoConstraints = false
        return btn
    ()

    lazy var speakButton: UIButton = 
        let btn = UIButton(type: .system)
        btn.setTitle("Speak", for: .normal)
        btn.addTarget(self, action: #selector(activateSpeak), for: .touchUpInside)
        btn.setTitleColor(.white, for: .normal)
        btn.backgroundColor = .green
        btn.translatesAutoresizingMaskIntoConstraints = false
        return btn
    ()

    let titleLabel: UILabel = 
        let l = UILabel()
        l.translatesAutoresizingMaskIntoConstraints = false
        l.textColor = .black
        l.textAlignment = .center
        l.text = "No Text"
        return l
    ()

    //MARK: - Initializers
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) 
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        self.setup()
    

    required init?(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    

    //MARK: - Class Methods
    func setup() 
        self.contentView.addSubview(listenButton)
        self.contentView.addSubview(speakButton)
        self.contentView.addSubview(titleLabel)

        let spacing: CGFloat = 25.0

        //Listen top left
        listenButton.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: spacing).isActive = true
        listenButton.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: spacing).isActive = true
        listenButton.widthAnchor.constraint(equalToConstant: 100).isActive = true
        listenButton.heightAnchor.constraint(equalToConstant: 50).isActive = true

        // title label, center top.
        titleLabel.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: spacing).isActive = true
        titleLabel.leadingAnchor.constraint(equalTo: self.listenButton.trailingAnchor, constant: spacing).isActive = true
        titleLabel.trailingAnchor.constraint(equalTo: self.speakButton.leadingAnchor, constant: -spacing).isActive = true
        titleLabel.heightAnchor.constraint(equalToConstant: 50).isActive = true

        //Speak top right
        speakButton.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: spacing).isActive = true
        speakButton.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -spacing).isActive = true
        speakButton.widthAnchor.constraint(equalToConstant: 100).isActive = true
        speakButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
    

    @objc func activateListen() 
        print("listen was pressed! on cell \(self.model?.outputText ?? "No Text")")
        /// The user wants to listen
        // call the delegate method..
        listen()
        // use self.model?.outputText
    

    @objc func activateSpeak() 
        print("Speak was pressed! on cell \(self.model?.outputText ?? "No Text")")
        /// The user is speaking, record audio
        // call the delegate method..
        speak()
        //self.model?.inputAudio = somedata
    




/// 4.
class ViewController: UITableViewController 

    // Array of your model objects
    var objects:[RepeaterModel] = []

    override func viewDidLoad() 
        super.viewDidLoad()
        self.tableView.register(RepeatCell.self, forCellReuseIdentifier: "Repeat")
        // create or fetch model objects
        let items = [
            RepeaterModel(outputText: "1st Cell", inputAudio: nil),
            RepeaterModel(outputText: "2nd Cell", inputAudio: nil),
            RepeaterModel(outputText: "3rd Cell", inputAudio: nil),
            RepeaterModel(outputText: "4th Cell", inputAudio: nil),
            RepeaterModel(outputText: "5th Cell", inputAudio: nil),
            RepeaterModel(outputText: "6th Cell", inputAudio: nil),
            RepeaterModel(outputText: "8th Cell", inputAudio: nil),
            RepeaterModel(outputText: "9th Cell", inputAudio: nil),
            RepeaterModel(outputText: "10th Cell", inputAudio: nil),
            RepeaterModel(outputText: "11th Cell", inputAudio: nil),
            RepeaterModel(outputText: "12th Cell", inputAudio: nil)
        ]
        self.objects += items
    


    //MARK: - TableView Methods

    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat 
        // 25 top spacing + 50 view element width + 25 bottom spacing
        return 100.0
    

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return objects.count
    

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        if let cell = self.tableView.dequeueReusableCell(withIdentifier: "Repeat") as? RepeatCell 
            cell.model = objects[indexPath.row]
            // other cell stuff
            return cell
        
        return UITableViewCell()
    

在每个向下的单元格上按“听”然后“说”会产生以下输出:

【讨论】:

感谢您花时间编写以上所有代码,不幸的是,它不起作用。它不播放音频,也不录制。我更新了我的问题,提供了有关我使用的先前方法的更多信息。有时间请看一下,谢谢。 或者我在RepeatCell扩展中做错了speak()和listen()函数,通常将bestString(语音识别)分配给另一个变量或textview,我需要做if语句,例如:如果(speakButton.isActiove) inputTextView.text = bestString 。但问题是我不能在扩展中使用 speakButton。也许还有另一种方法可以实现这一点,我需要弄清楚。 您可以通过协议方法传递 self 并返回一个值以分配给您的 textview。即listen(_ cell: UITableViewCell) -> String?,然后在按钮方法中使用textView.text = self.listen(self)。 (激活监听)。您遇到的问题很可能是因为recognitionTask 中的let cell = self.repeatTV.dequeueReusableCell(withIdentifier: "RepeatCell") as! RepeatCell 行,您应该引用单元格本身,而不是使另一个单元格出列。 好的,我会尝试找到如何引用单元格本身的方法。我假设单元格在进入 .isSelected 状态时获得由按钮本身引起的相同操作的问题。当我添加了一些只需要执行简单操作(聆听、更改颜色)的其他按钮时,它们会根据索引路径工作。无论如何,我对swift还是很陌生。你上面给出的例子也很好,它给了我一些关于事情是如何完成的见解。我会在接下来的几天里使用它,会告诉你它是怎么回事。感谢您的努力伙伴,非常感谢。

以上是关于Swift 4:按钮在另一个 UITableViewCell 中触发相同的操作的主要内容,如果未能解决你的问题,请参考以下文章

Swift 在另一个视图之上添加一个透明和模糊的视图

Swift 4 Capture Photo,photoOutput 返回 nil

在Swift中禁用工具栏按钮

swift4中的UITextfield验证 - 动态单元格

Swift 中选中/未选中的 UIButton

在表格视图的 swift 文件之间快速传递数组