j3141592653589793238
j3141592653589793238

Reputation: 1894

How to draw a line between two views in Swift 3?

I need help with drawing a simple line between two Outline Views in Swift 3 (Xcode 8). My situation:

Main ViewController
|--- Main View
     |--- Outline View
     |--- Outline View

So I Need help to get the coordinates of both Outline Views and draw a line with them (the line itself is not that difficult, more to get the coordinates). The goal is to draw a line (programmatically) that connects both Outline Views (f.ex. from one edge to the other, or from the top, ...).

I already tried following:

class Line: NSView{
    var origin = CGPoint()
    var destination = CGPoint()

    required init?(coder aDecoder: NSCoder){
        super.init(coder: aDecoder)
    }

    init(fromPoint: CGPoint, toPoint: CGPoint){
        self.origin = fromPoint
        self.destination = toPoint

        super.init(frame: CGRect(origin: fromPoint, size: CGSize(width: destination.x - origin.x, height: destination.y - origin.y)))
    }

    override func draw(_ dirtyRect: NSRect){
        let myPath = NSBezierPath()

        myPath.move(to: CGPoint(x: origin.x, y: origin.y))
        myPath.line(to: CGPoint(x: destination.x - origin.x, y: destination.y - origin.y))
        myPath.stroke()
    }
}

class ViewController: NSViewController{
    override func viewDidLoad(){
        super.viewDidLoad()

       let line = Line(fromPoint: self.view.convert(CGPoint.zero, to: self.view.viewWithTag(1)), toPoint: self.view.convert(CGPoint.zero, to: self.view.viewWithTag(2)))
        view.addSubview(line)
    }
}

But that didn't do anything.

I would appreciate your help!

Thank you

Upvotes: 2

Views: 2548

Answers (2)

j3141592653589793238
j3141592653589793238

Reputation: 1894

I now solved my problem (more or less) as following:

class Line: NSView{
    var fromPoint = CGPoint()
    var toPoint = CGPoint()

    func setPoints(fromPoint: CGPoint, toPoint: CGPoint){
        self.fromPoint = fromPoint
        self.toPoint = toPoint
    }

    override func draw(_ dirtyRect: NSRect) {
        let path = NSBezierPath()

        NSColor.green.setFill()
        path.move(to: fromPoint)
        path.line(to: toPoint)
        path.stroke()
    }
}


class ViewController: NSViewController{
     override function viewDidLoad(){
          super.viewDidLoad()

          let subview3 = Line(frame: self.view.bounds)
          subview3.setPoints(fromPoint: subview1.convert(CGPoint(x: subview1.bounds.maxX, y: subview1.bounds.maxY), to: self.view), toPoint: subview2.convert(CGPoint(x: subview2.bounds.minX, y: subview2.bounds.minY), to: self.view))
          self.view.addSubview(subview3)
     }
}

I need to know how to do this on runtime. Do I always have to create a new view in order to draw a path?

A full example:

//
//  ViewController.swift
//  DrawConnectViews
//
//  Created by T M on 17.06.17.
//  Copyright © 2017 TM. All rights reserved.
//

import Cocoa

class ViewController: NSViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let subview1 = CustomViewWithColor(frame: NSRect(origin: CGPoint(x: 10.0, y: 10.0), size: CGSize(width: 200.0, height: 200.0)))
        let subview2 = CustomViewWithColor(frame: NSRect(origin: CGPoint(x: 360.0, y: 360.0), size: CGSize(width: 200.0, height: 200.0)))

        // create a subview programatically:
        let subview3 = Line(frame: self.view.bounds)
        subview3.setPoints(fromPoint: subview1.convert(CGPoint(x: subview1.bounds.maxX, y: subview1.bounds.maxY), to: self.view), toPoint: subview2.convert(CGPoint(x: subview2.bounds.minX, y: subview2.bounds.minY), to: self.view))
        self.view.addSubview(subview3)

        subview1.setColor(color: NSColor.red)
        subview2.setColor(color: NSColor.blue)
        self.view.addSubview(subview1)
        self.view.addSubview(subview2)
    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }



}

class CustomViewWithColor: NSView{
    var color = NSColor()

    func setColor(color: NSColor){
        self.color = color
    }

    override func draw(_ dirtyRect: NSRect) {
        let path = NSBezierPath(rect: self.bounds)
        self.color.setFill()
        path.fill()



    }
}


class Line: NSView{
    var fromPoint = CGPoint()
    var toPoint = CGPoint()

    func setPoints(fromPoint: CGPoint, toPoint: CGPoint){
        self.fromPoint = fromPoint
        self.toPoint = toPoint
    }

    override func draw(_ dirtyRect: NSRect) {
        let path = NSBezierPath()

        NSColor.green.setFill()
        path.move(to: fromPoint)
        path.line(to: toPoint)
        path.stroke()
    }
}

That produces following: Output of program

Upvotes: 2

NRitH
NRitH

Reputation: 13893

A lot of people will think that this is overkill, but what I do is add a subview whose background is the line color, whose height is constrained to the desired line thickness, and whose leading and trailing edges are constrained to the superview's leading and trailing edges. This ensures that the border will always adjust with the superview's size, which is why adding a border as a layer or customizing the view's draw(in:) to draw the border as a path don't work as well.

Upvotes: 0

Related Questions