Разверните ячейку при использовании в Swift

Я пытаюсь реализовать функцию в своем приложении, чтобы, когда пользователь отбирает ячейку в моем представлении таблицы, ячейка расширяется вниз, чтобы выявлять заметки. Я нашел много примеров этого в Objective-C, но я еще не нашел его для Swift.

Этот пример кажется идеальным: Аккордеонная ячейка таблицы - Как динамически расширять/сокращать uitableviewcell?

Я попытался перевести его в Свифт:

var selectedRowIndex = NSIndexPath()
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    selectedRowIndex = indexPath
    tableView.beginUpdates()
    tableView.endUpdates()
}

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    if selectedRowIndex == selectedRowIndex.row && indexPath.row == selectedRowIndex.row {
        return 100
    }
    return 70
}

Однако это, похоже, приводит к сбою приложения.

Есть идеи?

Редактировать:

Вот мой код cellForRowAtIndexPath:

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    var cell:CustomTransactionTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as CustomTransactionTableViewCell

    cell.selectionStyle = UITableViewCellSelectionStyle.None

    if tableView == self.searchDisplayController?.searchResultsTableView {
        cell.paymentNameLabel.text = (searchResults.objectAtIndex(indexPath.row)) as? String
        //println(searchResults.objectAtIndex(indexPath.row))
        var indexValue = names.indexOfObject(searchResults.objectAtIndex(indexPath.row))
        cell.costLabel.text = (values.objectAtIndex(indexValue)) as? String
        cell.dateLabel.text = (dates.objectAtIndex(indexValue)) as? String

        if images.objectAtIndex(indexValue) as NSObject == 0 {
            cell.paymentArrowImage.hidden = false
            cell.creditArrowImage.hidden = true
        } else if images.objectAtIndex(indexValue) as NSObject == 1 {
            cell.creditArrowImage.hidden = false
            cell.paymentArrowImage.hidden = true
        }
    } else {
        cell.paymentNameLabel.text = (names.objectAtIndex(indexPath.row)) as? String
        cell.costLabel.text = (values.objectAtIndex(indexPath.row)) as? String
        cell.dateLabel.text = (dates.objectAtIndex(indexPath.row)) as? String

        if images.objectAtIndex(indexPath.row) as NSObject == 0 {
            cell.paymentArrowImage.hidden = false
            cell.creditArrowImage.hidden = true
        } else if images.objectAtIndex(indexPath.row) as NSObject == 1 {
            cell.creditArrowImage.hidden = false
            cell.paymentArrowImage.hidden = true
        }
    }
    return cell
}

Вот настройки выхода:

enter image description here

Ответы

Ответ 1

Первое сравнение в вашем операторе if никогда не может быть истинным, потому что вы сравниваете indexPath с целым числом. Вы также должны инициализировать выбранную переменную RowIndex значением строки, которое не может быть в таблице, например -1, поэтому при первой загрузке таблицы ничего не будет расширяться.

var selectedRowIndex: NSIndexPath = NSIndexPath(forRow: -1, inSection: 0)

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    if indexPath.row == selectedRowIndex.row {
        return 100
    }
    return 70
}

Ответ 2

Мне потребовалось немало времени, чтобы заставить это работать. Вот как я это решил.

PS: проблема с кодом @rdelmar заключается в том, что он предполагает, что у вас есть только один section в вашей таблице, поэтому он сравнивает только indexPath.row. Если у вас есть несколько разделов (или если вы хотите уже учитывать расширение кода позже), вы должны сравнить весь индекс, например:

1) Вам нужна переменная, чтобы указать, какая строка выбрана. Я вижу, вы уже это сделали, но вам нужно вернуть переменную в согласованное состояние "ничего не выбрано" (когда пользователь закрывает все ячейки). Я считаю, что лучший способ сделать это - через необязательный:

var selectedIndexPath: NSIndexPath? = nil

2) Вам нужно определить, когда пользователь выбирает ячейку. didSelectRowAtIndexPath - очевидный выбор. Вам нужно учитывать три возможных результата:

  1. пользователь нажимает на ячейку, а другая ячейка расширяется
  2. пользователь нажимает на ячейку, и никакая ячейка не расширяется
  3. пользователь нажимает на ячейку, которая уже расширена

Для каждого случая мы проверяем, равен ли выбранныйIndexPath равным nil (без расширения ячейки), равный indexPath из столбчатой строки (эта же ячейка уже расширена) или отличается от indexPath (другая ячейка расширяется). Мы соответствующим образом настраиваем selectedIndexPath. Эта переменная будет использоваться для проверки правильного rowHeight для каждой строки. Вы упомянули в комментариях, что didSelectRowAtIndexPath "похоже, не называется". Вы используете println() и проверяете консоль, чтобы узнать, вызвана ли она? Я включил его в код ниже.

PS: это не работает с использованием tableView.rowHeight потому что, по-видимому, rowHeight проверяется только один раз Swift перед обновлением всех строк в таблицеView.

И последнее, но не менее важное: я использую reloadRowsAtIndexPath для перезагрузки только необходимых строк. Но, также, потому что я знаю, что он будет перерисовывать таблицу, ретранслировать при необходимости и даже анимировать изменения. Обратите внимание, что [indexPath] находится между скобками, потому что этот метод запрашивает массив NSIndexPath:

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        println("didSelectRowAtIndexPath was called")
        var cell = tableView.cellForRowAtIndexPath(indexPath) as! MyCustomTableViewCell
        switch selectedIndexPath {
        case nil:
            selectedIndexPath = indexPath
        default:
            if selectedIndexPath! == indexPath {
                selectedIndexPath = nil
            } else {
                selectedIndexPath = indexPath
            }
        }
        tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}

3) Третий и последний шаг, Свифт должен знать, когда передавать каждое значение на высоту ячейки. Здесь мы выполняем аналогичную проверку, если /else. Я знаю, что вы можете сделать код намного короче, но я набираю все, чтобы другие люди могли легко это понять:

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        let smallHeight: CGFloat = 70.0
        let expandedHeight: CGFloat = 100.0
        let ip = indexPath
        if selectedIndexPath != nil {
            if ip == selectedIndexPath! {
                return expandedHeight
            } else {
                return smallHeight
            }
        } else {
            return smallHeight
        }
    }

Теперь некоторые заметки о вашем коде, которые могут быть причиной ваших проблем, если вышеупомянутое не решает проблему:

var cell:CustomTransactionTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as CustomTransactionTableViewCell

Я не знаю, нужна ли эта проблема, но self не должен, потому что вы, вероятно, помещаете этот код в свой (Custom) TableViewController. Кроме того, вместо указания типа переменной вы можете доверять указателю Swift, если вы правильно принудительно отбросили ячейку от детектора. Это кастование - это as! в приведенном ниже коде:

var cell = tableView.dequeueReusableCellWithIdentifier("CellIdentifier" forIndexPath: indexPath) as! CustomTransactionTableViewCell

Однако вам необходимо установить этот идентификатор. Перейдите в свою раскадровку, выберите таблицуView, у которой есть ячейка, в которой вы нуждаетесь, для подкласса TableViewCell, который вам нужен (вероятно, CustomTransactionTableViewCell, в вашем случае). Теперь выберите ячейку в TableView (убедитесь, что вы выбрали правильный элемент. Лучше всего открыть контур документа через Редактор> Показать структуру документа). С выбранной ячейкой перейдите в Инспектор атрибутов справа и введите имя Identifier.

Вы также можете попробовать комментировать cell.selectionStyle = UITableViewCellSelectionStyle.None чтобы проверить, не блокирует ли это выбор каким-либо образом (таким образом, ячейки будут менять цвет при нажатии, если они будут выбраны).

Удачи, приятель.

Ответ 3

Другой подход заключается в том, чтобы нажать новый контроллер представления в стеке навигации и использовать переход для расширяющегося эффекта. Преимуществом будет SoC (разделение проблем). Пример Swift 2.0 для обоих шаблонов.

Ответ 4

Я предлагаю решить это с ограничением макета высот modyfing

class ExpandableCell: UITableViewCell {

@IBOutlet weak var img: UIImageView!


@IBOutlet weak var imgHeightConstraint: NSLayoutConstraint!


var isExpanded:Bool = false
    {
    didSet
    {
        if !isExpanded {
            self.imgHeightConstraint.constant = 0.0

        } else {
            self.imgHeightConstraint.constant = 128.0
        }
    }
}

}

Затем внутри ViewController:

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
    super.viewDidLoad()
    self.tableView.delegate = self
    self.tableView.dataSource = self
    self.tableView.estimatedRowHeight = 2.0
    self.tableView.rowHeight = UITableViewAutomaticDimension
    self.tableView.tableFooterView = UIView()
}


// TableView DataSource methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 3
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell:ExpandableCell = tableView.dequeueReusableCell(withIdentifier: "ExpandableCell") as! ExpandableCell
    cell.img.image = UIImage(named: indexPath.row.description)
    cell.isExpanded = false
    return cell
}

// TableView Delegate methods
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    guard let cell = tableView.cellForRow(at: indexPath) as? ExpandableCell
        else { return }

        UIView.animate(withDuration: 0.3, animations: {
            tableView.beginUpdates()
            cell.isExpanded = !cell.isExpanded
            tableView.scrollToRow(at: indexPath, at: UITableViewScrollPosition.top, animated: true)
            tableView.endUpdates()

        })

    }




func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
    guard let cell = tableView.cellForRow(at: indexPath) as? ExpandableCell
        else { return }
    UIView.animate(withDuration: 0.3, animations: {
        tableView.beginUpdates()
        cell.isExpanded = false
        tableView.endUpdates()
    })
}
}

Полный учебник доступен здесь

Ответ 5

После получения указательного пути в didSelectRowAtIndexPath просто перезагрузите ячейку следующим методом reloadCellsAtIndexpath и в heightForRowAtIndexPathMethod проверьте следующее условие

if selectedIndexPath != nil && selectedIndexPath == indexPath {    
       return yourExpandedCellHieght
}