UITableView вызывает программные действия

У меня есть UITableView, где пользователь может прокручивать влево, чтобы выявлять действия (например, в почте iOS 8). Все работает так, как ожидалось. Я хочу вызвать это, когда пользователь нажимает на определенную часть ячейки. Как я могу вызвать это действие слайда программно?

Текущее поведение: пользователь должен пролистнуть ячейку, оставшуюся для раскрытия кнопок действий.

Желаемое поведение: пользовательские нажатия (кнопка действий) в ячейке. Ячейка соскальзывает для раскрытия кнопок действий.

Ответ 1

Ну, я не мог найти способ сделать это программно, но я придумал это обходное решение. Когда пользователь отбирает ячейку, я анимирует (панорамирует) ее влево, чтобы мгновенно выявить кнопку "Проведите меня по экрану". Это быстро отменяется, поэтому ячейка возвращается в нормальное состояние. Это дает визуальную подсказку, позволяющую пользователю узнать, что они могут пронести по ячейке:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

    __block UILabel *swipeLabel = [[UILabel alloc]initWithFrame:CGRectMake(cell.bounds.size.width,
                                                                   0,
                                                                   200,
                                                                   cell.bounds.size.height)];

    swipeLabel.text = @"  Swipe Me";
    swipeLabel.backgroundColor = [UIColor greenColor];
    swipeLabel.textColor = [UIColor whiteColor];
    [cell addSubview:swipeLabel];

    [UIView animateWithDuration:0.3 animations:^{
        [cell setFrame:CGRectMake(cell.frame.origin.x - 100, cell.frame.origin.y, cell.bounds.size.width, cell.bounds.size.height)];
    } completion:^(BOOL finished) {
        [UIView animateWithDuration:0.3 animations:^{
            [cell setFrame:CGRectMake(cell.frame.origin.x + 100, cell.frame.origin.y, cell.bounds.size.width, cell.bounds.size.height)];
        } completion:^(BOOL finished) {
            [swipeLabel removeFromSuperview];
            swipeLabel = nil;
        }];
    }];
}

Надеюсь, это поможет кому-то.

Обратите внимание, что вам нужно установить тип выбора tableViewCell равным none. Иначе серая полоса затмит ее.

Обновление. Я думал, что опубликую более Swifty версию:

func previewActions(forCellAt indexPath: IndexPath) {
    guard let cell = tableView.cellForRow(at: indexPath) else {
        return
    }

    let label: UILabel = {
        let label = UILabel(frame: CGRect.zero)
        label.text = "  Swipe Me  "
        label.backgroundColor = .blue
        label.textColor = .white
        return label
    }()

    // Figure out the best width and update label.frame
    let bestSize = label.sizeThatFits(label.frame.size)
    label.frame = CGRect(x: cell.bounds.width - bestSize.width, y: 0, width: bestSize.width, height: cell.bounds.height)
    cell.insertSubview(label, belowSubview: cell.contentView)

    UIView.animate(withDuration: 0.3, animations: {
        cell.transform = CGAffineTransform.identity.translatedBy(x: -label.bounds.width, y: 0)
        label.transform = CGAffineTransform.identity.translatedBy(x: label.bounds.width, y: 0)
    }) { (finished) in
        UIView.animateKeyframes(withDuration: 0.3, delay: 0.25, options: [], animations: {
            cell.transform = CGAffineTransform.identity
            label.transform = CGAffineTransform.identity
        }, completion: { (finished) in
            label.removeFromSuperview()
        })
    }
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    previewActions(forCellAt: indexPath)
    return
}

Ответ 2

Для тех, кто ищет версию VaporwareWolf от Swift, вот она:

func animateRevealHideActionForRow(tableView: UITableView, indexPath: IndexPath) {
    let cell = tableView.cellForRow(at: indexPath)

    // Should be used in a block
    var swipeLabel: UILabel? = UILabel.init(frame: CGRect(x: cell!.bounds.size.width,
                                                          y: 0,
                                                          width: 200,
                                                          height: cell!.bounds.size.height))

    swipeLabel!.text = "  Swipe Me";
    swipeLabel!.backgroundColor = UIColor.init(red: 255/255, green: 41/255, blue: 53/255, alpha: 1) // Red
    swipeLabel!.textColor = UIColor.white
    cell!.addSubview(swipeLabel!)

    UIView.animate(withDuration: 0.3, animations: {
        cell!.frame = CGRect(x: cell!.frame.origin.x - 100, y: cell!.frame.origin.y, width: cell!.bounds.size.width + 100, height: cell!.bounds.size.height)
    }) { (finished) in
        UIView.animate(withDuration: 0.3, animations: {

            cell!.frame = CGRect(x: cell!.frame.origin.x + 100, y: cell!.frame.origin.y, width: cell!.bounds.size.width - 100, height: cell!.bounds.size.height)

        }, completion: { (finished) in
            swipeLabel?.removeFromSuperview()
            swipeLabel = nil;
        })
    }
}

Ответ 3

И для версии Swift 3, где вы хотите вызвать эту функцию для каждой строки.

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    let cell = tableView.cellForRow(at: indexPath);
    UIView.animate(withDuration: 0.3, animations: {

        cell!.frame = CGRect(x: cell!.frame.origin.x - 100, y: cell!.frame.origin.y, width: cell!.bounds.size.width + 100, height: cell!.bounds.size.height)

    }) { (finished) in
        UIView.animate(withDuration: 0.3, animations: {

            cell!.frame = CGRect(x: cell!.frame.origin.x + 100, y: cell!.frame.origin.y, width: cell!.bounds.size.width - 100, height: cell!.bounds.size.height)

        }, completion: { (finished) in
        })
    }
}

Ответ 4

Другая измененная версия ответа VaporwareWolf, в которой показаны два прямоугольника вместо одного (прекрасно работает с iOS 11 UISwipeActionsConfiguration API). Я использую это для ячеек, которые имеют две метки, размеченные с ограничениями, и могли заставить это работать правильно только при анимации ограничений вместо реальной ячейки или меток.

func showActions(forRow row:Int) {
    guard let cell = (tableView.cellForRow(at: IndexPath(row: row, section: 0))) as? RequestTableViewCell else {
        return
    }

    let labelWidth:CGFloat = 20

    let createLabel:(UIColor)->UILabel = {
        color in
        let label = UILabel(frame: CGRect.zero)
        label.backgroundColor = color
        label.clipsToBounds = false
        label.frame = CGRect(x: cell.bounds.width, y: 0, width: labelWidth, height: cell.bounds.height)
        return label
    }

    let greenLabel = createLabel(.green)
    let redLabel = createLabel(.red)

    //ordering of the subviews is key to get it to look right
    cell.insertSubview(greenLabel, aboveSubview: cell.contentView)
    cell.insertSubview(redLabel, belowSubview: greenLabel)

    let originalLabelSeparationContstraint = cell.labelSeparationContstraint

    UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 3, options: .curveEaseOut, animations: {
        cell.leftLabelLeadingConstraint.constant -= (labelWidth * 2)
        cell.labelSeparationContstraint.constant = cell.requestAgeLabel.frame.minX - cell.nameLabel.frame.maxX
        cell.rightLabelTrailingContstraint.constant += (labelWidth * 2)
        cell.layoutIfNeeded()

        greenLabel.transform = greenLabel.transform.translatedBy(x: -(labelWidth), y: 0)
        redLabel.transform = redLabel.transform.translatedBy(x: -(labelWidth * 2), y: 0)
    }, completion: {
        _ in
        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 3, options: .curveEaseOut, animations: {
            cell.leftLabelLeadingConstraint.constant += (labelWidth * 2)
            cell.labelSeparationContstraint = originalLabelSeparationContstraint
            cell.rightLabelTrailingContstraint.constant -= (labelWidth * 2)
            cell.layoutIfNeeded()

            greenLabel.transform = CGAffineTransform.identity
            redLabel.transform = CGAffineTransform.identity
        }, completion: {
            _ in
            greenLabel.removeFromSuperview()
            redLabel.removeFromSuperview()
        })
    })
}

Ответ 5

Свифт 3 Эквивалент ответа Бурака. Вы можете программно отключить этот вызов там, где хотите, я помещаю его в вспомогательную функцию, чтобы любое представление таблицы могло показать это. И первый раз, когда пользователь использует мое приложение, я открываю его (я чувствовал, что требуется больше времени анимации).

class func animateRevealHideActionForRow(tableView: UITableView, indexPath: NSIndexPath) {
    let cell = tableView.cellForRow(at: indexPath as IndexPath);

    // Should be used in a block
    var swipeLabel: UILabel? = UILabel.init(frame: CGRect.init(x: cell!.bounds.size.width, y: 0, width: 200, height: cell!.bounds.size.height))

    swipeLabel!.text = "  Swipe Me";
    swipeLabel!.backgroundColor = UIColor.red
    swipeLabel!.textColor = UIColor.white
    cell!.addSubview(swipeLabel!)

    UIView.animate(withDuration: 1.0, animations: {
        cell!.frame = CGRect.init(x: cell!.frame.origin.x - 100, y: cell!.frame.origin.y, width: cell!.bounds.size.width + 100, height: cell!.bounds.size.height)
    }) { (finished) in
        UIView.animate(withDuration: 1.0, animations: {
            cell!.frame = CGRect.init(x: cell!.frame.origin.x + 100, y: cell!.frame.origin.y, width: cell!.bounds.size.width - 100, height: cell!.bounds.size.height)
        }, completion: { (finished) in
            swipeLabel?.removeFromSuperview()
            swipeLabel = nil;
        })
    }
}

Ответ 6

Не используйте хакерский путь и не используйте библиотеку, подобную этой, пока они не реализуют эту функцию.

https://github.com/SwipeCellKit/SwipeCellKit

Эта библиотека позволяет вам делать такие вещи, как: cell.showSwipe(orientation:.right, animated: true)