0

enter image description hereI am trying to implement swipe to delete feature with two options, one is to delete and another one is to edit. The things I want is these options should be vertical rather than horizontal. Thanks in advance for support.

PuNeet Pal
  • 59
  • 7

2 Answers2

0

You can easily achieve this swipe to reveal option feature using Custom TablViewCell

Design a view with two buttons and add a swipe gesture to the view to reveal the vertically aligned buttons

Jarvis The Avenger
  • 2,750
  • 1
  • 19
  • 37
0

Anyway, I think you would rather use default method editActionsForRowAt for similar cases. If not I hope this code will help you.

class TableViewController: UIViewController {

let tableView: UITableView = {
    let tableView = UITableView()
    tableView.translatesAutoresizingMaskIntoConstraints = false
    tableView.register(CustomCell.self,
                              forCellReuseIdentifier: CustomCell.identifier)
    
    return tableView
}()

var selectedCell: CustomCell?

override func viewDidLoad() {
    super.viewDidLoad()
    
    view.backgroundColor = .white
    
    view.addSubview(tableView)
    setupConstraints()
    
    tableView.dataSource = self
    tableView.delegate = self
    tableView.reloadData()
}

func setupConstraints() {
    NSLayoutConstraint.activate([
        tableView.topAnchor.constraint(equalTo: view.topAnchor),
        tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
        tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
        tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
    ])
}
}

extension TableViewController: UITableViewDelegate & UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    2
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: CustomCell.identifier, for: indexPath) as? CustomCell else {
        return UITableViewCell()
    }
    
    cell.delegate = self
    
    return cell
}

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

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    selectedCell?.setInitialState()
}
}

extension TableViewController: CustomCellDelegate {
func didUpdateState(customCell: CustomCell?) {
    if customCell != selectedCell {
        selectedCell?.setInitialState()
    }
    selectedCell = customCell
}
}

protocol CustomCellDelegate: class {
    func didUpdateState(customCell: CustomCell?)
}

class CustomCell: UITableViewCell {

weak var delegate: CustomCellDelegate?

static let identifier = "Cell"

private let customViewWidth: CGFloat = 100

private let customView: CustomView = {
    let view = CustomView()
    view.translatesAutoresizingMaskIntoConstraints = false
    view.backgroundColor = .yellow
    return view
}()

private let fakeContentView: UIView = {
    let view = UIView()
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
}()

enum CustomCellState {
    case hiddenCustomView
    case showedCustomView
}

private var trailingConstraint = NSLayoutConstraint()
private var cellState: CustomCellState = .hiddenCustomView

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    
    backgroundColor = .yellow
    
    let panGesture = UIPanGestureRecognizer(target: self, action:(#selector(handleGesture(_:))))
    fakeContentView.addGestureRecognizer(panGesture)
    
    contentView.addSubview(fakeContentView)
    fakeContentView.addSubview(customView)
    setConstraints()
    updateCellState()
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

func setInitialState() {
    self.trailingConstraint.constant = 0
    UIView.animate(withDuration: 0.5, animations: {
        self.contentView.layoutIfNeeded()
    })
}

@objc private func handleGesture(_ recognizer: UIPanGestureRecognizer) {
    let location = recognizer.location(in: fakeContentView)
    let dx = frame.width - location.x
    
    updateFrame(deltaX: dx)
    
    if recognizer.state == .ended {
        cellState = dx > customViewWidth / 2 ? .showedCustomView : .hiddenCustomView
        updateCellState()
        delegate?.didUpdateState(customCell: self)
    }
}

private func updateFrame(deltaX: CGFloat) {
    
    guard abs(deltaX) <= customViewWidth else {
        return
    }
    
    trailingConstraint.constant = -deltaX
    contentView.layoutIfNeeded()
}

private func updateCellState() {
    let dx: CGFloat = cellState == .hiddenCustomView ? 0 : customViewWidth
    self.trailingConstraint.constant = -dx
    
    UIView.animate(withDuration: 0.5, animations: {
        self.contentView.layoutIfNeeded()
    }, completion: nil)
}

private func setConstraints() {
    trailingConstraint = fakeContentView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
    trailingConstraint.isActive = true
     
    NSLayoutConstraint.activate([
        fakeContentView.topAnchor.constraint(equalTo: contentView.topAnchor),
        fakeContentView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
        fakeContentView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
        
        customView.topAnchor.constraint(equalTo: fakeContentView.topAnchor),
        customView.bottomAnchor.constraint(equalTo: fakeContentView.bottomAnchor),
        customView.leadingAnchor.constraint(equalTo: fakeContentView.trailingAnchor),
        customView.widthAnchor.constraint(equalToConstant: customViewWidth)
    ])
}
}

class CustomView: UIView {

private let button1: UIButton = {
    let button = UIButton(type: .custom)
    button.backgroundColor = .blue
    button.translatesAutoresizingMaskIntoConstraints = false
    return button
}()

private let button2: UIButton = {
    let button = UIButton(type: .custom)
    button.backgroundColor = .black
    button.translatesAutoresizingMaskIntoConstraints = false
    return button
}()

override init(frame: CGRect) {
    super.init(frame: frame)
    backgroundColor = .gray
    addSubview(button1)
    addSubview(button2)
    setConstraints()
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

private func setConstraints() {
     
    NSLayoutConstraint.activate([
        button1.topAnchor.constraint(equalTo: topAnchor),
        button1.leadingAnchor.constraint(equalTo: leadingAnchor),
        button1.trailingAnchor.constraint(equalTo: trailingAnchor),
        button1.bottomAnchor.constraint(equalTo: centerYAnchor),
        
        button2.bottomAnchor.constraint(equalTo: bottomAnchor),
        button2.leadingAnchor.constraint(equalTo: leadingAnchor),
        button2.trailingAnchor.constraint(equalTo: trailingAnchor),
        button2.topAnchor.constraint(equalTo: button1.bottomAnchor)
    ])
}
}