前言

使用Swift实现的转盘菜单,主要用到UIBezierPathCALayer遮罩绘制扇形UIViewCATransform3DMakeRotation实现旋转动画。代码设计使用默认configureCallback回调方便创建和设置基本属性,参考UITableView代理和数据源模式,支持AutoLayoutFrame

效果图

1.遮罩绘制扇形View

计算扇形曲线位置,通过CALayermask属性绘制出扇形UIView

核心代码

func setMaskLayer(_ startAngle: CGFloat, endAngle: CGFloat) {
let center = CGPoint(x: bounds.width * 0.5, y: bounds.height * 0.5)
let layer = 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.中间镂空

func createHole(in view : UIView, radius: CGFloat)   {
let path = 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))
let maskLayer = CAShapeLayer()
maskLayer.path = path
maskLayer.fillRule = .evenOdd
view.layer.mask = maskLayer
view.clipsToBounds = true
}

3.旋转动画

添加UIPanGestureRecognizerUITapGestureRecognizer手势,根据手势位置使用atan2函数计算旋转角度,然后用CATransform3DMakeRotation围绕Z轴旋转做动画

核心代码

func handlePanGesture(_ sender: UIPanGestureRecognizer) {
let location = sender.location(in: self)
switch sender.state {
case .began:
startPoint = location
case .changed:
let radian1 = -atan2(startPoint.x - menuLayerView.center.x, startPoint.y - menuLayerView.center.y)
let radian2 = -atan2(location.x - menuLayerView.center.x, location.y - menuLayerView.center.y)
menuLayerView.transform = menuLayerView.transform.rotated(by: radian2 - radian1)
startPoint = location
default:
let angle = 2 * CGFloat(Double.pi) / CGFloat(cells.count)
var menuViewAngle = atan2(menuLayerView.transform.b, menuLayerView.transform.a)
if menuViewAngle < 0 {
menuViewAngle += CGFloat(2 * Double.pi)
}
var index = cells.count - Int((menuViewAngle + CGFloat(Double.pi / 4)) / angle)
if index == cells.count {
index = 0
}
setSelectedIndex(index, animated: true)
}
}
func handleTapGesture(_ sender: UITapGestureRecognizer) {
let location = sender.location(in: menuLayerView)
for (index, cell) in cells.enumerated() {
if cell.path.contains(location) {
setSelectedIndex(index, animated: true)
}
}
}

4.弹出收起动画

func openMenuView(withAnimate animate: 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)
}
}
func closeMenuView(withAnimate animate: Bool = true) {
openMenu = false
let scale = (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。

iOS开发之转盘菜单的更多相关文章

  1. ios开发之级联菜单(两个tableView实现)

    一:在ios项目实际开发中经常会看到级联菜单的效果:如图:点击左侧菜单,右侧菜单刷新数据.此篇用两个tableView来实现如图效果: 二:代码: 1:构造数据模型:利用kvc快速构建数据模型 #im ...

  2. iOS开发系列--App扩展开发

    概述 从iOS 8 开始Apple引入了扩展(Extension)用于增强系统应用服务和应用之间的交互.它的出现让自定义键盘.系统分享集成等这些依靠系统服务的开发变成了可能.WWDC 2016上众多更 ...

  3. iOS开发——创建你自己的Framework

    如果你想将你开发的控件与别人分享,一种方法是直接提供源代码文件.然而,这种方法并不是很优雅.它会暴露所有的实现细节,而这些实现你可能并不想开源出来.此外,开发者也可能并不想看到你的所有代码,因为他们可 ...

  4. IOS开发基础知识碎片-导航

    1:IOS开发基础知识--碎片1 a:NSString与NSInteger的互换 b:Objective-c中集合里面不能存放基础类型,比如int string float等,只能把它们转化成对象才可 ...

  5. iOS开发系列--IOS程序开发概览

    概览 终于到了真正接触IOS应用程序的时刻了,之前我们花了很多时间去讨论C语言.ObjC等知识,对于很多朋友而言开发IOS第一天就想直接看到成果,看到可以运行的IOS程序.但是这里我想强调一下,前面的 ...

  6. IOS开发基础知识--碎片11

    1:AFNetwork判断网络状态 #import “AFNetworkActivityIndicatorManager.h" - (BOOL)application:(UIApplicat ...

  7. IOS开发基础知识--碎片42

    1:报thread 1:exc_bad_access(code=1,address=0x70********) 闪退 这种错误通常是内存管理的问题,一般是访问了已经释放的对象导致的,可以开启僵尸对象( ...

  8. iOS 开发之使用safari对webview进行调试

    转自:http://www.tuicool.com/articles/ZBFnUbz 使用safari对webview进行调试 时间 2016-02-25 14:35:20  陈斌彬的技术博客 原文  ...

  9. 文顶顶iOS开发博客链接整理及部分项目源代码下载

    文顶顶iOS开发博客链接整理及部分项目源代码下载   网上的iOS开发的教程很多,但是像cnblogs博主文顶顶的博客这样内容图文并茂,代码齐全,示例经典,原理也有阐述,覆盖面宽广,自成系统的系列教程 ...

随机推荐

  1. Maven:Maven的project标签报错红线

    作者在外网完成demo项目,把Maven的本地库打成压缩包放进内网时,Maven的project标签报错红线,且别的依赖不报错,同时Maven不引入本地仓库的依赖包. 解决方法: 进入自己的Maven ...

  2. Linux文件目录结构详解 (转)

      整理自<鸟哥的私房菜> 对于每一个Linux学习者来说,了解Linux文件系统的目录结构,是学好Linux的至关重要的一步.,深入了解linux文件目录结构的标准和每个目录的详细功能, ...

  3. Linux | 管首命令符号

    简介 管道的意思,在我们日常生活中,意思就是运输一个东西,到下一个地方,所以说 管道命令符 的使用也是差不多的,也是运送一段数据到下一个地方,格式:命令A | 命令B | 命令C .... 所以说,管 ...

  4. c++中的继承关系

    1 什么是继承 面向对象的继承关系指类之间的父子关系.用类图表示如下: 2 为什么要有继承?/ 继承的意义? 因为继承是面向对象中代码复用的一种手段.通过继承,可以获取父类的所有功能,也可以在子类中重 ...

  5. Django基础-003 配置Django自带的后台管理,操作数据库

    插入测试数据,可以自己写页面来插入数据 也可以使用Django自带的后台管理,来操作数据表 1.创建用户 python manage.py createsuperuser 2.在浏览器输入地址,进入D ...

  6. java使用IO读写文件

    https://www.cnblogs.com/qiaoyeye/p/5383723.html java读写文件的IO流分两大类,字节流和字符流,基类分别是字符:Reader和Writer:字节:In ...

  7. [刘阳Java]_Web前端入门级练习_迅雷官宣网设计

    也有一段时间没有写一些技术专题文章,今天给大家来几篇Web前端入门级练习的文章.主讲<迅雷官宣网>的设计,相信大家平时也用过迅雷这个下载工具,所以分享的这篇技术大家在一定程序会感兴趣. 先 ...

  8. .NET Core 中的 Swagger 应用与微服务场景下的Swagger Api 集成显示

    Swagger 与 OpenAPI 的历史来源: Swagger 项目于 2015 年捐赠给 OpenAPI Initiative,此后被称为 OpenAPI.这两个名称可以互换使用.但是," ...

  9. PAT乙级:1082 射击比赛 (20分)

    PAT乙级:1082 射击比赛 (20分) 题干 本题目给出的射击比赛的规则非常简单,谁打的弹洞距离靶心最近,谁就是冠军:谁差得最远,谁就是菜鸟.本题给出一系列弹洞的平面坐标(x,y),请你编写程序找 ...

  10. 第八篇--编写Windows服务

    编写service服务参考网址:https://blog.csdn.net/nodeathphoenix/article/details/24181509 vc获得显示器状态(捕获息屏.亮屏网址):h ...