iOS新增绘制圆的方法实例代码
iOS 的坐标系和我们几何课本中的二维坐标系并不一样!
# BezierPath绘制圆弧
使用 UIBezierPath 进行绘制圆弧的方法,通常会直接使用 addArc :
addArc(withCenter:, radius:, startAngle:, endAngle:, clockwise:)
或者使用 addCurve 进行拟圆弧:
addCurve(to:, controlPoint1:, controlPoint2:)
其实我们可以通过,两个坐标点(startPoint & endPoint),和两点间的线段对应的圆弧的弧度(angle/radian)就能确定这个圆的信息(半径radius, center), 所以我们是不是可以封装出只提供 start, end 和 angle 就能绘制 arc 的函数?
addArc(startPoint: , endPoint: , angle: , clockwise:)
# 计算两点间的距离
这里逻辑很简单不做赘述。
func calculateLineLength(_ point1: CGPoint, _ point2: CGPoint) -> CGFloat { let w = point1.x - point2.x let h = point1.y - point2.y return sqrt(w * w + h * h) }
# 计算两点间的夹角
计算 point 和 origin 连线在 iOS 坐标系的角度
func calculateAngle(point: CGPoint, origin: CGPoint) -> Double { if point.y == origin.y { return point.x > origin.x ? 0.0 : -Double.pi } if point.x == origin.x { return point.y > origin.y ? Double.pi * 0.5 : Double.pi * -0.5 } // Note: 修正标准坐标系角度到 iOS 坐标系 let rotationAdjustment = Double.pi * 0.5 let offsetX = point.x - origin.x let offsetY = point.y - origin.y // Note: 使用 -offsetY 是因为 iOS 坐标系与标准坐标系的区别 if offsetY > 0 { return Double(atan(offsetX / -offsetY)) + rotationAdjustment } else { return Double(atan(offsetX / -offsetY)) - rotationAdjustment } }
# 计算圆心的坐标
如果你已经将几何知识丢的差不多了的话,我在这里画了个大概的草图,如下( angle 比较小时):
angle 比较大时:
所以我么可以写出如下计算中心点的代码
// Woring: 只计算从start到end **顺时针** 计算对应的 **小于π** 圆弧对应的圆心 // Note: 计算逆时针(end到start)可以看做将传入的start和end对调后计算顺时针时的圆心位置 // Note: 计算大于π的叫相当于将end和start对换后计算2π-angle的顺时针圆心位置 // Note: 综上传入start,end,angle 右外部自行处理逻辑 func calculateCenterFor(startPoint start: CGPoint, endPoint end: CGPoint, radian: Double) -> CGPoint { guard radian <= Double.pi else { fatalError("Does not support radian calculations greater than π!") } guard start != end else { fatalError("Start position and end position cannot be equal!") } if radian == Double.pi { let centerX = (end.x - start.x) * 0.5 + start.x let centerY = (end.y - start.y) * 0.5 + start.y return CGPoint(x: centerX, y: centerY) } let lineAB = calculateLineLength(start, end) // 平行 Y 轴 if start.x == end.x { let centerY = (end.y - start.y) * 0.5 + start.y let tanResult = CGFloat(tan(radian * 0.5)) let offsetX = lineAB * 0.5 / tanResult let centerX = start.x + offsetX * (start.y > end.y ? 1.0 : -1.0) return CGPoint(x: centerX, y: centerY) } // 平行 X 轴 if start.y == end.y { let centerX = (end.x - start.x) * 0.5 + start.x let tanResult = CGFloat(tan(radian * 0.5)) let offsetY = lineAB * 0.5 / tanResult let centerY = start.y + offsetY * (start.x < end.x ? 1.0 : -1.0) return CGPoint(x: centerX, y: centerY) } // 普通情况 // 计算半径 let radius = lineAB * 0.5 / CGFloat(sin(radian * 0.5)) // 计算与 Y 轴的夹角 let angleToYAxis = atan(abs(start.x - end.x) / abs(start.y - end.y)) let cacluteAngle = CGFloat(Double.pi - radian) * 0.5 - angleToYAxis // 偏移量 let offsetX = radius * sin(cacluteAngle) let offsetY = radius * cos(cacluteAngle) var centetX = end.x var centerY = end.y // 以 start 为原点判断象限区间(iOS坐标系) if end.x > start.x && end.y < start.y { // 第一象限 centetX = end.x + offsetX centerY = end.y + offsetY } else if end.x > start.x && end.y > start.y { // 第二象限 centetX = start.x - offsetX centerY = start.y + offsetY } else if end.x < start.x && end.y > start.y { // 第三象限 centetX = end.x - offsetX centerY = end.y - offsetY } else if end.x < start.x && end.y < start.y { // 第四象限 centetX = start.x + offsetX centerY = start.y - offsetY } return CGPoint(x: centetX, y: centerY) }
这里附上一个逆时针绘制第一张图中圆心位置的草图,图中已将 start 和 end 对换
如果你对其中计算时到底该使用 + 还是 - 有困惑的话也可以自己多画些草图大概验证下,总之有疑惑多动手🤭
# 实现我们的目标函数
在有了计算圆心位置,和两点间角度的函数后我们很容易就能实现 addArc(startPoint: , endPoint: , angle: , clockwise:) 了;
func addArc(startPoint start: CGPoint, endPoint end: CGPoint, angle: Double, clockwise: Bool) { guard start != end && (angle >= 0 && angle <= 2 * Double.pi) else { return } if angle == 0 { move(to: start) addLine(to: end) return } var tmpStart = start, tmpEnd = end, tmpAngle = angle // Note: 保证计算圆心时是从 start 到 end 顺时针 小于 π 的角 if tmpAngle > Double.pi { tmpAngle = 2 * Double.pi - tmpAngle (tmpStart, tmpEnd) = (tmpEnd, tmpStart) } if !clockwise { (tmpStart, tmpEnd) = (tmpEnd, tmpStart) } let center = calculateCenterFor(startPoint: tmpStart, endPoint: tmpEnd, radian: tmpAngle) let radius = calculateLineLength(start, center) var startAngle = calculateAngle(point: start, origin: center) var endAngle = calculateAngle(point: end, origin: center) // Note: 逆时针绘制则交换 startAngle 和 endAngle,并且将开始点移动的 end 位置 if !clockwise { (startAngle, endAngle) = (endAngle, startAngle) move(to: end) } addArc(withCenter: center, radius: radius, startAngle: CGFloat(startAngle), endAngle: CGFloat(endAngle), clockwise: true) move(to: end) }
# 完结
最后也不知道是你否会碰到相同的需求,这里附上源码和一份样例及运行结果图;
override func draw(_ rect: CGRect) { let path = UIBezierPath() var start = CGPoint(x: 160, y: 130) var end = CGPoint(x: 180, y: 200) path.move(to: start) path.addArc(startPoint: start, endPoint: end, angle: Double.pi * 1.6, clockwise: true) path.move(to: start) path.addArc(startPoint: start, endPoint: end, angle: Double.pi * 0.8, clockwise: true) start = CGPoint(x: 142, y: 130) end = CGPoint(x: 162, y: 200) path.move(to: start) path.addArc(startPoint: start, endPoint: end, angle: Double.pi * 0.4, clockwise: true) start = CGPoint(x: 140, y: 130) end = CGPoint(x: 160, y: 200) path.move(to: start) path.addArc(startPoint: start, endPoint: end, angle: Double.pi * 1.6, clockwise: false) path.move(to: start) path.addArc(startPoint: start, endPoint: end, angle: Double.pi * 0.8, clockwise: false) path.close() path.lineWidth = 1 UIColor.red.setStroke() path.stroke() }
ps: 每次都写 Double.pi / x 很烦? 试试类似于 SwiftUI 提供的接口, 使用 度数(degress) 而非 弧度(radian)
struct Angle { private var degress: Double static func deggess(_ degress: Double) -> Angle { return .init(degress: degress) } // 弧度 var radians: Double { Double.pi * degress / 180.0 } } // Angle.deggess(90).radians // 1.570796326794897
func addArc(startPoint start: CGPoint, endPoint end: CGPoint, angle: Angle, clockwise: Bool)
感谢阅读,祝好祝顺🥰
总结
到此这篇关于iOS新增绘制圆的文章就介绍到这了,更多相关iOS新增绘制圆内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!
相关文章
- 今天小编在这里就来给Painter的这一款软件的使用者们来说一下绘制帅气卡通魔法王子漫画的具体教程,各位想知道绘制步骤的使用者,那么下面就快来跟着小编一起看一看教程...2016-09-14
- 这篇文章主要介绍了MySQL性能监控软件Nagios的安装及配置教程,这里以CentOS操作系统为环境进行演示,需要的朋友可以参考下...2015-12-14
- 这篇文章主要介绍了C#绘制曲线图的方法,以完整实例形式较为详细的分析了C#进行曲线绘制的具体步骤与相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
iOS APP h5快捷程序 .mobileconfig的生成
1.从APP Store 下载Apple Configurator 2从一个管理点管理所有iOS设备应用程序,文档和配置文件。想要确保您的所有家庭成员在其每台iOS设备上都有类似的应用和文档,管理日益增...2021-12-23- 今天小编在这里就来给Painter的这一款软件的使用者们来说一下绘制雷神传插画的教程,各位想知道具体绘制步骤的使用者,那么下面就快来跟着小编一起看看绘制方法吧。 ...2016-09-14
iOS设置UIButton文字显示位置和字体大小、颜色的方法
这篇文章给大家分享了iOS如何设置UIButton的文字显示位置和字体的大小、颜色,文中给出了示例代码,相信对大家的学习和理解很有帮助,有需要的朋友们下面来一起看看吧。...2020-06-30- 这篇文章主要为大家详细介绍了iOS如何将图片裁剪成圆形,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-06-30
- 这篇文章主要给大家介绍了关于iOS给border设置渐变色的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-09
- 这篇文章主要介绍了iOS新版微信底部返回横条问题的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-30
- 这篇文章主要介绍了python进行相关性分析并绘制散点图,具有一定借鉴价值,需要的朋友可以参考下,希望能够给你带来帮助...2021-09-18
- 这篇文章主要介绍了C#实现绘制面形图表的方法,对于C#初学者很好的掌握C#图形绘制有一定的借鉴价值,需要的朋友可以参考下...2020-06-25
- 这篇文章主要介绍了vue+axios全局添加请求头和参数操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-07-24
- 这篇文章主要介绍了C#绘制中国国旗的方法,以实例形式较为详细的分析了C#图形绘制的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
- 这篇文章主要给大家介绍了关于iOS蓝牙设备名称缓存问题的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-12-08
- 这篇文章主要介绍了iOS新版微信底部工具栏遮挡问题完美解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-30
- 这篇文章主要给大家介绍了关于iOS新增绘制圆的方法,文中通过示例代码介绍的非常详细,对各位iOS开发者们具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧...2020-06-30
- 这篇文章主要介绍了如何在CocosCreator里画个炫酷的雷达图,对Graphics感兴趣的同学,一定要看看,并且把代码实践一下...2021-04-16
- 这篇文章主要为大家详细介绍了iOS UIBezierPath实现饼状图,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-03-20
- 这篇文章主要介绍了封装 axios+promise通用请求函数操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-12
- ios获取文件路径的方法,iphone沙箱模型的四个文件夹,通过documents,tmp,app,Library得到模拟器路径的简单方式,下面小编整理相关资料,把IOS获取各种文件目录路径的方式总结如下,需要的朋友可以参考下...2020-06-30