使用Swift实现的转盘菜单,主要用到UIBezierPath、CALayer遮罩绘制扇形UIView,CATransform3DMakeRotation实现旋转动画。代码设计使用默认configureCallback回调方便创建和设置基本属性,参考UITableView代理和数据源模式,支持AutoLayout和Frame。
效果图1.遮罩绘制扇形View计算扇形曲线位置,通过CALayer的mask属性绘制出扇形UIView核心代码
funcsetMaskLayer(_startAngle:CGFloat,endAngle:CGFloat){ letcenter=CGPoint(x:bounds.width*0.5,y:bounds.height*0.5) letlayer=CAShapeLayer() path.addArc(withCenter:center,radius:bounds.width*0.5,startAngle:startAngle,endAngle:endAngle,clockwise:true) path.addLine(to:center) layer.path=path.cgPath layer.rasterizationScale=UIScreen.main.scale layer.shouldRasterize=true self.layer.mask=layer}2.中间镂空funccreateHole(inview:UIView,radius:CGFloat) { letpath=CGMutablePath() path.addArc(center:view.center,radius:radius,startAngle:0.0,endAngle:2.0*.pi,clockwise:true) path.addRect(CGRect(origin:.zero,size:view.bounds.size)) letmaskLayer=CAShapeLayer() maskLayer.path=path maskLayer.fillRule=.evenOdd view.layer.mask=maskLayer view.clipsToBounds=true}3.旋转动画添加UIPanGestureRecognizer、UITapGestureRecognizer手势,根据手势位置使用atan2函数计算旋转角度,然后用CATransform3DMakeRotation围绕Z轴旋转做动画核心代码
funchandlePanGesture(_sender:UIPanGestureRecognizer){ letlocation=sender.location(in:self) switchsender.state{ case.began: startPoint=location case.changed: letradian1=-atan2(startPoint.x-menuLayerView.center.x,startPoint.y-menuLayerView.center.y) letradian2=-atan2(location.x-menuLayerView.center.x,location.y-menuLayerView.center.y) menuLayerView.transform=menuLayerView.transform.rotated(by:radian2-radian1) startPoint=location default: letangle=2*CGFloat(Double.pi)/CGFloat(cells.count) varmenuViewAngle=atan2(menuLayerView.transform.b,menuLayerView.transform.a) ifmenuViewAngle<0{ menuViewAngle+=CGFloat(2*Double.pi) } varindex=cells.count-Int((menuViewAngle+CGFloat(Double.pi/4))/angle) ifindex==cells.count{ index=0 } setSelectedIndex(index,animated:true) }}funchandleTapGesture(_sender:UITapGestureRecognizer){ letlocation=sender.location(in:menuLayerView) for(index,cell)incells.enumerated(){ ifcell.path.contains(location){ setSelectedIndex(index,animated:true) } }}4.弹出收起动画funcopenMenuView(withAnimateanimate:Bool=true){ openMenu=true UIView.animate(withDuration:animate?configure.animationDuration:0,delay:0,usingSpringWithDamping:0.7,initialSpringVelocity:5.0,options:.curveEaseInOut){ self.centerButton.transform=CGAffineTransform(rotationAngle:.pi*-0.5) self.centerButton.setImage(self.configure.closeImage,for:.normal) self.menuLayerView.transform=CGAffineTransform(scaleX:1,y:1).rotated(by:self.currentAngle) }}funccloseMenuView(withAnimateanimate:Bool=true){ openMenu=false letscale=(configure.centerRadius*2)/bounds.width UIView.animate(withDuration:animate?configure.animationDuration:0,delay:0,usingSpringWithDamping:0.7,initialSpringVelocity:5.0,options:.curveEaseInOut){ self.centerButton.transform=.identity self.centerButton.setImage(self.configure.openImage,for:.normal) self.menuLayerView.transform=CGAffineTransform(scaleX:scale,y:scale).rotated(by:self.currentAngle) }}5.内部细节考虑到方便布局和使用,内部使用UIView叠加旋转实现,这里也可以采用Layer直接绘制实现,相对UIView,层次结构会简单很多
总结核心代码已经贴出,完整代码请查看----->>>CLDemo,如果对你有所帮助,欢迎Star。
评论