Swift语言开发:仿Clear手势操作(拖拽、划动、捏合)UITableView

前端之家收集整理的这篇文章主要介绍了Swift语言开发:仿Clear手势操作(拖拽、划动、捏合)UITableView前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

http://www.itnose.net/news/155/6429874


2016-01-03 19:46

这是一个完全依靠手势的操作ToDoList的演示,功能上左划删除,右划完成任务,拖拽调整顺序,捏合张开插入。

项目源码: https://github.com/luan-ma/ClearStyleDemo.Swift

初始化

TDCToDoItem.swift 定义模型对象

TDCToDoListController.swift 继承自UITableViewController, 演示UITableView操作

  1. varitems=[
  2. TDCToDoItem(text:"Feedthecat"),TDCToDoItem(text:"Buyeggs"),TDCToDoItem(text:"PackbagsforWWDC"),TDCToDoItem(text:"Ruletheweb"),TDCToDoItem(text:"BuyanewiPhone"),TDCToDoItem(text:"Findmissingsocks"),TDCToDoItem(text:"Writeanewtutorial"),TDCToDoItem(text:"MasterObjective-C"),TDCToDoItem(text:"Rememberyourweddinganniversary!"),TDCToDoItem(text:"Drinklessbeer"),TDCToDoItem(text:"Learntodraw"),TDCToDoItem(text:"Takethecartothegarage"),TDCToDoItem(text:"SellthingsoneBay"),TDCToDoItem(text:"Learntojuggle"),TDCToDoItem(text:"Giveup")
  3. ]
  4.  
  5. overridefuncviewDidLoad(){
  6. super.viewDidLoad()
  7.  
  8. //捏合手势
  9. letpinch=UIPinchGestureRecognizer(target:self,action:"handlePinch:")
  10. //长按拖拽
  11. letlongPress=UILongPressGestureRecognizer(target:self,action:"handleLongPress:")
  12.  
  13. tableView.addGestureRecognizer(pinch)
  14. tableView.addGestureRecognizer(longPress)
  15. }


左划删除、右划完成

在每一个Cell添加滑动手势(Pan)。处理划动距离,超过宽度1/3就为有效操作,左划为删除操作,右划为完成操作。

布局使用AutoLayout,中间内容区的限制条件是宽度等于容器宽度、高度等于容器高度、垂直中对齐、水平中对齐,而平移操作实际上就是操作水平中对齐的距离值。

TDCToDoItemCell.swift关键代码如下

手势判断

  1. //如果是划动手势,仅支持左右划动;如果是其它手势,则有父类负责
  2. overridefuncgestureRecognizerShouldBegin(gestureRecognizer:UIGestureRecognizer)->Bool{
  3. ifletpanGesture=gestureRecognizeras?UIPanGestureRecognizer{
  4. lettranslation=panGesture.translationInView(self.superview)
  5. returnfabs(translation.x)>fabs(translation.y)
  6. }else{
  7. returnsuper.gestureRecognizerShouldBegin(gestureRecognizer)
  8. }
  9. }

手势操作

  1. varonDelete:((TDCToDoItemCell)->Void)?
  2. varonComplete:((TDCToDoItemCell)->Void)?
  3.  
  4. privatevardeleteOnDragRelease:Bool=false
  5. privatevarcompleteOnDragRelease:Bool=false
  6.  
  7. //划动平移的实际AutoLayout中的水平中对齐的距离
  8. @IBOutletweakvarcenterConstraint:NSLayoutConstraint!
  9. privatevaroriginConstant:CGFloat=0
  10.  
  11. funchandlePan(panGesture:UIPanGestureRecognizer){
  12. switchpanGesture.state{
  13. case.Began:
  14. originConstant=centerConstraint.constant
  15. case.Changed:
  16. lettranslation=panGesture.translationInView(self)
  17. centerConstraint.constant=translation.x
  18.  
  19. //划动移动1/3宽度为有效划动
  20. letfinished=fabs(translation.x)>CGRectGetWidth(bounds)/3
  21. iftranslation.x<originConstant{//右划
  22. iffinished{
  23. deleteOnDragRelease=true
  24. rightLabel.textColor=UIColor.redColor()
  25. }else{
  26. deleteOnDragRelease=false
  27. rightLabel.textColor=UIColor.whiteColor()
  28. }
  29. }else{//左划
  30. iffinished{
  31. completeOnDragRelease=true
  32. leftLabel.textColor=UIColor.greenColor()
  33. }else{
  34. completeOnDragRelease=false
  35. leftLabel.textColor=UIColor.whiteColor()
  36. }
  37. }
  38. case.Ended:
  39. centerConstraint.constant=originConstant
  40.  
  41. ifdeleteOnDragRelease{
  42. deleteOnDragRelease=false
  43. ifletonDelete=onDelete{
  44. onDelete(self)
  45. }
  46. }
  47.  
  48. ifcompleteOnDragRelease{
  49. completeOnDragRelease=false
  50. ifletonComplete=onComplete{
  51. onComplete(self)
  52. }
  53. }
  54. default:
  55. break
  56. }
  57. }

TDCToDoListController.swift中执行删除操作

  1. /*
  2. //简单删除
  3. funcdeleteToDoItem(indexPath:NSIndexPath){
  4. tableView.beginUpdates()
  5. items.removeAtIndex(indexPath.row)
  6. tableView.deleteRowsAtIndexPaths([indexPath],withRowAnimation:.Automatic)
  7. tableView.endUpdates()
  8. }
  9. */
  10.  
  11. //视觉效果更漂亮的删除
  12. funcdeleteToDoItem(indexPath:NSIndexPath){
  13. letitem=items.removeAtIndex(indexPath.row)
  14. varanimationEnabled=false
  15. letlastCell=tableView.visibleCells.last
  16. vardelay:NSTimeInterval=0
  17. forcellintableView.visibleCells{
  18. letcell=cellas!TDCToDoItemCell
  19. ifanimationEnabled{
  20. UIView.animateWithDuration(0.25,delay:delay,options:.CurveEaseInOut,animations:{()->Voidin
  21. cell.frame=CGRectOffset(cell.frame,-CGRectGetHeight(cell.frame))
  22. },completion:{(completed)->Voidin
  23. ifcell==lastCell{
  24. self.tableView.reloadData()
  25. }
  26. })
  27. delay+=0.03
  28. }
  29.  
  30. ifcell.toDoItem==item{
  31. animationEnabled=true
  32. cell.hidden=true
  33. }
  34. }
  35. }


拖拽排序

长按选中某Cell,截图此Cell生成UIImageView,然后隐藏此Cell(hidden=true),随手指移动拖拽UIImageView,每次拖拽到一个Cell上的时候,交换当前Cell和隐藏Cell位置。效果如下

截图UIView生成一个新的UIImageVIew

  1. funcsnapView(view:UIView)->UIImageView{
  2. UIGraphicsBeginImageContextWithOptions(view.bounds.size,false,0)
  3. view.layer.renderInContext(UIGraphicsGetCurrentContext()!)
  4. letimage=UIGraphicsGetImageFromCurrentImageContext()
  5. UIGraphicsEndImageContext()
  6.  
  7. letsnapShot=UIImageView(image:image)
  8. snapShot.layer.masksToBounds=false;
  9. snapShot.layer.cornerRadius=0;
  10. snapShot.layer.shadowOffset=CGSizeMake(-5.0,0.0);
  11. snapShot.layer.shadowOpacity=0.4;
  12. snapShot.layer.shadowRadius=5;
  13. snapShot.frame=view.frame
  14. returnsnapShot
  15. }

拖拽操作代码,详细操作参考注释

  1. privatevarsourceIndexPath:NSIndexPath?
  2. privatevarsnapView:UIView?
  3.  
  4. funchandleLongPress(longPress:UILongPressGestureRecognizer){
  5. letpoint=longPress.locationInView(tableView)
  6.  
  7. ifletindexPath=tableView.indexPathForRowAtPoint(point){
  8. switchlongPress.state{
  9. case.Began:
  10. ifletcell=tableView.cellForRowAtIndexPath(indexPath){
  11. sourceIndexPath=indexPath
  12. letsnapView=self.snapView(cell)
  13. snapView.alpha=0
  14.  
  15. self.snapView=snapView
  16.  
  17. tableView.addSubview(snapView)
  18.  
  19. UIView.animateWithDuration(0.25,animations:{
  20. //选中Cell跳出放大效果
  21. snapView.alpha=0.95
  22. snapView.center=CGPointMake(cell.center.x,point.y)
  23. snapView.transform=CGAffineTransformMakeScale(1.05,1.05)
  24.  
  25. cell.alpha=0
  26. },completion:{(completed)->Voidin
  27. cell.hidden=true
  28. cell.alpha=1
  29. })
  30. }else{
  31. sourceIndexPath=nil
  32. snapView=nil
  33. break
  34. }
  35. case.Changed:
  36. ifletsnapView=snapView{
  37. //截图随手指上下移动
  38. snapView.center=CGPointMake(snapView.center.x,point.y)
  39. }
  40.  
  41. //如果手指移动到一个新的Cell上面,隐藏Cell跟此Cell交换位置
  42. ifletfromIndexPath=sourceIndexPath{
  43. iffromIndexPath!=indexPath{
  44. tableView.beginUpdates()
  45. lettemp=items[indexPath.row]
  46. items[indexPath.row]=items[fromIndexPath.row]
  47. items[fromIndexPath.row]=temp
  48. tableView.moveRowAtIndexPath(fromIndexPath,toIndexPath:indexPath)
  49. tableView.endUpdates()
  50. sourceIndexPath=indexPath
  51. }
  52. }
  53.  
  54. //手指移动到屏幕顶端或底部,UITableView自动滚动
  55. letstep:CGFloat=64
  56. ifletparentView=tableView.superview{
  57. letparentPos=tableView.convertPoint(point,toView:parentView)
  58. ifparentPos.y>parentView.bounds.height-step{
  59. varoffset=tableView.contentOffset
  60. offset.y+=(parentPos.y-parentView.bounds.height+step)
  61. ifoffset.y>tableView.contentSize.height-tableView.bounds.height{
  62. offset.y=tableView.contentSize.height-tableView.bounds.height
  63. }
  64. tableView.setContentOffset(offset,animated:false)
  65. }elseifparentPos.y<=step{
  66. varoffset=tableView.contentOffset
  67. offset.y-=(step-parentPos.y)
  68. ifoffset.y<0{
  69. offset.y=0
  70. }
  71. tableView.setContentOffset(offset,animated:false)
  72. }
  73. }
  74. default:
  75. ifletsnapView=snapView,letfromIndexPath=sourceIndexPath,letcell=tableView.cellForRowAtIndexPath(fromIndexPath){
  76. cell.alpha=0
  77. cell.hidden=false
  78.  
  79. //长按移动结束,隐藏的Cell恢复显示删除截图
  80. UIView.animateWithDuration(0.25,animations:{()->Voidin
  81. snapView.center=cell.center
  82. snapView.alpha=0
  83.  
  84. cell.alpha=1
  85. },completion:{[unownedself](completed)->Voidin
  86. snapView.removeFromSuperview()
  87. self.snapView=nil
  88. self.sourceIndexPath=nil
  89.  
  90. self.tableView.performSelector("reloadData",withObject:nil,afterDelay:0.5)
  91. })
  92. }
  93. }
  94. }
  95. }


捏合张开插入

通过捏合手势中两个触点获取两个相邻的Cell,通过修改UIView.transform属性移动屏幕上的Cell位置。

获取Pinch两个手指坐标的工具方法

  1. funcpointsOfPinch(pinch:UIPinchGestureRecognizer)->(CGPoint,CGPoint){
  2. ifpinch.numberOfTouches()>1{
  3. letpoint1=pinch.locationOfTouch(0,inView:tableView)
  4. letpoint2=pinch.locationOfTouch(1,inView:tableView)
  5. ifpoint1.y<=point2.y{
  6. return(point1,point2)
  7. }else{
  8. return(point2,point1)
  9. }
  10. }else{
  11. letpoint=pinch.locationOfTouch(0,inView:tableView)
  12. return(point,point)
  13. }
  14. }

捏合张开操作代码,详情请参考代码和注释

  1. //插入点
  2. privatevarpinchIndexPath:NSIndexPath?
  3. //临时代理视图
  4. privatevarplacheHolderCell:TDCPlaceHolderView?
  5. //两触点的起始位置
  6. privatevarsourcePoints:(upperPoint:CGPoint,downPoint:CGPoint)?
  7. //可以插入操作的标志
  8. privatevarpinchInsertEnabled=false
  9.  
  10. funchandlePinch(pinch:UIPinchGestureRecognizer){
  11. switchpinch.state{
  12. case.Began:
  13. pinchBegan(pinch)
  14. case.Changed:
  15. pinchChanged(pinch)
  16. default:
  17. pinchEnd(pinch)
  18. }
  19. }
  20.  
  21. funcpinchBegan(pinch:UIPinchGestureRecognizer){
  22. pinchIndexPath=nil
  23. sourcePoints=nil
  24. pinchInsertEnabled=false
  25.  
  26. let(upperPoint,downPoint)=pointsOfPinch(pinch)
  27. ifletupperIndexPath=tableView.indexPathForRowAtPoint(upperPoint),letdownIndexPath=tableView.indexPathForRowAtPoint(downPoint){
  28. ifdownIndexPath.row-upperIndexPath.row==1{
  29. letupperCell=tableView.cellForRowAtIndexPath(upperIndexPath)!
  30. letplacheHolder=NSBundle.mainBundle().loadNibNamed("TDCPlaceHolderView",owner:tableView,options:nil).firstas!TDCPlaceHolderView
  31. placheHolder.frame=CGRectOffset(upperCell.frame,CGRectGetHeight(upperCell.frame)/2)
  32. tableView.insertSubview(placheHolder,atIndex:0)
  33.  
  34. sourcePoints=(upperPoint,downPoint)
  35. pinchIndexPath=upperIndexPath
  36. placheHolderCell=placheHolder
  37. }
  38. }
  39. }
  40.  
  41. funcpinchChanged(pinch:UIPinchGestureRecognizer){
  42. ifletpinchIndexPath=pinchIndexPath,letoriginPoints=sourcePoints,letplacheHolderCell=placheHolderCell{
  43. letpoints=pointsOfPinch(pinch)
  44.  
  45. letupperDistance=points.0.y-originPoints.upperPoint.y
  46. letdownDistance=originPoints.downPoint.y-points.1.y
  47. letdistance=-min(0,min(upperDistance,downDistance))
  48. NSLog("distance=\(distance)")
  49.  
  50. //移动两边的Cell
  51. forcellintableView.visibleCells{
  52. letindexPath=tableView.indexPathForCell(cell)!
  53. ifindexPath.row<=pinchIndexPath.row{
  54. cell.transform=CGAffineTransformMakeTranslation(0,-distance)
  55. }else{
  56. cell.transform=CGAffineTransformMakeTranslation(0,distance)
  57. }
  58. }
  59.  
  60. //插入的Cell变形
  61. letscaleY=min(64,fabs(distance)*2)/CGFloat(64)
  62. placheHolderCell.transform=CGAffineTransformMakeScale(1,scaleY)
  63.  
  64. placheHolderCell.lblTitle.text=scaleY<=0.5?"张开双指插入新项目":"松手可以插入新项目"
  65.  
  66. //张开超过一个Cell高度时,执行插入操作
  67. pinchInsertEnabled=scaleY>=1
  68. }
  69. }
  70.  
  71. funcpinchEnd(pinch:UIPinchGestureRecognizer){
  72. ifletpinchIndexPath=pinchIndexPath,letplacheHolderCell=placheHolderCell{
  73. placheHolderCell.transform=CGAffineTransformIdentity
  74. placheHolderCell.removeFromSuperview()
  75. self.placheHolderCell=nil
  76.  
  77. ifpinchInsertEnabled{
  78. //恢复各Cell的transform
  79. forcellinself.tableView.visibleCells{
  80. cell.transform=CGAffineTransformIdentity
  81. }
  82.  
  83. //插入操作
  84. letindex=pinchIndexPath.row+1
  85. items.insert(TDCToDoItem(text:""),atIndex:index)
  86. tableView.reloadData()
  87.  
  88. //弹出键盘
  89. letcell=tableView.cellForRowAtIndexPath(NSIndexPath(forRow:index,inSection:0))as!TDCToDoItemCell
  90. cell.txtField.becomeFirstResponder()
  91. }else{
  92. //放弃插入,恢复原位置
  93. UIView.animateWithDuration(0.25,delay:0,animations:{[unownedself]()->Voidin
  94. forcellinself.tableView.visibleCells{
  95. cell.transform=CGAffineTransformIdentity
  96. }
  97. },completion:{[unownedself](completed)->Voidin
  98. self.tableView.reloadData()
  99. })
  100. }
  101. }
  102.  
  103. sourcePoints=nil
  104. pinchIndexPath=nil
  105. pinchInsertEnabled=false
  106. }


参考

1. https://github.com/ColinEberhardt/iOS-ClearStyle

2. http://blog.csdn.net/u013604612/article/details/43884039

猜你在找的Swift相关文章