Ответ 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
}
}
}