如图所示,我需要实现的是向左滑动,显示图像的按钮,蓝色按钮,向右滑动绿色按钮时,如何执行此操作?我使用swift和xcode 6.4
这是我在问之前尝试过的,我能够在单元格右侧显示两个带文本的选项,但我不想要那个,图中需要的是,如上所述,按钮需要是图像而不是文字。
答案 0 :(得分:18)
您可以将UITableViewCell
子类化为合并UIPanGestureRecognizer
来操纵单元格的contentView
框架,并在contentView
后面添加按钮。
为了了解这是如何工作的详细信息,我在下面添加了有关如何执行此操作的示例代码以供参考。这也增加了一个轻敲手势识别器来关闭'点击操作而不是选择单元格。
此外,根据评论中的要求,这里有一个如何工作的GIF(显示侧面按钮的颜色作为行动的指示,但您可以轻松修改contentView
' s框架与子类中的按钮完全重叠。)
//
// 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
}
}
}
答案 1 :(得分:4)
我可以使用滑动手势识别器获得相同的结果。希望这会有所帮助。
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;
}
}
}
答案 2 :(得分:4)
有很多方法可以解决这个问题,您可以查看许多现有库中的一个,例如Massimiliano Bigatti https://github.com/mbigatti/BMXSwipableCell的BMXSwipeableCell,您可以在其中查看源代码或完全复制它。
另一种方法是阅读以下两个很棒的教程之一:
如何使用动作制作可滑动的TableViewCell - 不使用坚果ScrollViews - 作者:Ellen Shapiro: https://www.raywenderlich.com/62435/make-swipeable-table-view-cell-actions-without-going-nuts-scroll-views
如何制作一个手势驱动的待办事项列表应用程序,如Swift中的Clear - 作者: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
的{{1}}的三个阶段指导可滑动单元格的外观:UIGestureStateBegan, UIGestureStateChanged, UIGestureStateEnded
UIGestureStateBegan
:首先检查这是一个水平平移,而不是UIGestureDelegate
中的垂直平移。我们这样做是为了不混淆UITableView,你可能还记得它是垂直滚动的。然后获取对单元格原始中心的引用。
UIGestureStateChanged
:当用户将手指向左或向右移动时,我们需要更新单元格的外观。通过使用原始中心参考移动单元格的contentView's
中心,以及通过手势给我们的移动,我们可以获得我们希望实现的行为。
UIGestureStateEnded
:在这里,我们需要决定是否要在用户释放单元格后保留显示的图像,按钮等,或者我们是否要“快速”回来。这个阈值确实达到,但是与单元格的总宽度相比,左边或右边会有某种百分比变化。如果你想“反弹”,只需将contentView
的帧设置为我们保留引用的原始帧即可。如果没有,请将其设置为一个偏好,以便很好地显示您希望显示的内容。
我希望这有助于理解这个概念,请查看其中一个令人惊叹的教程,以获得更详细的解释!
答案 3 :(得分:1)
如果你想使用库来实现这样的功能,我建议你使用 https://github.com/MortimerGoro/MGSwipeTableCell ......
易于使用且易于定制。
答案 4 :(得分:0)
总体思路很简单 - 您的单元格内容视图是UIScrollView
,其中包含两侧的视图。
然而,完整的工作解决方案有点复杂,可能过于宽泛而无法回答。
我建议您从已经实施的解决方案开始,例如: SWTableViewCell(但还有其他人)并查看源代码。或者直接使用它。大多数解决方案都可以使用cocoapods
安装,它们可以在Swift和Objective-C中使用。
答案 5 :(得分:-2)
基于@Tobi Nary的答案,这是4.2的快速版本。
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 : UIView.AnimationOptions = [.allowUserInteraction, .beginFromCurrentState]
var animationDuration : TimeInterval = 0.5
var animationDelay : TimeInterval = 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 = CGPoint.zero
weak var rightButton : UIButton! {
willSet(val) {
if let r = self.rightButton {
r.removeFromSuperview()
}
if let b = val {
self.addSubview(b)
b.addTarget(self, action: Selector(("didTapButton:")), for: .touchUpInside)
b.translatesAutoresizingMaskIntoConstraints = false
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-(0)-[v]-(0)-|", options: [], metrics: nil, views: ["v":b]))
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "[v]-(0)-|", options: [], metrics: nil, views: ["v":b]))
let wc = NSLayoutConstraint(item: b, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.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: Selector("didTapButton:"), for: .touchUpInside)
b.translatesAutoresizingMaskIntoConstraints = false
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-(0)-[v]-(0)-|", options: [], metrics: nil, views: ["v":b]))
self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "|-(0)-[v]", options: [], metrics: nil, views: ["v":b]))
let wc = NSLayoutConstraint(item: b, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.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: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
private func commonInit() {
let pan = UIPanGestureRecognizer(target: self, action: Selector(("didPan:")))
pan.delegate = self
self.addGestureRecognizer(pan)
self.panRecognizer = pan
let tap = UITapGestureRecognizer(target: self, action: Selector(("didTap:")))
tap.delegate = self
self.addGestureRecognizer(tap)
self.buttonCancelTap = tap
self.contentView.backgroundColor = UIColor.clear
}
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.translation(in: 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.animate(withDuration: 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(cell: self)
self.beginPoint = sender.location(in: self)
self.beginPoint.x -= self.contentView.frame.origin.x
case .changed:
let now = sender.location(in: 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.isEnabled = false
sender.isEnabled = 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.isEnabled = false
sender.isEnabled = true
}
}
default:
delegate?.swipeableTableViewCellDidRecognizeSwipe(cell: self)
let offset = self.contentView.frame.origin.x
if offset > self.buttonWidth && self.leftButton != nil {
UIView.animate(withDuration: 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.animate(withDuration: 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.animate(withDuration: 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.animate(withDuration: 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.isEnabled = false
self.panRecognizer.isEnabled = true
}, completion: nil)
}
else {
self.contentView.frame.origin.x = 0
self.panRecognizer.isEnabled = false
self.panRecognizer.isEnabled = true
}
return true
}
else {
return false
}
}
func didTapButton(sender:UIButton!) {
if let d = delegate {
if let l = self.leftButton {
if sender == l {
d.swipeableTableViewCellDidTapLeftButton(cell: self)
}
}
if let r = self.rightButton {
if sender == r {
d.swipeableTableViewCellDidTapRightButton(cell: self)
}
}
}
self.closeButtonsIfShown(animated: 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
}
}
}