Как прокручивать ячейки в uitableview влево и вправо, показывая изображение слева и изображение справа

Как показано на рисунке, мне нужно добиться того, чтобы прокручивать левую кнопку, появляется кнопка с изображением, синяя, а когда прокручивается, появляется зеленая кнопка, как это сделать? я использую swift и xcode 6.4

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

введите описание изображения здесь

Ответы

Ответ 1

Вы можете подклассифицировать UITableViewCell, чтобы включить UIPanGestureRecognizer, который управляет фреймом ячейки contentView и добавлять ваши кнопки за contentView.

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

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

введите описание изображения здесь

//
//  MWSwipeableTableViewCell.swift
//  MW UI Toolkit
//
//  Created by Jan Greve on 02.12.14.
//  Copyright (c) 2014 Markenwerk GmbH. All rights reserved.
//

import UIKit

protocol MWSwipeableTableViewCellDelegate : NSObjectProtocol {
  func swipeableTableViewCellDidRecognizeSwipe(cell : MWSwipeableTableViewCell)
  func swipeableTableViewCellDidTapLeftButton(cell : MWSwipeableTableViewCell)
  func swipeableTableViewCellDidTapRightButton(cell : MWSwipeableTableViewCell)
}

class MWSwipeableTableViewCell: UITableViewCell {
  weak var delegate : MWSwipeableTableViewCellDelegate?
  var animationOptions : UIViewAnimationOptions = [.AllowUserInteraction, .BeginFromCurrentState]
  var animationDuration : NSTimeInterval = 0.5
  var animationDelay : NSTimeInterval = 0
  var animationSpingDamping : CGFloat = 0.5
  var animationInitialVelocity : CGFloat = 1
  private weak var leftWidthConstraint : NSLayoutConstraint!
  private weak var rightWidthConstraint : NSLayoutConstraint!
  var buttonWidth :CGFloat = 80 {
    didSet(val) {
      if let r = self.rightWidthConstraint {
        r.constant = self.buttonWidth
      }
      if let l = self.leftWidthConstraint {
        l.constant = self.buttonWidth
      }
    }
  }
  private weak var panRecognizer : UIPanGestureRecognizer!
  private weak var buttonCancelTap : UITapGestureRecognizer!

  private var beginPoint : CGPoint = CGPointZero
  weak var rightButton : UIButton! {
    willSet(val) {
      if let r = self.rightButton {
        r.removeFromSuperview()
      }
      if let b = val {
        self.addSubview(b)
        b.addTarget(self, action: "didTapButton:", forControlEvents: .TouchUpInside)
        b.translatesAutoresizingMaskIntoConstraints = false
        self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-(0)-[v]-(0)-|", options: [], metrics: nil, views: ["v":b]))
        self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("[v]-(0)-|", options: [], metrics: nil, views: ["v":b]))
        let wc = NSLayoutConstraint(item: b, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: self.buttonWidth)
        b.addConstraint(wc)
        self.rightWidthConstraint = wc
        self.sendSubviewToBack(b)
      }
    }
  }
  weak var leftButton : UIButton! {
    willSet(val) {
      if let l = self.leftButton {
        l.removeFromSuperview()
      }
      if let b = val {
        self.addSubview(b)
        b.addTarget(self, action: "didTapButton:", forControlEvents: .TouchUpInside)
        b.translatesAutoresizingMaskIntoConstraints = false
        self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-(0)-[v]-(0)-|", options: [], metrics: nil, views: ["v":b]))
        self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|-(0)-[v]", options: [], metrics: nil, views: ["v":b]))
        let wc = NSLayoutConstraint(item: b, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: self.buttonWidth)
        b.addConstraint(wc)
        self.leftWidthConstraint = wc
        self.sendSubviewToBack(b)
      }
    }
  }

  override func awakeFromNib() {
    super.awakeFromNib()
  }

  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    commonInit()
  }

  override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    commonInit()
  }

  private func commonInit() {

    let pan = UIPanGestureRecognizer(target: self, action: "didPan:")
    pan.delegate = self
    self.addGestureRecognizer(pan)
    self.panRecognizer = pan

    let tap = UITapGestureRecognizer(target: self, action: "didTap:")
    tap.delegate = self
    self.addGestureRecognizer(tap)
    self.buttonCancelTap = tap

    self.contentView.backgroundColor = UIColor.clearColor()
  }


  override func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
    if let tap = gestureRecognizer as? UITapGestureRecognizer {
      if tap == self.buttonCancelTap {
                return self.contentView.frame.origin.x != 0
        }
      else {
        return super.gestureRecognizerShouldBegin(gestureRecognizer)
      }
    }
    else if let pan = gestureRecognizer as? UIPanGestureRecognizer {
      let trans = pan.translationInView(self)
      if abs(trans.x) > abs(trans.y) {
        return true
      }
      else if self.contentView.frame.origin.x != 0 {
        return true
      }
      else {
        return false
      }
    }
    else {
      return super.gestureRecognizerShouldBegin(gestureRecognizer)
    }
  }


  func didTap(sender : UITapGestureRecognizer) {
    UIView.animateWithDuration(self.animationDuration, delay: self.animationDelay, usingSpringWithDamping: self.animationSpingDamping, initialSpringVelocity: self.animationInitialVelocity, options: self.animationOptions, animations: { () -> Void in
      self.contentView.frame.origin.x = 0
      }, completion: nil)
  }

  func didPan(sender: UIPanGestureRecognizer) {
    switch sender.state {
    case .Began:
        self.delegate?.swipeableTableViewCellDidRecognizeSwipe(self)
      self.beginPoint = sender.locationInView(self)
      self.beginPoint.x -= self.contentView.frame.origin.x

    case .Changed:
      let now = sender.locationInView(self)
      let distX = now.x - self.beginPoint.x
      if distX <= 0 {
        let d = max(distX,-(self.contentView.frame.size.width-self.buttonWidth))
        if d > -self.buttonWidth*2 || self.rightButton != nil || self.contentView.frame.origin.x > 0 {
          self.contentView.frame.origin.x = d
        }
        else {
          sender.enabled = false
          sender.enabled = true
        }
      }
      else {
        let d = min(distX,self.contentView.frame.size.width-self.buttonWidth)
        if d < self.buttonWidth*2 || self.leftButton != nil || self.contentView.frame.origin.x < 0 {
          self.contentView.frame.origin.x = d
        }
        else {
          sender.enabled = false
          sender.enabled = true
        }
      }

    default:
        delegate?.swipeableTableViewCellDidRecognizeSwipe(self)
      let offset = self.contentView.frame.origin.x
      if offset > self.buttonWidth && self.leftButton != nil {
        UIView.animateWithDuration(self.animationDuration, delay: self.animationDelay, usingSpringWithDamping: self.animationSpingDamping, initialSpringVelocity: self.animationInitialVelocity, options: self.animationOptions, animations: { () -> Void in
          self.contentView.frame.origin.x = self.buttonWidth
          }, completion: nil)
      }
      else if -offset > self.buttonWidth && self.rightButton != nil {
        UIView.animateWithDuration(self.animationDuration, delay: self.animationDelay, usingSpringWithDamping: self.animationSpingDamping, initialSpringVelocity: self.animationInitialVelocity, options: self.animationOptions, animations: { () -> Void in
          self.contentView.frame.origin.x = -self.buttonWidth
          }, completion: nil)
      }
      else {
        UIView.animateWithDuration(self.animationDuration, delay: self.animationDelay, usingSpringWithDamping: self.animationSpingDamping, initialSpringVelocity: self.animationInitialVelocity, options: self.animationOptions, animations: { () -> Void in
          self.contentView.frame.origin.x = 0
          }, completion: nil)
      }
    }
    }

  func closeButtonsIfShown(animated:Bool = true) -> Bool {
    if self.contentView.frame.origin.x != 0 {
      if animated {
        UIView.animateWithDuration(self.animationDuration, delay: self.animationDelay, usingSpringWithDamping: self.animationSpingDamping, initialSpringVelocity: self.animationInitialVelocity, options: self.animationOptions, animations: { () -> Void in
          self.contentView.frame.origin.x = 0
          self.panRecognizer.enabled = false
          self.panRecognizer.enabled = true
          }, completion: nil)
      }
      else {
        self.contentView.frame.origin.x = 0
        self.panRecognizer.enabled = false
        self.panRecognizer.enabled = true

      }
      return true
    }
    else {
      return false
    }
  }

  func didTapButton(sender:UIButton!) {
    if let d = delegate {
      if let l = self.leftButton {
        if sender == l {
          d.swipeableTableViewCellDidTapLeftButton(self)
        }
      }
      if let r = self.rightButton {
        if sender == r {
          d.swipeableTableViewCellDidTapRightButton(self)
        }
      }
    }
    self.closeButtonsIfShown(false)
  }

  override func setHighlighted(highlighted: Bool, animated: Bool) {
    let showing = self.contentView.frame.origin.x != 0
    if !showing {
      super.setHighlighted(highlighted, animated: animated)
      self.rightButton?.alpha = showing || !highlighted ? 1 : 0
      self.leftButton?.alpha = showing || !highlighted ? 1 : 0
    }
  }

  override func setSelected(selected: Bool, animated: Bool) {
    let showing = self.contentView.frame.origin.x != 0
    if !showing {
      super.setSelected(selected, animated: animated)
      self.rightButton?.alpha = showing || !selected ? 1 : 0
      self.leftButton?.alpha = showing || !selected ? 1 : 0
    }
  }
}

Ответ 2

Есть много способов пойти на это, вы могли бы изучить одну из многих существующих библиотек, таких как BMXSwipeableCell от Massimiliano Bigatti https://github.com/mbigatti/BMXSwipableCell, где вы можете либо посмотреть исходный код, либо скопировать его полностью.

Другой подход - чтение одного из следующих двух замечательных уроков:

Как сделать Swipeable TableViewCell с действиями - без использования прокрутки в орехах - от Ellen Shapiro: https://www.raywenderlich.com/62435/make-swipeable-table-view-cell-actions-without-going-nuts-scroll-views

Как сделать приложение для списка дел, выполняемое с помощью жестов, как Clear in Swift - от Audrey Tam: https://www.raywenderlich.com/77974/making-a-gesture-driven-to-do-list-app-like-clear-in-swift-part-1

Чтобы дать вам краткое представление, суть такова:

я. Создайте пользовательский TableViewCell

II. Добавьте UIPangestureRecognizer

III. Сохраните ссылку на исходный кадр contentView, это необходимо, так как вы будете прокручивать (панорамирование) этого contentView влево и вправо. И сохраните ссылку на исходный центр ячейки.

IV. Добавьте изображения, кнопки или другие виды, которые вы хотите отобразить в ячейке

Что произойдет, так это то, что вы будете направлять внешний вид вашей прокручиваемой ячейки через три этапа UIPangestureRecognizer: UIGestureStateBegan, UIGestureStateChanged, UIGestureStateEnded

  • UIGestureStateBegan: сначала проверьте, является ли это горизонтальным, а не вертикальным в UIGestureDelegate. Мы делаем это, чтобы не путать UITableView, который, как вы могли бы помнить, прокручивать по вертикали. Затем получите ссылку на исходный центр ячейки.

  • UIGestureStateChanged: Когда пользователь перемещает палец влево или вправо, нам нужно обновить внешний вид ячейки. Перемещая центр ячейки contentView's, используя исходную центральную ссылку, и движение, данное нам жестом, мы получаем именно то поведение, которое хотим достичь.

  • UIGestureStateEnded: Здесь нам нужно решить, хотим ли мы сохранить отображаемое изображение, кнопку и т.д. в виду после того, как пользователь выпустит ячейку, или если мы хотим "привязать" назад. Порог для этого действительно зависит, но будет какое-то процентное изменение влево или вправо по сравнению с общей шириной ячейки. Если вы хотите "привязать" назад, просто установите рамку contentView в исходный кадр, на который мы ссылались. Если нет, установите его в смещение, которое красиво отобразит содержимое, которое вы хотите раскрыть.

Надеюсь, что это помогло понять концепцию, пожалуйста, ознакомьтесь с одним из двух замечательных уроков для более подробного объяснения!

Ответ 3

Я мог бы добиться того же результата, используя распознаватель жестов. Надеюсь, это поможет.

  • Создайте таблицу с ячейкой прототипа.
  • Добавить кнопку как с левой, так и с правой стороны ячейки с изображением для состояния управления по умолчанию (вы можете изменить изображение на основе состояния управления состоянием).
  • Добавьте представление контейнера (здесь это mainView) поверх ячейки, охватывающей всю область ячеек.
  • Создайте TableViewCustomCell, выполнив подкласс UITableViewCell
  • Измените класс ячейки прототипа на пользовательский TableViewCustomCell
  • Используйте следующий код в TableViewCustomCell

Swift Code:

    import UIKit

    class TableViewCustomCell:UITableViewCell {

    @IBOutlet weak var rightButton: UIButton!
    @IBOutlet weak var leftButton: UIButton!
    @IBOutlet weak var mainView: UIView!
    @IBAction func leftButtonTap(sender: AnyObject) {
        print("leftTap")
    }

    @IBAction func rightButtonTap(sender: AnyObject) {
         print("rightTap")
    }

    override func awakeFromNib() {
        let leftSwipe = UISwipeGestureRecognizer(target: self, action:Selector("swipe:"))
        leftSwipe.direction = .Left;
        self.mainView.addGestureRecognizer(leftSwipe)

        let rightSwipe = UISwipeGestureRecognizer(target: self, action:Selector("swipe:"))
        rightSwipe.direction = .Right;
        self.mainView.addGestureRecognizer(rightSwipe)
    }

    func swipe(sender:AnyObject)
    {
        let swipeGesture:UISwipeGestureRecognizer = sender as! UISwipeGestureRecognizer
        if(swipeGesture.direction == .Left)
        {
            var frame:CGRect = self.mainView.frame;
            frame.origin.x = -self.leftButton.frame.width;
            self.mainView.frame = frame;
        }
        else if(swipeGesture.direction == .Right)
        {
            var frame:CGRect = self.mainView.frame;
            frame.origin.x = +self.rightButton.frame.width;
            self.mainView.frame = frame;
        }

    }
}

Ответ 4

Если вы хотите использовать библиотеку для такой функции, я бы предложил вам использовать https://github.com/MortimerGoro/MGSwipeTableCell...

его простая в использовании и удобная настройка.

Ответ 5

Общая идея прост - представление содержимого вашей ячейки - это UIScrollView с представлениями по сторонам.

Однако полное рабочее решение немного сложнее и, вероятно, слишком велико для ответа.

Я бы рекомендовал вам начать с уже реализованного решения, например. SWTableViewCell (но есть и другие) и посмотрите исходный код. Или просто используйте его напрямую. Большинство решений можно установить с помощью cocoapods, и они работают как в Swift, так и в Objective-C.