前言
作为一个iOS开发者,在实际工作我们无数次跟UITableView打交道,UITableView拥有着诸多优点,强大,好用,性能好等。单元格作为UITableView重要的部分,通常会被自定义以适应不同的需求。今天简单模仿下仿照QQ聊天单元格左滑效果。
![](https://img.haomeiwen.com/i1680240/224d78e85c2894e6.gif)
1、利用系统API实现单元格左滑效果
@available(iOS 8.0, *)
optional public func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? // supercedes -tableView:titleForDeleteConfirmationButtonForRowAtIndexPath: if return value is non-nil
该函数由iOS 8.0以后提供,是UITableViewDelegate中的一个函数,用起来简单方便,如果产品需求简单倒是直接建议用此方法实现对应功能。
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
// 实例化一个操作对象UITableViewRowAction
let deleteAction = UITableViewRowAction(style: .default, title: " 删除 ") { (_, indexpath) in
// 点击触发事件
}
let test2 = UITableViewRowAction(style: .default, title: "测试") { (_, indexpath) in
// 点击触发事件
}
// 设置操作按钮的背景色
test2.backgroundColor = UIColor.blue
// 返回操作对象数组
return [deleteAction, test2]
}
以下是UITableViewRowAction提供的开发接口
@available(iOS 8.0, *)
open class UITableViewRowAction : NSObject, NSCopying {
public convenience init(style: UITableViewRowActionStyle, title: String?, handler: @escaping (UITableViewRowAction, IndexPath) -> Swift.Void)
open var style: UITableViewRowActionStyle { get }
open var title: String?
@NSCopying open var backgroundColor: UIColor? // default background color is dependent on style
@NSCopying open var backgroundEffect: UIVisualEffect?
}
通过以上接口可以看出,操作按钮的设置有很大局限性,背景颜色可以设置,但是宽度无法设置,又或操作按钮想显示图片,则无法设置。
2、自定义实现单元格左滑效果(仿照QQ聊天单元格左滑效果)
这里贴一下主要代码块,说下思路,比较简单,有兴趣的朋友可以下载Demo看一下,在文章问候有链接
自定义单元格,继承自UITableViewCell
![](https://img.haomeiwen.com/i1680240/ac2a5fdee096d914.png)
提供实例化对象类方法
/**
实例化对象类方法
rightButtonBgColors: 右边操作按钮背景色数组,不传则显示默认颜色(右边操作按钮最多三个,多传无用)
rightButtonTitles: 右边操作按钮标题数组(右边操作按钮最多三个,多传无用)
closureClickRightButton: 点击右边操作按钮触发事件,参数是button,可以根据button的title或者tag判断点击了哪个操作按钮
*/
class func swipeTableViewCell(tableView: UITableView, rightButtonBgColors:[UIColor]? = nil, rightButtonTitles: [String], closureClickRightButton: ((_ button: UIButton) -> ())?) -> (JYSwipeTableViewCell) {
var cell = tableView.dequeueReusableCell(withIdentifier: "JYSwipeTableViewCellId") as? JYSwipeTableViewCell
if cell == nil {
cell = Bundle.main.loadNibNamed("JYSwipeTableViewCell", owner: nil, options: nil)?.first as? JYSwipeTableViewCell
}
cell?.tableView = tableView
if let rightButtonBgColors = rightButtonBgColors, rightButtonBgColors.count > 0 {
cell?.rightButtonBgColors = rightButtonBgColors
}
cell?.rightButtonTitles = rightButtonTitles
cell?.closureClickRightButton = closureClickRightButton
return cell!
}
手势添加:两个手势,滑动和点击
override func awakeFromNib() {
super.awakeFromNib()
selectionStyle = .none
isSwiped = false
// overlayerContentView添加滑动手势
let pan = UIPanGestureRecognizer(target: self, action: #selector(overlayerContentViewDidSwip(pan:)))
overlayerContentView.addGestureRecognizer(pan)
// overlayerContentView添加点击手势
let tap = UITapGestureRecognizer(target: self, action: #selector(overlayerContentViewDidTap(tap:)))
overlayerContentView.addGestureRecognizer(tap)
}
手势触发事件,主要处理就在这里,根据滑动状态(开始滑动、滑动中、滑动结束)的变化处理自定义的内容视图的x坐标。有一个注意点:当前滑动动画正在进行时禁调tableView的isUserInteractionEnabled(手势交互),以免当前动画正在进行,用户紧接着滑动另一个单元格,然后同时两个单元格显示操作按钮。
// MARK - 手势触发事件
extension JYSwipeTableViewCell {
/// 点击overlayerContentView后出发的方法
@objc fileprivate func overlayerContentViewDidTap(tap: UITapGestureRecognizer) {
// 恢复已滑动单元格
recoverSwipedCell()
}
// 滑动overlayerContentView后出发的方法
@objc fileprivate func overlayerContentViewDidSwip(pan: UIPanGestureRecognizer) {
var translationX = pan.translation(in: self).x
// print("translationX: \(translationX)")
if pan.state == .began {// 滑动开始
// 恢复已滑动单元格
recoverSwipedCell()
}else if pan.state == .changed {// 滑动中
if translationX < 0 {// 从右往左滑
if isSwiped == false {// 当前不是滑动后的状态
translationX = translationX < maxSwipValue ? maxSwipValue : translationX
overlayerContentViewLeftLC.constant = translationX
}
}else {// 从左往右滑
if isSwiped == true {// 处在滑动后的状态
UIView.animate(withDuration: animateDuration, animations: {[weak self] in
self?.overlayerContentView.frame = (self?.bounds)!
}, completion: {[weak self] (_) in
self?.isSwiped = false
})
}
}
}else if pan.state == .ended, isSwiped == false {// 滑动结束,且单元格当前不是滑动后的状态
if translationX > maxSwipValue * 0.3 {// 滑过距离没有超过maxSwipValue的三分之一
overlayerContentViewLeftLC.constant = 0
}else {// 滑过距离超过maxSwipValue的三分之一
overlayerContentViewLeftLC.constant = maxSwipValue
}
tableView?.isUserInteractionEnabled = false
UIView.animate(withDuration: animateDuration, animations: {[weak self] in
self?.contentView.layoutIfNeeded()
}, completion: {[weak self] (_) in
if self?.overlayerContentViewLeftLC.constant == maxSwipValue {
self?.isSwiped = true
}
self?.tableView?.isUserInteractionEnabled = true
})
}
}
/// 恢复已滑动单元格
fileprivate func recoverSwipedCell() {
guard let tableView = tableView else {
return
}
for cell in tableView.visibleCells {
guard let cell = cell as? JYSwipeTableViewCell else {
continue
}
if cell.isSwiped == true {// 已经处在滑动后的状态
cell.overlayerContentViewLeftLC.constant = 0
UIView.animate(withDuration: animateDuration, animations: {
cell.contentView.layoutIfNeeded()
}, completion: { (_) in
cell.isSwiped = false
})
}
}
}
}
网友评论