Смена картинки у кнопки при нажатии

swift3

#1

Привет! Делаю плеер, заполнил таблицу при помощи json массива, кнопки работают и музыка играет. Но возникла проблема со сменой картинок у кнопки, тобишь чтобы при нажатии на кнопку, сменилась картинка, затем если нажал плей на другой песне, то картинка у первой сбросилась. Я пробовал это сделать через массив со всеми нажатыми кнопками и сбрасывал их каждый раз. Но тут проблема в том, что если я нажал плей на 1 музыке, сменяется картинка и у другой песни и так через каждые 6 строк.


#2

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

Например, так (код очень примерный и не проверялся на работоспособность):

enum SongState {
    case playing, stopped, undefined //etc
}
    
struct Song {
    var name = ""
    var artist = ""
    var audioFile = ""
    var pictureFile = ""
    var state: SongState = .undefined
}
    
//...
//данные для таблицы
let playList = [Song]()
    
//...
//допустим класс ячейки называется Cell
@objc private func myCoolButton_Pressed(sender: UIButton) {
    guard let cell = sender.findClass(type: Cell.self) else {return}
    guard let indexPath = self.tableView.indexPath(for: cell) else { return }
    if playList[indexPath.row].state == .undefined {
        playList[indexPath.row].state = .playing
        sender.setImage(UIImage(named: "mypic_playing"), for: .normal)
        //сбросить состояние у других кнопок, пробежавшись по массиву
    }
}
    
//...
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! Cell
    //вот тут назначаем картинки в зависимости от состояния
    switch playList[indexPath.row].state {
    case .undefined:
        cell.myCoolButton.setImage(UIImage(named: "mypic_undefined"), for: .normal)
    case .playing:
        cell.myCoolButton.setImage(UIImage(named: "mypic_playing"), for: .normal)
    default:
        cell.myCoolButton.setImage(UIImage(named: "mypic_some_state"), for: .normal)
        break
    }
    return cell
}
    

//небольшой extension для поиска нужного view вверх по дереву 
extension UIView {
    func findClass<T>(type: T.Type) -> T? {
        var parentResponder: UIResponder? = self
        while parentResponder != nil {
            parentResponder = parentResponder!.next
            if let view = parentResponder as? T {
                return view
            }
        }
        return nil
    }
}



#3

не совсем понял, почему свич работает по состоянию массива с песнями, у массива же нет свойства state


#4

В моем примере плейлист - это массив из структур Song

let playList = [Song]()

Song выглядит так:

struct Song {
    var name = ""
    var artist = ""
    var audioFile = ""
    var pictureFile = ""
    var state: SongState = .undefined
}

В поле структуры state мы сохраняем состояние песни и в зависимости от этого состояния назначаем кнопке картинку.

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


#5

понял) А вот у sender, findClass тоже нету


#6

Нету, поэтому я написал extension.

//небольшой extension для поиска нужного view вверх по дереву 
extension UIView {
    func findClass<T>(type: T.Type) -> T? {
        var parentResponder: UIResponder? = self
        while parentResponder != nil {
            parentResponder = parentResponder!.next
            if let view = parentResponder as? T {
                return view
            }
        }
        return nil
    }
}

#7

и последний вопрос надеюсь) Ругается на guard let cell = sender.findClass(type: Cell.self) else {return} ,

Use of unresolved identifier ‘Cell’


#8

Правильно ругается. Я же не знаю, как вы назвали ваш класс прототипа ячейки. Поэтому для примера написал “Cell”. Если у вас класс ячейки называется MyAwesomeCell, то так и пишите sender.findClass(type: MyAwesomeCell.self).


#9

вроде все настроил)

также, отключить форматирование текста можно, начав строку с четырех пробелов
вот тут нужно было пробежаться по массиву playlist и сбросить состояние до .undefined ведь так? Теперь ругается на Cannot assign to property: ‘i’ is a ‘let’ constant

guard let cell = sender.findClass(type: UIT.self) else {return}
guard let indexPath = self.tableView.indexPath(for: cell) else { return }
if playList[indexPath.row].state == .undefined {
playList[indexPath.row].state = .playing

        for  i in playList {
            i.state = .undefined
        }
       
        sender.setBackgroundImage(UIImage(named: "btnPlay-2.png"), for: UIControlState.normal)
        
    }

#10

поправил, просто сделал так

for i in 0…playList.count-1 {
if (i != indexPath.row) {
playList[i].state = .undefined
}

        }

спасибо большое! все работает!