根据this Ray Wenderlich的文章,我能够创建像这样的二叉树数据结构:
package variableScope;
import java.awt.Toolkit;
import java.awt.event.ActionListener;
import javax.swing.Timer;
public class VariableScopeTest {
public static void main(String[] args) {
repeatMessage("Hello", 2000);
}
private static void repeatMessage(String text, int delay) {
ActionListener listener = event -> {
System.out.println(text);
Toolkit.getDefaultToolkit().beep();
};
new Timer(delay, listener).start();
}
}
我在此处找到了很多示例,以可视化Android中的此类树 Graphical binary tree in Android或PHP draw binary tree with php,但在Swift中什么都没有。我考虑过Core Graphics库,但是从哪里开始呢?谁能给我一个例子吗?
答案 0 :(得分:1)
关于如何画线的基础知识,您:
UIBezierPath
; move(to:)
移至起点; addLine(to:)
将线添加到终点; 然后您可以通过以下任一方式在用户界面中呈现该路径:
CAShapeLayer
,并指定其strokeWidth
,strokeColor
和fillColor
;设置其path
,然后将该形状图层添加为视图的layer
的子图层;或UIView
子类,然后在其draw(_:)
方法中调用所需setStroke
的{{1}},设置UIColor
的{{1}} ,然后lineWidth
和UIBezierPath
。通常,我会使用stroke()
方法,基本上配置形状层,但让操作系统为我呈现该形状层。
话虽如此,我可能会更进一步,并将线条图包装在其自己的UIBezierPath
子类中。思想过程不仅是通常由CAShapeLayer
对象组成的高级视图,而且还为各种高级UX打开了大门(例如,您可能希望检测节点上的点击并执行某些操作)
无论如何,我会将“连接线”绘图代码包装在UIView
子类中,如下所示:
UIView
这定义了形状图层,以将线从一个角笔划到另一个角,当此视图的UIView
更改时,它将相应地自动更新。这样,我现在可以通过更新此class ConnectorView: UIView {
enum ConnectorType {
case upperRightToLowerLeft
case upperLeftToLowerRight
case vertical
}
var connectorType: ConnectorType = .upperLeftToLowerRight { didSet { layoutIfNeeded() } }
override class var layerClass: AnyClass { return CAShapeLayer.self }
var shapeLayer: CAShapeLayer { return layer as! CAShapeLayer }
convenience init(connectorType: ConnectorType) {
self.init()
self.connectorType = connectorType
}
override init(frame: CGRect = .zero) {
super.init(frame: frame)
configure()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
override func layoutSubviews() {
let path = UIBezierPath()
switch connectorType {
case .upperLeftToLowerRight:
path.move(to: CGPoint(x: bounds.minX, y: bounds.minY))
path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
case .upperRightToLowerLeft:
path.move(to: CGPoint(x: bounds.maxX, y: bounds.minY))
path.addLine(to: CGPoint(x: bounds.minX, y: bounds.maxY))
case .vertical:
path.move(to: CGPoint(x: bounds.midX, y: bounds.minY))
path.addLine(to: CGPoint(x: bounds.midX, y: bounds.maxY))
}
shapeLayer.path = path.cgPath
}
override var description: String { return String(format: "<ConnectorView: %p; frame = %@, type = %@", self, frame.debugDescription, connectorType.string) }
}
private extension ConnectorView {
func configure() {
shapeLayer.lineWidth = 3
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
}
}
子类的frame
来控制在哪里显示连接器线视图。这种方法的优点在于,我现在可以为此frame
定义约束,以使顶部/底部/左侧/右侧锚点与{{的UIView
和ConnectorView
1}}。通过将节点置于这些连接器线视图的前面,可以得到所需的外观。
仅供参考,对于简单的矩形节点,您可能只是将节点本身归为centerX
:
centerY
现在,诀窍在于放置节点的位置,以便为其所有子节点留出足够的空间。如果您是iOS约束系统的新手,那么它将看起来超级混乱(坦率地说,即使您熟悉它,也有点难看),但是您可以执行以下操作:
UIView
这看起来很难看,但是我认为这比编写自己的基于规则的节点定位引擎要好。但是这些约束的规则捕获了一些基本的“规则”:
每个节点都有特定的固定大小。
每个节点到下面的水平都有一定距离。
当一个节点有子节点时,将该节点居于两个子节点上方,并以一定的固定距离隔开这些子节点。
在间隔对等节点时,将整个二叉树包装在容器视图中的给定节点下,并将其用于间隔。因此,查看下级节点之一,即二叉树左侧的UILabel
,其子级的容器视图如下:
当查看其上方的节点时,其容器不仅包含两个直接子节点,还包含它们的容器:
最终结果是一个二叉树,其中所有子节点都具有合理的间距,但父节点仍位于两个直接子节点的中心。
无论如何,视图控制器可以像这样调用上面的代码:
class NodeView: UILabel {
weak var containerView: UIView!
override init(frame: CGRect = .zero) {
super.init(frame: frame)
configure()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
}
private extension NodeView {
func configure() {
backgroundColor = UIColor.white
layer.borderColor = UIColor.black.cgColor
layer.borderWidth = 3
textAlignment = .center
}
}
屈服:
答案 1 :(得分:1)
这非常简单,创建html页面,它将基于此链接绘制树: http://fperucic.github.io/treant-js/
将生成的HTML字符串加载到UIWebView中 Insert CSS into loaded HTML in UIWebView / WKWebView
如果您也在使用Android程序,则可以使用相同的技术。
您还可以通过Swift处理UIWebView内部的事件: Xcode, Swift; Detect hyperlink click in UIWebView