Search code examples
iosswiftuibezierpathuitouch

How to transform UIBezierPath with UITouch


i am trying to change the shape of UIBezierPath that i have created like in this tutorial: [https://www.appcoda.com/bezier-paths-introduction/] thats my code:

class Geometry: UIView {

    //var path: UIBezierPath!
    var path = UIBezierPath()

    override func draw(_ rect: CGRect) {
        createLine(x: 100, y: 100)
    }
    override init(frame: CGRect) {
        super.init(frame: frame)

        self.backgroundColor = UIColor.white
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    func transformShape(x:CGFloat, y:CGFloat)
    {
        path.removeAllPoints()
        createLine(x: x, y: y)
    }
    func createLine(x: CGFloat, y: CGFloat)
    {
        var path = UIBezierPath()
        path.move(to: CGPoint(x: 0.0, y: 0.0))
        path.addLine(to: CGPoint(x: x, y: y))
        path.close()
        path.fill()
        path.stroke()
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
    {
        let touch = touches.first
        let location = (touch?.location(in: self))!;
        transformShape(x:location.x, y:location.y)
    }
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
    {
        let touch = touches.first
        let location = (touch?.location(in: self))!;
        transformShape(x:location.x, y:location.y)
    }
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
    {
        let touch = touches.first
        let location = (touch?.location(in: self))!;
        transformShape(x:location.x, y:location.y)
    }
}

i have added the the view in the viewcontroller like this:

import UIKit

var geo = Geometry()

let screenWidth: Int = 1024
let screenHeight: Int = 768

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        geo = Geometry(frame: CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight))
        self.view.addSubview(geo)

    }
}

to make it easier to understand what i want to do i made this. i want to be able to move the end of both lines to the left and right on x without changing the y-postion

enter image description here

i have not found anything besides animation. but thats not what i want to accomplish. the top of the triangle should follow the movements of the finger. thanks


Solution

  • It certainly isn't difficult to do this in a crude way:

    enter image description here

    Here's how I'm doing that:

    class MyView : UIView {
        override init(frame: CGRect) {
            super.init(frame:frame)
            backgroundColor = .yellow
            let g = UIPanGestureRecognizer(target: self, action: #selector(pan))
            self.addGestureRecognizer(g)
        }
        @objc
        func pan(_ g:UIGestureRecognizer) {
            switch g.state {
            case .changed:
                let p = g.location(in: self)
                self.apex.x = p.x
                self.setNeedsDisplay()
            default: break
            }
        }
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        lazy var apex = CGPoint(x:self.bounds.midX, y:self.bounds.midY)
        override func draw(_ rect: CGRect) {
            let con = UIGraphicsGetCurrentContext()!
            con.move(to: CGPoint(x: 0, y: self.bounds.maxY))
            con.addLine(to: self.apex)
            con.addLine(to: CGPoint(x: self.bounds.maxX, y: self.bounds.maxY))
            con.strokePath()
        }
    }
    

    In that code, I'm simply setting the apex point to where the finger is (keeping the y-component constant, as you specify), and redrawing the whole bezier path. So everything from there is just a question of refinement — reducing latency, for example (which you can do by anticipating touches).