如何向 TableView 添加向左滑动手势和 return 滑动时的单元格位置 Swift 3
How to add a Swipe Left gesture to a TableView and return the cell location when swiped in Swift 3
我有一个 TableView,它已经编码为在使用 didSelectRowAt
方法选择任何单元格时执行操作。
现在,我想向 Table(或单元格)添加向左滑动手势,这样我就可以在滑动单元格而不是点击时执行辅助操作。
1) 我希望单元格在滑动时向左移动,但我不想在单元格移动的 space 中添加按钮。
2) 相反,我希望能够 'drag' 单元格离开到某个点(比如中途),然后使用 indexPath 执行辅助操作(所以我知道哪个单元格被拖走了)。
3) 如果用户停止拖动或放开单元格,我希望它 return 到它的起始位置并且没有任何动作发生。
我已经看到很多示例执行此操作的各个部分,但大多数示例都在 Obj-C 中或在与单元格相同的行中插入按钮。
还有,是不是每个单元格都加上Gesture比较好?
将它添加到 table...
似乎更聪明
编辑:请参阅下面的完整答案代码
我做了一些研究并创建了一个简单的例子来说明如何做到这一点 - 创建一个可以滑动和点击的 table 单元格。我在音乐播放器中使用它 - 点击一个单元格并播放歌曲,滑动相同的单元格并转到不同的视图。
我已经根据这两个现有示例构建了我的解决方案:
https://www.raywenderlich.com/77974/making-a-gesture-driven-to-do-list-app-like-clear-in-swift-part-1
https://gabrielghe.github.io/swift/2016/03/20/swipable-uitableviewcell
我没有要共享的存储库,所以所有代码都在这里。
我正在使用 Xcode 8.2.1 和 Swift 3
第 1 步:
- 创建一个新的单视图 Swift 项目,然后打开 Storyboard。
- 将一个 TableView 拖到现有的 ViewController 上并拖动它以适合视图。
第 2 步:
- 将新的 Swift 文件添加到项目并将其命名为 "TableViewCellSliding.swift"。
Copy/paste将下面的代码放入新文件中。
//
// TableViewCellSliding.swift
//
import UIKit
protocol SlidingCellDelegate {
// tell the TableView that a swipe happened
func hasPerformedSwipe(touch: CGPoint)
func hasPerformedTap(touch: CGPoint)
}
class SlidingTableViewCell: UITableViewCell {
var delegate: SlidingCellDelegate?
var originalCenter = CGPoint()
var isSwipeSuccessful = false
var touch = CGPoint()
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
// add a PAN gesture
let pRecognizer = UIPanGestureRecognizer(target: self, action: #selector(SlidingTableViewCell.handlePan(_:)))
pRecognizer.delegate = self
addGestureRecognizer(pRecognizer)
// add a TAP gesture
// note that adding the PAN gesture to a cell disables the built-in tap responder (didSelectRowAtIndexPath)
// so we can add in our own here if we want both swipe and tap actions
let tRecognizer = UITapGestureRecognizer(target: self, action: #selector(SlidingTableViewCell.handleTap(_:)))
tRecognizer.delegate = self
addGestureRecognizer(tRecognizer)
}
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer {
let translation = panGestureRecognizer.translation(in: superview!)
//look for right-swipe
if (fabs(translation.x) > fabs(translation.y)) && (translation.x > 0){
// look for left-swipe
//if (fabs(translation.x) > fabs(translation.y)) && (translation.x < 0){
//print("gesture 1")
touch = panGestureRecognizer.location(in: superview)
return true
}
//not left or right - must be up or down
return false
}else if gestureRecognizer is UITapGestureRecognizer {
touch = gestureRecognizer.location(in: superview)
return true
}
return false
}
func handleTap(_ recognizer: UITapGestureRecognizer){
// call function to get indexPath since didSelectRowAtIndexPath will be disabled
delegate?.hasPerformedTap(touch: touch)
}
func handlePan(_ recognizer: UIPanGestureRecognizer) {
if recognizer.state == .began {
originalCenter = center
}
if recognizer.state == .changed {
checkIfSwiped(recongizer: recognizer)
}
if recognizer.state == .ended {
let originalFrame = CGRect(x: 0, y: frame.origin.y, width: bounds.size.width, height: bounds.size.height)
if isSwipeSuccessful{
delegate?.hasPerformedSwipe(touch: touch)
//after 'short' swipe animate back to origin quickly
moveViewBackIntoPlaceSlowly(originalFrame: originalFrame)
} else {
//after successful swipe animate back to origin slowly
moveViewBackIntoPlace(originalFrame: originalFrame)
}
}
}
func checkIfSwiped(recongizer: UIPanGestureRecognizer) {
let translation = recongizer.translation(in: self)
center = CGPoint(x: originalCenter.x + translation.x, y: originalCenter.y)
//this allows only swipe-right
isSwipeSuccessful = frame.origin.x > frame.size.width / 2.0 //pan is 1/2 width of the cell
//this allows only swipe-left
//isSwipeSuccessful = frame.origin.x < -frame.size.width / 3.0 //pan is 1/3 width of the cell
}
func moveViewBackIntoPlace(originalFrame: CGRect) {
UIView.animate(withDuration: 0.2, animations: {self.frame = originalFrame})
}
func moveViewBackIntoPlaceSlowly(originalFrame: CGRect) {
UIView.animate(withDuration: 1.5, animations: {self.frame = originalFrame})
}
}
第 3 步:
Copy/paste将下面的代码放入现有文件"ViewController.swift"
//
// ViewController.swift
//
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, SlidingCellDelegate {
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableView.dataSource = self
tableView.delegate = self
tableView.register(SlidingTableViewCell.self, forCellReuseIdentifier: "cell")
tableView.rowHeight = 50;
}
func hasPerformedSwipe(touch: CGPoint) {
if let indexPath = tableView.indexPathForRow(at: touch) {
// Access the image or the cell at this index path
print("got a swipe row:\(indexPath.row)")
}
}
func hasPerformedTap(touch: CGPoint){
if let indexPath = tableView.indexPathForRow(at: touch) {
// Access the image or the cell at this index path
print("got a tap row:\(indexPath.row)")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView,numberOfRowsInSection section: Int)-> Int {
return 100
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell=tableView.dequeueReusableCell(withIdentifier: "cell",for: indexPath) as! SlidingTableViewCell
// Configure cell
cell.selectionStyle = .none
cell.textLabel?.text = "hello \(indexPath.row)"
cell.delegate = self
return cell
}
func tableView(sender: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// do stuff with indexPath.row and indexPath.section
//never make it here because we added a tap gesture but this will
print("selected cell")
}
}
第 4 步:
在情节提要中将其全部连接起来。
打开 Storyboard 视图和 select TableView。转到 Connections Inspector(圆圈中的右上角箭头)并从 New Referencing Outlet 拖到 TableView 并从弹出菜单中 select "tableView"。
在 TableView 仍然 selected 的情况下,从 Outlets > dataSource 拖动到 Storyboard 中的 TableView。从 Outlets > delegate.
开始重复
第 5 步:
- 运行啦!
我不会详细介绍任何代码,因为顶部的两个链接做得很好。这只是关于拥有可以构建的完整、简单、干净的代码。享受吧。
我有一个 TableView,它已经编码为在使用 didSelectRowAt
方法选择任何单元格时执行操作。
现在,我想向 Table(或单元格)添加向左滑动手势,这样我就可以在滑动单元格而不是点击时执行辅助操作。
1) 我希望单元格在滑动时向左移动,但我不想在单元格移动的 space 中添加按钮。
2) 相反,我希望能够 'drag' 单元格离开到某个点(比如中途),然后使用 indexPath 执行辅助操作(所以我知道哪个单元格被拖走了)。
3) 如果用户停止拖动或放开单元格,我希望它 return 到它的起始位置并且没有任何动作发生。
我已经看到很多示例执行此操作的各个部分,但大多数示例都在 Obj-C 中或在与单元格相同的行中插入按钮。
还有,是不是每个单元格都加上Gesture比较好? 将它添加到 table...
似乎更聪明编辑:请参阅下面的完整答案代码
我做了一些研究并创建了一个简单的例子来说明如何做到这一点 - 创建一个可以滑动和点击的 table 单元格。我在音乐播放器中使用它 - 点击一个单元格并播放歌曲,滑动相同的单元格并转到不同的视图。
我已经根据这两个现有示例构建了我的解决方案: https://www.raywenderlich.com/77974/making-a-gesture-driven-to-do-list-app-like-clear-in-swift-part-1
https://gabrielghe.github.io/swift/2016/03/20/swipable-uitableviewcell
我没有要共享的存储库,所以所有代码都在这里。
我正在使用 Xcode 8.2.1 和 Swift 3
第 1 步:
- 创建一个新的单视图 Swift 项目,然后打开 Storyboard。
- 将一个 TableView 拖到现有的 ViewController 上并拖动它以适合视图。
第 2 步:
- 将新的 Swift 文件添加到项目并将其命名为 "TableViewCellSliding.swift"。
Copy/paste将下面的代码放入新文件中。
// // TableViewCellSliding.swift // import UIKit protocol SlidingCellDelegate { // tell the TableView that a swipe happened func hasPerformedSwipe(touch: CGPoint) func hasPerformedTap(touch: CGPoint) } class SlidingTableViewCell: UITableViewCell { var delegate: SlidingCellDelegate? var originalCenter = CGPoint() var isSwipeSuccessful = false var touch = CGPoint() required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) // add a PAN gesture let pRecognizer = UIPanGestureRecognizer(target: self, action: #selector(SlidingTableViewCell.handlePan(_:))) pRecognizer.delegate = self addGestureRecognizer(pRecognizer) // add a TAP gesture // note that adding the PAN gesture to a cell disables the built-in tap responder (didSelectRowAtIndexPath) // so we can add in our own here if we want both swipe and tap actions let tRecognizer = UITapGestureRecognizer(target: self, action: #selector(SlidingTableViewCell.handleTap(_:))) tRecognizer.delegate = self addGestureRecognizer(tRecognizer) } override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer { let translation = panGestureRecognizer.translation(in: superview!) //look for right-swipe if (fabs(translation.x) > fabs(translation.y)) && (translation.x > 0){ // look for left-swipe //if (fabs(translation.x) > fabs(translation.y)) && (translation.x < 0){ //print("gesture 1") touch = panGestureRecognizer.location(in: superview) return true } //not left or right - must be up or down return false }else if gestureRecognizer is UITapGestureRecognizer { touch = gestureRecognizer.location(in: superview) return true } return false } func handleTap(_ recognizer: UITapGestureRecognizer){ // call function to get indexPath since didSelectRowAtIndexPath will be disabled delegate?.hasPerformedTap(touch: touch) } func handlePan(_ recognizer: UIPanGestureRecognizer) { if recognizer.state == .began { originalCenter = center } if recognizer.state == .changed { checkIfSwiped(recongizer: recognizer) } if recognizer.state == .ended { let originalFrame = CGRect(x: 0, y: frame.origin.y, width: bounds.size.width, height: bounds.size.height) if isSwipeSuccessful{ delegate?.hasPerformedSwipe(touch: touch) //after 'short' swipe animate back to origin quickly moveViewBackIntoPlaceSlowly(originalFrame: originalFrame) } else { //after successful swipe animate back to origin slowly moveViewBackIntoPlace(originalFrame: originalFrame) } } } func checkIfSwiped(recongizer: UIPanGestureRecognizer) { let translation = recongizer.translation(in: self) center = CGPoint(x: originalCenter.x + translation.x, y: originalCenter.y) //this allows only swipe-right isSwipeSuccessful = frame.origin.x > frame.size.width / 2.0 //pan is 1/2 width of the cell //this allows only swipe-left //isSwipeSuccessful = frame.origin.x < -frame.size.width / 3.0 //pan is 1/3 width of the cell } func moveViewBackIntoPlace(originalFrame: CGRect) { UIView.animate(withDuration: 0.2, animations: {self.frame = originalFrame}) } func moveViewBackIntoPlaceSlowly(originalFrame: CGRect) { UIView.animate(withDuration: 1.5, animations: {self.frame = originalFrame}) } }
第 3 步:
Copy/paste将下面的代码放入现有文件"ViewController.swift"
// // ViewController.swift // import UIKit class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, SlidingCellDelegate { @IBOutlet weak var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. tableView.dataSource = self tableView.delegate = self tableView.register(SlidingTableViewCell.self, forCellReuseIdentifier: "cell") tableView.rowHeight = 50; } func hasPerformedSwipe(touch: CGPoint) { if let indexPath = tableView.indexPathForRow(at: touch) { // Access the image or the cell at this index path print("got a swipe row:\(indexPath.row)") } } func hasPerformedTap(touch: CGPoint){ if let indexPath = tableView.indexPathForRow(at: touch) { // Access the image or the cell at this index path print("got a tap row:\(indexPath.row)") } } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1 } func tableView(_ tableView: UITableView,numberOfRowsInSection section: Int)-> Int { return 100 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell=tableView.dequeueReusableCell(withIdentifier: "cell",for: indexPath) as! SlidingTableViewCell // Configure cell cell.selectionStyle = .none cell.textLabel?.text = "hello \(indexPath.row)" cell.delegate = self return cell } func tableView(sender: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { // do stuff with indexPath.row and indexPath.section //never make it here because we added a tap gesture but this will print("selected cell") } }
第 4 步:
在情节提要中将其全部连接起来。
打开 Storyboard 视图和 select TableView。转到 Connections Inspector(圆圈中的右上角箭头)并从 New Referencing Outlet 拖到 TableView 并从弹出菜单中 select "tableView"。
在 TableView 仍然 selected 的情况下,从 Outlets > dataSource 拖动到 Storyboard 中的 TableView。从 Outlets > delegate.
开始重复
第 5 步:
- 运行啦!
我不会详细介绍任何代码,因为顶部的两个链接做得很好。这只是关于拥有可以构建的完整、简单、干净的代码。享受吧。