【问题标题】:Swift 4: Button triggers the same action in another UITableViewCellSwift 4:按钮在另一个 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

}

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

【问题讨论】:

    标签: uitableview swift4 indexpath


    【解决方案1】:

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

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

    1. 模型对象,即struct
    2. 一种协议,其默认实现对所有单元都相同。
    3. 符合调用协议方法的协议的TableViewCell 类。
    4. 一个 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还是很陌生。你上面给出的例子也很好,它给了我一些关于事情是如何完成的见解。我会在接下来的几天里使用它,会告诉你它是怎么回事。感谢您的努力伙伴,非常感谢。
    猜你喜欢
    • 2017-02-26
    • 2021-07-18
    • 2020-08-01
    • 1970-01-01
    • 2016-10-11
    • 1970-01-01
    • 1970-01-01
    • 2018-12-04
    • 2016-11-12
    相关资源
    最近更新 更多