如何在Swift 4中绘制二叉树?

时间:2019-04-07 06:04:33

标签: swift binary-tree core-graphics

根据this Ray Wenderlich的文章,我能够创建像这样的二叉树数据结构:

enter image description here

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库,但是从哪里开始呢?谁能给我一个例子吗?

2 个答案:

答案 0 :(得分:1)

关于如何画线的基础知识,您:

  1. 创建UIBezierPath;
  2. 使用move(to:)移至起点;
  3. 使用addLine(to:)将线添加到终点;

然后您可以通过以下任一方式在用户界面中呈现该路径:

  • 创建CAShapeLayer,并指定其strokeWidthstrokeColorfillColor;设置其path,然后将该形状图层添加为视图的layer的子图层;或
  • 创建UIView子类,然后在其draw(_:)方法中调用所需setStroke的{​​{1}},设置UIColor的{​​{1}} ,然后lineWidthUIBezierPath

通常,我会使用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定义约束,以使顶部/底部/左侧/右侧锚点与{{的UIViewConnectorView 1}}。通过将节点置于这些连接器线视图的前面,可以得到所需的外观。

仅供参考,对于简单的矩形节点,您可能只是将节点本身归为centerX

centerY

现在,诀窍在于放置节点的位置,以便为其所有子节点留出足够的空间。如果您是iOS约束系统的新手,那么它将看起来超级混乱(坦率地说,即使您熟悉它,也有点难看),但是您可以执行以下操作:

UIView

这看起来很难看,但是我认为这比编写自己的基于规则的节点定位引擎要好。但是这些约束的规则捕获了一些基本的“规则”:

  1. 每个节点都有特定的固定大小。

  2. 每个节点到下面的水平都有一定距离。

  3. 当一个节点有子节点时,将该节点居于两个子节点上方,并以一定的固定距离隔开这些子节点。

  4. 在间隔对等节点时,将整个二叉树包装在容器视图中的给定节点下,并将其用于间隔。因此,查看下级节点之一,即二叉树左侧的UILabel,其子级的容器视图如下:

    enter image description here

    当查看其上方的节点时,其容器不仅包含两个直接子节点,还包含它们的容器:

    enter image description here

    最终结果是一个二叉树,其中所有子节点都具有合理的间距,但父节点仍位于两个直接子节点的中心。

无论如何,视图控制器可以像这样调用上面的代码:

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
    }
}

屈服:

enter image description here

答案 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