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博主文顶顶的博客这样内容图文并茂,代码齐全,示例经典,原理也有阐述,覆盖面宽广,自成系统的系列教程 ...
随机推荐
- python 正则表达式 初级
举例: 1.匹配hello world key = r"<h1>hello world<h1>" #源文本 p1 = r"<h1>.+ ...
- HADOOP及SPARK安装步骤及问题解决
说明:主节点IP:192.168.35.134 主机名:master 从节点slave1 IP: 192.168.35.135 主机名:slave1 从节点slave2 IP: 192.168 ...
- buu [MRCTF2020]EasyCpp
上次没写出,这次认真分析了一下,发现自己的调试水平也有了上涨,也看了一些C++逆向的文章,尤其是stl,发现C++的oop还是挺复杂,这题还没考啥虚函数的还行了. 一.拖入ida,找到主函数,还是挺容 ...
- python 动态指定header获取网页源代码的函数
import random import requests def get_htmla(url): aui=0 while aui==0: try: header={'User-Agent':'Moz ...
- 高校表白App-团队冲刺第四天
今天要做什么 就如昨天所说,今天继续进行引导页制作的学习.并开始通过ViewPager做简单的布局与Activity. 遇到的问题 本来以为只是使用一个ViewPager控件就可以搞定,原来还是需要配 ...
- 记录Jackson和Lombok的坑
记录Jackson和Lombok的坑 今天遇到Jackson反序列化json缺少了字段,后来研究下发现是Jackson的机制和Lombok生成的setter不一致,导致没有正确调用setter. 复现 ...
- react应用(基于react脚手架)
使用create-react-app创建react应用 react脚手架 1) xxx脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目 a. 包含了所有需要的配置 b. 指定好了所有的依赖 ...
- redis数据类型及应用场景
0.key的通用操作 KEYS * keys a keys a* 查看已存在所有键的名字 ****TYPE 返回键所存储值的类型 ****EXPIRE\ PEXPIRE 以秒\毫秒设定生存时间 *** ...
- js排序——sort()排序用法
sort() 方法用于对数组的元素进行排序,并返回数组.默认排序顺序是根据字符串Unicode码点. 语法:array.sort(fun):参数fun可选.规定排序顺序.必须是函数.注:如果调用该方法 ...
- GhostScript 沙箱绕过(命令执行)漏洞(CVE-2019-6116)
影响范围 Ghostscript 9.24之前版本 poc地址:https://github.com/vulhub/vulhub/blob/master/ghostscript/CVE-2019-61 ...