UITableView Custom Cell кнопка выбирает другие кнопки из основного вида? - PullRequest
0 голосов
/ 04 сентября 2018

В моей пользовательской ячейке просмотра таблицы есть кнопка, которую пользователь может нажимать, чтобы переключаться между выбранным и невыбранным. Это просто UIButton, эта кнопка содержится в моей настраиваемой ячейке просмотра в качестве розетки.

import UIKit

class CustomCellAssessment: UITableViewCell {

@IBOutlet weak var name: UILabel!
@IBOutlet weak var dateTaken: UILabel!
@IBOutlet weak var id: UILabel!
@IBOutlet weak var score: UILabel!
@IBOutlet weak var button: UIButton!


override func awakeFromNib() {
     super.awakeFromNib()
    // Initialization code
}

 override func setSelected(_ selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)


    // Configure the view for the selected state
}

@IBAction func selectButton(_ sender: UIButton) {

if sender.isSelected{

  sender.isSelected = false

}else{
  sender.isSelected = true
} 
} 
}

Странно то, что когда я нажимаю кнопку, скажем, на первой ячейке, она затем выбирает кнопку на 8 ячеек вниз на другую ячейку (вне поля зрения) в том же виде таблицы. У каждой ячейки есть своя собственная кнопка, но, как будто использование dequeReusableCell приводит к тому, что система ведет себя так. Почему это происходит и связано ли это с тем, как работают кнопки UI для ячеек табличного просмотра?

Спасибо

Ответы [ 4 ]

0 голосов
/ 04 сентября 2018

Один из способов решения вашей проблемы заключается в следующем

1. First create a protocol to know when button is clicked

protocol MyCellDelegate: class {
    func cellButtonClicked(_ indexPath: IndexPath)
}

2. Now in your cell class, you could do something like following

class MyCell: UITableViewCell {

var indexPath: IndexPath?
weak var cellButtonDelegate: MyCellDelegate?


func configureCell(with value: String, atIndexPath indexPath: IndexPath, selected: [IndexPath]) {
        self.indexPath = indexPath  //set the indexPath
        self.textLabel?.text = value
        if selected.contains(indexPath) {
            //this one is selected so do the stuff
            //here we will chnage only the background color
            backgroundColor = .red
            self.textLabel?.textColor = .white

        } else {
            //unselected
            backgroundColor = .white
            self.textLabel?.textColor = .red
        }
}

    @IBAction func buttonClicked(_ sender: UIButton) {
        guard let delegate = cellButtonDelegate, let indexPath = indexPath else { return }
        delegate.cellButtonClicked(indexPath)
    }
}

3. Now in your controller. I'm using UItableViewController here

class TheTableViewController: UITableViewController, MyCellDelegate {

    let cellData = ["cell1","cell2","cell3","cell4","cell2","cell3","cell4","cell2","cell3","cell4","cell2","cell3","cell4","cell2","cell3","cell4","cell2","cell3","cell4"]
    var selectedIndexpaths = [IndexPath]()


    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(MyCell.self, forCellReuseIdentifier: "MyCell")
    }


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

    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 55.0
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! MyCell
        cell.cellButtonDelegate = self
        cell.configureCell(with: cellData[indexPath.row], atIndexPath: indexPath, selected: selectedIndexpaths)
        return cell
    }

    override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 30.0
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let cell = tableView.cellForRow(at: indexPath) as! MyCell
        cell.buttonClicked(UIButton())
    }

    func cellButtonClicked(_ indexPath: IndexPath) {
        if selectedIndexpaths.contains(indexPath) {
            //this means cell has already selected state
            //now we will toggle the state here from selected to unselected by removing indexPath
            if let index = selectedIndexpaths.index(of: indexPath) {
                selectedIndexpaths.remove(at: index)
            }

        } else {
            //this is new selection so add it
            selectedIndexpaths.append(indexPath)
        }

        tableView.reloadData()
    }
}
0 голосов
/ 04 сентября 2018

У каждой ячейки есть своя собственная кнопка , но при этом dequeReusableCell приводит к тому, что система ведет себя так.

Неправильно. UITableViewCells можно использовать повторно, поэтому, если у вашего tableView 8 видимых ячеек, при загрузке 9-й ячейки номер 1 будет использоваться повторно.

Решение: вам нужно следить за состоянием ваших клеток. Когда вызывается метод cellForRowAtIndexPath, необходимо настроить ячейку с нуля .

В вашем ViewController может быть небольшой массив, содержащий состояние ячеек:

var cellsState: [CellState]

и сохранить там выбранное состояние для каждого indexPath. Затем в cellForRowAtIndexPath вы настраиваете ячейку с состоянием.

cell.selected = self.cellsState[indexPath.row].selected

Итак, обзор я бы сделал:

1 - На cellForRowAtIndexPath я бы установил

cell.button.tag = indexPath.row
cell.selected = cellsState[indexPath.row].selected

2 - Переместите IBAction на ваш ViewController или TableViewController

3 - при вызове метода щелчка обновлять выбранное состояние ячейки

self.cellsState[sender.tag].selected = true

Не забудьте всегда настраивать всю ячейку на cellForRowAtIndexPath

Edit:

import UIKit

struct CellState {
    var selected:Bool
    init(){
        selected = false
    }
}

class MyCell: UITableViewCell {

    @IBOutlet weak var button: UIButton!

    override func awakeFromNib() {
        self.button.setTitleColor(.red, for: .selected)
        self.button.setTitleColor(.black, for: .normal)
    }

}

class ViewController: UIViewController, UITableViewDataSource {

    var cellsState:[CellState] = []

    @IBOutlet weak var tableView: UITableView!


    override func viewDidLoad() {
        super.viewDidLoad()

        //Add state for 5 cells.
        for _ in 0...5 {
            self.cellsState.append(CellState())
        }
    }   


    @IBAction func didClick(_ sender: UIButton) {

        self.cellsState[sender.tag].selected = true
        self.tableView.reloadData()
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return cellsState.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! MyCell
        cell.button.isSelected = self.cellsState[indexPath.row].selected
        cell.button.tag = indexPath.row
        return cell
    }
}
0 голосов
/ 04 сентября 2018

В вашем tableView(_: cellForRowAt:) выполните действие для нажатия кнопки и добавьте цель

cell.button.addTarget(self, action: #selector(self.selectedButton), for: .touchUpInside)

В целевом методе используйте следующие строки, чтобы получить indexPath

func selectedButton(sender: UIButton){
       let hitPoint: CGPoint = sender.convert(CGPoint.zero, to: self.tableView)
       let indexPath: NSIndexPath = self.tableView.indexPathForRow(at: hitPoint)! as NSIndexPath
}

Тогда делай свои вещи, используя indexPath. На самом деле ваш метод не может найти, в котором нажата кнопка indexPath, поэтому не работает нужная кнопка.

0 голосов
/ 04 сентября 2018

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

Если выбор кнопок является отдельным от выбора ячеек таблицы, то вам нужно будет отслеживать индексные пути выбранных кнопок как часть вашей модели данных, а затем обновлять выбранное состояние кнопки в tableView(_: cellForRowAt:).

В табличном представлении будет создано только столько ячеек, сколько необходимо для отображения информации, отображаемой на экране и в битах. После этого клетки используются повторно. Если ячейка прокручивается за верхнюю часть экрана, табличное представление помещает ее в очередь для повторного использования, а затем, когда ей необходимо отобразить новую строку внизу при прокрутке, она вытаскивает ее из очереди и выдает ее вам. tableView(_: cellForRowAt:).

Ячейка, которую вы здесь видите, будет точно такой же, как и та, которую вы использовали примерно на 8 строк ранее, и вам решать, как ее полностью настроить. Вы можете сделать это в cellForRow, и у вас также есть возможность в самой ячейке, реализовав prepareForReuse, который вызывается непосредственно перед снятием очереди с ячейки.

...