http://www.itnose.net/news/155/6429874
这是一个完全依靠手势的操作TodoList的演示,功能上左划删除,右划完成任务,拖拽调整顺序,捏合张开插入。
项目源码: https://github.com/luan-ma/ClearStyleDemo.Swift
初始化
TDCTodoItem.swift 定义模型对象
TDCTodoListController.swift 继承自UITableViewController, 演示UITableView操作
varitems=[ 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") ] overridefuncviewDidLoad(){ super.viewDidLoad() //捏合手势 letpinch=UIPinchGestureRecognizer(target:self,action:"handlePinch:") //长按拖拽 letlongPress=UILongPressGestureRecognizer(target:self,action:"handleLongPress:") tableView.addGestureRecognizer(pinch) tableView.addGestureRecognizer(longPress) }
左划删除、右划完成
在每一个Cell添加滑动手势(Pan)。处理划动距离,超过宽度1/3就为有效操作,左划为删除操作,右划为完成操作。
布局使用AutoLayout,中间内容区的限制条件是宽度等于容器宽度、高度等于容器高度、垂直中对齐、水平中对齐,而平移操作实际上就是操作水平中对齐的距离值。
TDCTodoItemCell.swift关键代码如下
手势判断
//如果是划动手势,仅支持左右划动;如果是其它手势,则有父类负责 overridefuncgestureRecognizerShouldBegin(gestureRecognizer:UIGestureRecognizer)->Bool{ ifletpanGesture=gestureRecognizeras?UIPanGestureRecognizer{ lettranslation=panGesture.translationInView(self.superview) returnfabs(translation.x)>fabs(translation.y) }else{ returnsuper.gestureRecognizerShouldBegin(gestureRecognizer) } }
手势操作
varonDelete:((TDCTodoItemCell)->Void)? varonComplete:((TDCTodoItemCell)->Void)? privatevardeleteOnDragrelease:Bool=false privatevarcompleteOnDragrelease:Bool=false //划动平移的实际AutoLayout中的水平中对齐的距离 @IBOutletweakvarcenterConstraint:NSLayoutConstraint! privatevaroriginConstant:CGFloat=0 funchandlePan(panGesture:UIPanGestureRecognizer){ switchpanGesture.state{ case.Began: originConstant=centerConstraint.constant case.Changed: lettranslation=panGesture.translationInView(self) centerConstraint.constant=translation.x //划动移动1/3宽度为有效划动 letfinished=fabs(translation.x)>CGRectGetWidth(bounds)/3 iftranslation.x<originConstant{//右划 iffinished{ deleteOnDragrelease=true rightLabel.textColor=UIColor.redColor() }else{ deleteOnDragrelease=false rightLabel.textColor=UIColor.whiteColor() } }else{//左划 iffinished{ completeOnDragrelease=true leftLabel.textColor=UIColor.greenColor() }else{ completeOnDragrelease=false leftLabel.textColor=UIColor.whiteColor() } } case.Ended: centerConstraint.constant=originConstant ifdeleteOnDragrelease{ deleteOnDragrelease=false ifletonDelete=onDelete{ onDelete(self) } } ifcompleteOnDragrelease{ completeOnDragrelease=false ifletonComplete=onComplete{ onComplete(self) } } default: break } }
在TDCTodoListController.swift中执行删除操作
/* //简单删除 funcdeletetoDoItem(indexPath:NSIndexPath){ tableView.beginUpdates() items.removeAtIndex(indexPath.row) tableView.deleteRowsAtIndexPaths([indexPath],withRowAnimation:.Automatic) tableView.endUpdates() } */ //视觉效果更漂亮的删除 funcdeletetoDoItem(indexPath:NSIndexPath){ letitem=items.removeAtIndex(indexPath.row) varanimationEnabled=false letlastCell=tableView.visibleCells.last vardelay:NSTimeInterval=0 forcellintableView.visibleCells{ letcell=cellas!TDCTodoItemCell ifanimationEnabled{ UIView.animateWithDuration(0.25,delay:delay,options:.CurveEaseInOut,animations:{()->Voidin cell.frame=CGRectOffset(cell.frame,-CGRectGetHeight(cell.frame)) },completion:{(completed)->Voidin ifcell==lastCell{ self.tableView.reloadData() } }) delay+=0.03 } ifcell.todoItem==item{ animationEnabled=true cell.hidden=true } } }
拖拽排序
长按选中某Cell,截图此Cell生成UIImageView,然后隐藏此Cell(hidden=true),随手指移动拖拽UIImageView,每次拖拽到一个Cell上的时候,交换当前Cell和隐藏Cell位置。效果如下
截图UIView生成一个新的UIImageVIew
funcsnapView(view:UIView)->UIImageView{ UIGraphicsBeginImageContextWithOptions(view.bounds.size,false,0) view.layer.renderInContext(UIGraphicsGetCurrentContext()!) letimage=UIGraphicsGetimageFromCurrentimageContext() UIGraphicsEndImageContext() letsnapShot=UIImageView(image:image) snapShot.layer.masksToBounds=false; snapShot.layer.cornerRadius=0; snapShot.layer.shadowOffset=CGSizeMake(-5.0,0.0); snapShot.layer.shadowOpacity=0.4; snapShot.layer.shadowRadius=5; snapShot.frame=view.frame returnsnapShot }
拖拽操作代码,详细操作参考注释
privatevarsourceIndexPath:NSIndexPath? privatevarsnapView:UIView? funchandleLongPress(longPress:UILongPressGestureRecognizer){ letpoint=longPress.locationInView(tableView) ifletindexPath=tableView.indexPathForRowAtPoint(point){ switchlongPress.state{ case.Began: ifletcell=tableView.cellForRowAtIndexPath(indexPath){ sourceIndexPath=indexPath letsnapView=self.snapView(cell) snapView.alpha=0 self.snapView=snapView tableView.addSubview(snapView) UIView.animateWithDuration(0.25,animations:{ //选中Cell跳出放大效果 snapView.alpha=0.95 snapView.center=CGPointMake(cell.center.x,point.y) snapView.transform=CGAffineTransformMakeScale(1.05,1.05) cell.alpha=0 },completion:{(completed)->Voidin cell.hidden=true cell.alpha=1 }) }else{ sourceIndexPath=nil snapView=nil break } case.Changed: ifletsnapView=snapView{ //截图随手指上下移动 snapView.center=CGPointMake(snapView.center.x,point.y) } //如果手指移动到一个新的Cell上面,隐藏Cell跟此Cell交换位置 ifletfromIndexPath=sourceIndexPath{ iffromIndexPath!=indexPath{ tableView.beginUpdates() lettemp=items[indexPath.row] items[indexPath.row]=items[fromIndexPath.row] items[fromIndexPath.row]=temp tableView.moveRowAtIndexPath(fromIndexPath,toIndexPath:indexPath) tableView.endUpdates() sourceIndexPath=indexPath } } //手指移动到屏幕顶端或底部,UITableView自动滚动 letstep:CGFloat=64 ifletparentView=tableView.superview{ letparentPos=tableView.convertPoint(point,toView:parentView) ifparentPos.y>parentView.bounds.height-step{ varoffset=tableView.contentOffset offset.y+=(parentPos.y-parentView.bounds.height+step) ifoffset.y>tableView.contentSize.height-tableView.bounds.height{ offset.y=tableView.contentSize.height-tableView.bounds.height } tableView.setContentOffset(offset,animated:false) }elseifparentPos.y<=step{ varoffset=tableView.contentOffset offset.y-=(step-parentPos.y) ifoffset.y<0{ offset.y=0 } tableView.setContentOffset(offset,animated:false) } } default: ifletsnapView=snapView,letfromIndexPath=sourceIndexPath,letcell=tableView.cellForRowAtIndexPath(fromIndexPath){ cell.alpha=0 cell.hidden=false //长按移动结束,隐藏的Cell恢复显示,删除截图 UIView.animateWithDuration(0.25,animations:{()->Voidin snapView.center=cell.center snapView.alpha=0 cell.alpha=1 },completion:{[uNownedself](completed)->Voidin snapView.removeFromSuperview() self.snapView=nil self.sourceIndexPath=nil self.tableView.performSelector("reloadData",withObject:nil,afterDelay:0.5) }) } } } }
捏合张开插入
通过捏合手势中两个触点获取两个相邻的Cell,通过修改UIView.transform属性移动屏幕上的Cell位置。
获取Pinch两个手指坐标的工具方法
funcpointsOfPinch(pinch:UIPinchGestureRecognizer)->(CGPoint,CGPoint){ ifpinch.numberOftouches()>1{ letpoint1=pinch.locationOfTouch(0,inView:tableView) letpoint2=pinch.locationOfTouch(1,inView:tableView) ifpoint1.y<=point2.y{ return(point1,point2) }else{ return(point2,point1) } }else{ letpoint=pinch.locationOfTouch(0,inView:tableView) return(point,point) } }
捏合张开操作代码,详情请参考代码和注释
//插入点 privatevarpinchIndexPath:NSIndexPath? //临时代理视图 privatevarplacheHolderCell:TDCPlaceHolderView? //两触点的起始位置 privatevarsourcePoints:(upperPoint:CGPoint,downPoint:CGPoint)? //可以插入操作的标志 privatevarpinchInsertEnabled=false funchandlePinch(pinch:UIPinchGestureRecognizer){ switchpinch.state{ case.Began: pinchBegan(pinch) case.Changed: pinchChanged(pinch) default: pinchEnd(pinch) } } funcpinchBegan(pinch:UIPinchGestureRecognizer){ pinchIndexPath=nil sourcePoints=nil pinchInsertEnabled=false let(upperPoint,downPoint)=pointsOfPinch(pinch) ifletupperIndexPath=tableView.indexPathForRowAtPoint(upperPoint),letdownIndexPath=tableView.indexPathForRowAtPoint(downPoint){ ifdownIndexPath.row-upperIndexPath.row==1{ letupperCell=tableView.cellForRowAtIndexPath(upperIndexPath)! letplacheHolder=NSBundle.mainBundle().loadNibNamed("TDCPlaceHolderView",owner:tableView,options:nil).firstas!TDCPlaceHolderView placheHolder.frame=CGRectOffset(upperCell.frame,CGRectGetHeight(upperCell.frame)/2) tableView.insertSubview(placheHolder,atIndex:0) sourcePoints=(upperPoint,downPoint) pinchIndexPath=upperIndexPath placheHolderCell=placheHolder } } } funcpinchChanged(pinch:UIPinchGestureRecognizer){ ifletpinchIndexPath=pinchIndexPath,letoriginPoints=sourcePoints,letplacheHolderCell=placheHolderCell{ letpoints=pointsOfPinch(pinch) letupperdistance=points.0.y-originPoints.upperPoint.y letdowndistance=originPoints.downPoint.y-points.1.y letdistance=-min(0,min(upperdistance,downdistance)) NSLog("distance=\(distance)") //移动两边的Cell forcellintableView.visibleCells{ letindexPath=tableView.indexPathForCell(cell)! ifindexPath.row<=pinchIndexPath.row{ cell.transform=CGAffineTransformMakeTranslation(0,-distance) }else{ cell.transform=CGAffineTransformMakeTranslation(0,distance) } } //插入的Cell变形 letscaleY=min(64,fabs(distance)*2)/CGFloat(64) placheHolderCell.transform=CGAffineTransformMakeScale(1,scaleY) placheHolderCell.lblTitle.text=scaleY<=0.5?"张开双指插入新项目":"松手可以插入新项目" //张开超过一个Cell高度时,执行插入操作 pinchInsertEnabled=scaleY>=1 } } funcpinchEnd(pinch:UIPinchGestureRecognizer){ ifletpinchIndexPath=pinchIndexPath,letplacheHolderCell=placheHolderCell{ placheHolderCell.transform=CGAffineTransformIdentity placheHolderCell.removeFromSuperview() self.placheHolderCell=nil ifpinchInsertEnabled{ //恢复各Cell的transform forcellinself.tableView.visibleCells{ cell.transform=CGAffineTransformIdentity } //插入操作 letindex=pinchIndexPath.row+1 items.insert(TDCTodoItem(text:""),atIndex:index) tableView.reloadData() //弹出键盘 letcell=tableView.cellForRowAtIndexPath(NSIndexPath(forRow:index,inSection:0))as!TDCTodoItemCell cell.txtField.becomeFirstResponder() }else{ //放弃插入,恢复原位置 UIView.animateWithDuration(0.25,delay:0,animations:{[uNownedself]()->Voidin forcellinself.tableView.visibleCells{ cell.transform=CGAffineTransformIdentity } },completion:{[uNownedself](completed)->Voidin self.tableView.reloadData() }) } } sourcePoints=nil pinchIndexPath=nil pinchInsertEnabled=false }
参考
1. https://github.com/ColinEberhardt/iOS-ClearStyle
2. http://blog.csdn.net/u013604612/article/details/43884039