Home > Enterprise >  Draw Shape over another Shape
Draw Shape over another Shape

Time:10-05

Problem :

I cannot draw a shape on another shape.

What I am trying to achieve :

Draw circles on the line.

Anyway, the circle is shifting the line. I didn't find a way to make it as swift UI seems relatively new. I am currently learning swift and I prefer swift UI rater than storyboard.

If circle and line are different struct, this is because I want to reuse the shape later on.

There is the code :

import SwiftUI

public var PointArray = [CGPoint]()
public var PointArrayInit:Bool = false

struct Arc: Shape {
    var startAngle: Angle
    var endAngle: Angle
    var clockwise: Bool
    var centerCustom:CGPoint

    func path(in rect: CGRect) -> Path {
        let rotationAdjustment = Angle.degrees(90)
        let modifiedStart = startAngle - rotationAdjustment
        let modifiedEnd = endAngle - rotationAdjustment

        var path = Path()
        path.addArc(center: CGPoint(x: rect.midX, y: rect.midY), radius: 20, startAngle: modifiedStart, endAngle: modifiedEnd, clockwise: !clockwise)

        return path
    }
}

struct CurveCustomInit: Shape {
    private var Divider:Int = 10
    
    func path(in rect: CGRect) -> Path {
        var path = Path()
        let xStep:CGFloat = DrawingZoneWidth / CGFloat(Divider)
        let yStep:CGFloat = DrawingZoneHeight / 2
        var xStepLoopIncrement:CGFloat = 0
        path.move(to: CGPoint(x: 0, y: yStep))

        for _ in 0...Divider {
            let Point:CGPoint = CGPoint(x: xStepLoopIncrement, y: yStep)
            PointArray.append(Point)
            path.addLine(to: Point)
            xStepLoopIncrement  = xStep
        }

        PointArrayInit = true
        return (path)
    }
}

struct TouchCurveBasic: View {

    var body: some View {
        if !PointArrayInit {
            
            Arc(startAngle: .degrees(0), endAngle: .degrees(360), clockwise: true, centerCustom: CGPoint(x: 50, y: 400))
                .stroke(Color.blue, lineWidth: 4)

            CurveCustomInit()
                .stroke(Color.red, style: StrokeStyle(lineWidth: 10, lineCap: .round, lineJoin: .round))
                .frame(width: 300, height: 300)
        } else {
            
        }
    }
}

struct TouchCurveBasic_Previews: PreviewProvider {
    static var previews: some View {
        TouchCurveBasic()
    }
}

There is what I get : enter image description here

CodePudding user response:

Here is an other way for you, you can limit the size of drawing with giving a frame or you can use the available size of view without limiting it or even you can use the current limit coming from parent and updated it, like i did on drawing the Line. The method that I used was overlay modifier.

struct ContentView: View {
    
    var body: some View {
        
        ArcView(radius: 30.0)
            .stroke(lineWidth: 10)
            .foregroundColor(.blue)
            .frame(width: 60, height: 60)
            .overlay(LineView().stroke(lineWidth: 10).foregroundColor(.red).frame(width: 400))

    }
    
}

struct ArcView: Shape {
    
    let radius: CGFloat

    func path(in rect: CGRect) -> Path {

        return Path { path in
            
            path.addArc(center: CGPoint(x: rect.midX, y: rect.midY), radius: radius, startAngle: Angle(degrees: 0.0), endAngle: Angle(degrees: 360.0), clockwise: true)
            
        }

    }
}

struct LineView: Shape {

    func path(in rect: CGRect) -> Path {
        
        return Path { path in
            
            path.move(to: CGPoint(x: rect.minX, y: rect.midY))
            path.addLines([CGPoint(x: rect.minX, y: rect.midY), CGPoint(x: rect.maxX, y: rect.midY)])
 
        }

    }
}

result:

enter image description here

CodePudding user response:

If you use a ZStack, the views will be stacked on top of each other in the Z axis. For example:

ZStack {
    Arc(startAngle: .degrees(0), endAngle: .degrees(360), clockwise: true, centerCustom: CGPoint(x: 50, y: 400))
        .stroke(Color.blue, lineWidth: 4)

    CurveCustomInit()
        .stroke(Color.red, style: StrokeStyle(lineWidth: 10, lineCap: .round, lineJoin: .round))
        .frame(width: 300, height: 300)
}

enter image description here

(I replaced DrawingZoneWidth with rect.width and the same change with the height, since those weren't included in your code).

Although the above should address your main issue, there are a couple of SwiftUI things to be aware of that I see in your code:

  1. You're storing PointArray outside your views and then appending to it in the CurveCustomInit path. You should never be altering your state inside a path or body method like this.

  2. Type names in Swift start generally start with capital letters. Variable names generally start with lowercase letters.

  3. You're relying on static numbers DrawingZoneWidth and DrawingZoneHeight (which aren't included in your code) that probably aren't going to be properly responsive. In Shapes, you can just use the size of the rect to determine the size that you should draw in. Also, try to avoid reading the screen size whenever possible. When you need to, look into GeometryReader

  4. Much like the above, I question the use of .frame(width: 300, height: 300) on the CurveCustomInit -- this may break the relationship to Arc depending on the center points, etc. Try for a more responsive design.

  • Related