Swift多叉树绘制
2023-04-18 12:48:00 时间
主要涉及一个遍历层级,以及遍历计算坐标
上码
结点
class NaryTreeNode {
var parent: NaryTreeNode?
var title: String?
var son: [NaryTreeNode]?
var level: Int = 0 // 层
var frame: CGRect?
}
复制代码
树配置
struct NaryTreeConfig {
var lineSpace: CGFloat = 30
var interspace: CGFloat = 30
var nodeSize = CGSize(width: 60, height: 60)
}
复制代码
树
class NaryTree: UIView {
var config = NaryTreeConfig() {
didSet {
setupNodeFrame(node: root)
}
}
var root: NaryTreeNode? {
didSet {
setupNodeFrame(node: root)
}
}
override func draw(_ rect: CGRect) {
super.draw(rect)
drawNode()
}
}
private extension NaryTree {
func drawNode() {
guard let root = root else { return }
var stack = [NaryTreeNode]()
stack.append(root)
drawCircle(frame: root.frame)
while !stack.isEmpty {
let node = stack.removeLast()
guard let son = node.son else { return }
for child in son {
stack.insert(child, at: 0)
drawCircle(frame: child.frame)
}
}
}
func drawCircle(frame: CGRect?) {
guard let context = UIGraphicsGetCurrentContext(),
let frame = frame
else {
return
}
UIColor.red.set()
let path = UIBezierPath(arcCenter: CGPoint(x: frame.midX, y: frame.midY), radius: config.nodeSize.width / 2, startAngle: 0, endAngle: 360, clockwise: true).cgPath
context.addPath(path)
context.fillPath()
}
}
private extension NaryTree {
func setupNodes() {
setupNodeFrame(node: root)
setNeedsDisplay()
}
func setupNodeFrame(node: NaryTreeNode?) {
var minX: CGFloat = -config.nodeSize.width - config.interspace
guard let node = node else { return }
levelOrder(node)
calculateFrame(node: node, minX: &minX)
}
func calculateFrame(node: NaryTreeNode, minX: inout CGFloat) {
guard let son = node.son,
son.isEmpty == false
else {
minX += config.nodeSize.width + config.interspace
let y = CGFloat(node.level) * config.lineSpace + CGFloat(node.level) * config.nodeSize.height
node.frame = CGRect(x: minX, y: y, width: config.nodeSize.width, height: config.nodeSize.height)
return
}
son.enumerated().forEach { _, child in
calculateFrame(node: child, minX: &minX)
}
guard let minx = son.first?.frame?.origin.x,
let maxX = son.last?.frame?.origin.x else { return }
let x = (minx + maxX) / 2.0
let y = CGFloat(node.level) * config.lineSpace + CGFloat(node.level) * config.nodeSize.height
node.frame = CGRect(x: x, y: y, width: config.nodeSize.width, height: config.nodeSize.height)
}
/// 层序遍历
func levelOrder(_ root: NaryTreeNode?) {
guard let root = root else { return }
var stack = [NaryTreeNode]()
stack.append(root)
while !stack.isEmpty {
let node = stack.removeLast()
guard let son = node.son else { return }
for child in son {
stack.insert(child, at: 0)
child.level = node.level + 1
}
}
}
}
相关文章
- [Abp 源码分析]一、Abp 框架启动流程分析
- 分布式与集群是一回事儿么?别让这么简单的问题难住你
- 利用 DynamicLinq 实现简单的动态表达式构建查询
- 复制粘贴一时爽:传播最广的一段Java代码曝出Bug
- 使用 C# 编写自己的区块链挖矿算法
- 在 Docker 当中搭建 docFX 站点
- 200 行代码使用 C# 实现区块链
- Linux 下 Shell 的自动交互
- 如何理解Session、Cookie、Token三者的区别与联系
- 【NCTS峰会回顾】云测学院陈霁:测试开发到测试架构之路
- 设计模式-建造者模式-创建型模式
- 使用 FRP 实现内网穿透
- 设计模式-抽象工厂-创建型模式
- 【NCTS峰会回顾】汽车之家闻小龙:QA团队精准测试实践之路
- 浅谈模块系统与 ABP 框架初始化
- 【NCTS峰会回顾】京东物流樊宇:如何让配送地址更准确——带你走近京东物流大数据算法测试探索之路
- 【NCTS峰会回顾】阿里羽瑶:基于图像智能算法的端上h5页面测试提效轻量化解决方案
- 设计模式-工厂方法-创建型模式
- 设计模式-简单工厂-创建型模式
- 设计模式-单例模式-创建型模式