iOS开发之转盘菜单
前言
使用Swift实现的转盘菜单,主要用到UIBezierPath
、CALayer
遮罩绘制扇形UIView
,CATransform3DMakeRotation
实现旋转动画。代码设计使用默认configureCallback
回调方便创建和设置基本属性,参考UITableView
代理和数据源模式,支持AutoLayout
和Frame
。
效果图
1.遮罩绘制扇形View
计算扇形曲线位置,通过CALayer
的mask
属性绘制出扇形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.旋转动画
添加UIPanGestureRecognizer
、UITapGestureRecognizer
手势,根据手势位置使用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开发之转盘菜单的更多相关文章
- ios开发之级联菜单(两个tableView实现)
一:在ios项目实际开发中经常会看到级联菜单的效果:如图:点击左侧菜单,右侧菜单刷新数据.此篇用两个tableView来实现如图效果: 二:代码: 1:构造数据模型:利用kvc快速构建数据模型 #im ...
- iOS开发系列--App扩展开发
概述 从iOS 8 开始Apple引入了扩展(Extension)用于增强系统应用服务和应用之间的交互.它的出现让自定义键盘.系统分享集成等这些依靠系统服务的开发变成了可能.WWDC 2016上众多更 ...
- iOS开发——创建你自己的Framework
如果你想将你开发的控件与别人分享,一种方法是直接提供源代码文件.然而,这种方法并不是很优雅.它会暴露所有的实现细节,而这些实现你可能并不想开源出来.此外,开发者也可能并不想看到你的所有代码,因为他们可 ...
- IOS开发基础知识碎片-导航
1:IOS开发基础知识--碎片1 a:NSString与NSInteger的互换 b:Objective-c中集合里面不能存放基础类型,比如int string float等,只能把它们转化成对象才可 ...
- iOS开发系列--IOS程序开发概览
概览 终于到了真正接触IOS应用程序的时刻了,之前我们花了很多时间去讨论C语言.ObjC等知识,对于很多朋友而言开发IOS第一天就想直接看到成果,看到可以运行的IOS程序.但是这里我想强调一下,前面的 ...
- IOS开发基础知识--碎片11
1:AFNetwork判断网络状态 #import “AFNetworkActivityIndicatorManager.h" - (BOOL)application:(UIApplicat ...
- IOS开发基础知识--碎片42
1:报thread 1:exc_bad_access(code=1,address=0x70********) 闪退 这种错误通常是内存管理的问题,一般是访问了已经释放的对象导致的,可以开启僵尸对象( ...
- iOS 开发之使用safari对webview进行调试
转自:http://www.tuicool.com/articles/ZBFnUbz 使用safari对webview进行调试 时间 2016-02-25 14:35:20 陈斌彬的技术博客 原文 ...
- 文顶顶iOS开发博客链接整理及部分项目源代码下载
文顶顶iOS开发博客链接整理及部分项目源代码下载 网上的iOS开发的教程很多,但是像cnblogs博主文顶顶的博客这样内容图文并茂,代码齐全,示例经典,原理也有阐述,覆盖面宽广,自成系统的系列教程 ...
随机推荐
- easyswoole实现线上更新代码
众所周知,easyswoole作为常驻内存的框架,修改代码并不能直接生效,而是需要重启服务,那么,当你的easyswoole项目上线之后,该如何保证旧请求的同时去更新代码呢? nginx reload ...
- F5负载均衡_monitors(健康检查)
故障现象: 后端有5台服务器,每个服务器上跑着8个应用.使用F5做应用负载调度.这40个应用里面,3-10个应用在高峰期的时候weblogic的DOS窗口显示与数据库断开连接(端口通.业务断),但是F ...
- java基础---类和对象(3)
一.Object类 java.lang.Object类是Java语言中类层次结构的根类,也就是说任何一个类都是该类的直接或者间接子类 如果定义一个Java类时没有使用extends关键字声明其父类,则 ...
- 「CF559E」 Gerald and Path
「CF559E」 Gerald and Path 为啥我现在做啥题都在想网络流啊 考虑 \(\texttt{DP}\). 容易想到状态应该包含当前枚举了前 \(i\) 条线段,且第 \(i\) 条线段 ...
- python使用笔记17--异常处理
什么是异常? 异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行. 一般情况下,在Python无法正常处理程序时就会发生一个异常. 异常是Python对象,表示一个错误. 当Pyth ...
- 一行代码打印python之禅
就这一句: import this 输出: The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is be ...
- [005] - JavaSE面试题(五):String类
第一期:Java面试 - 100题,梳理各大网站优秀面试题.大家可以跟着我一起来刷刷Java理论知识 [005] - JavaSE面试题(五):String类 第1问:String.StringBuf ...
- awk对某个字段分割处理
工作中遇到要根据文件中某个字段分割成多行文本的处理,想到用awk处理,这里记录下: 问题: 原文件:假设一共2个字段,用"|"分割,其中第二个字段用"#"分割, ...
- Three.js-任意平面的镜像矩阵
1. 什么是镜像变换 直接看下面这张图: 这张图很好的诠释了镜像变化,关于y轴的变化,关于x轴的变化.这种关于任意轴的变化,就是镜像了. 2d下的镜像矩阵变化 我们以图像关于Y轴镜像为例子:原图形和结 ...
- linux下系统时间和时钟时间
linux中有关系统时间.时钟时间的命令: 1显示系统时间的命令 ># date 2显示时钟时间的命令 ># clock或hwclock 3系统时间与互联网同步的命令 ># ntpd ...