0.3 C
New York
Sunday, February 23, 2025

ios – How can I make sure that letters are positioned upright alongside a user-drawn path in SwiftUI?


I’m engaged on a SwiftUI view the place the person can draw a customized path, and I wish to place letters of a string alongside that path. The letters ought to be aligned perpendicularly to the trail, guaranteeing that the textual content seems to be standing upright alongside the drawn floor (whether or not the trail is straight, sloped, or curvy). Nonetheless, the angle of the letters is typically not as anticipated, particularly when the trail is sloped or curved.

Right here’s the code I’ve written to this point to attain this:

import SwiftUI

struct DraggingLettersView: View {
    @State non-public var letters: [DraggedLetter] = []
    @State non-public var currentIndex = 0
    @State non-public var dragPath: [CGPoint] = []
    
    non-public let longString = "Let me know if you happen to face any additional points! 😊"
    non-public let letterSpacing: CGFloat = 15

    var physique: some View {
        GeometryReader { geometry in
            ZStack {
                // Draw the drag path
                Path { path in
                    for (index, level) in dragPath.enumerated() {
                        if index == 0 {
                            path.transfer(to: level)
                        } else {
                            path.addLine(to: level)
                        }
                    }
                }
                .stroke(Coloration.blue, lineWidth: 2)
                
                // Place letters alongside the trail
                ForEach(letters) { letter in
                    Textual content(String(letter.character))
                        .font(.system(dimension: 24, weight: .daring))
                        .foregroundColor(.white)
                        .rotationEffect(.levels(letter.angle))
                        .place(letter.place)
                }
                
                // Clear Button
                Button(motion: {
                    letters.removeAll()
                    currentIndex = 0
                    dragPath.removeAll()
                }, label: {
                    Textual content("Clear")
                        .body(width: geometry.dimension.width * 0.2 , top: geometry.dimension.top * 0.05)
                        .background(Coloration.white)
                        .cornerRadius(10)
                        .shadow(radius: 5)
                })
                .place(x: geometry.dimension.width - 60, y: 80)
            }
            .body(width: geometry.dimension.width, top: geometry.dimension.top)
            .background(Coloration.grey)
            .gesture(
                DragGesture(minimumDistance: 0)
                    .onChanged { worth in
                        handleDrag(worth.location)
                    }
                    .onEnded { worth in
                        placeTextAlongPath()
                    }
            )
        }
        .background(Coloration.white)
        .edgesIgnoringSafeArea(.all)
    }

    // MARK: - Deal with Drag
    non-public func handleDrag(_ place: CGPoint) {
        dragPath.append(place)
    }

    // MARK: - Place Textual content Alongside Path
    non-public func placeTextAlongPath() {
        letters.removeAll()
        
        let pathLength = calculatePathLength()
        
        let numberOfLetters = Int(pathLength / letterSpacing)
        
        for i in 0.. CGFloat {
        var size: CGFloat = 0
        
        for i in 1.. CGPoint {
        guard dragPath.rely > 1 else { return dragPath.first ?? .zero }
        
        let pathLength = calculatePathLength()
        let segmentLength = CGFloat(index) * letterSpacing
        
        var accumulatedLength: CGFloat = 0
        for i in 1..= segmentLength {
                let ratio = (segmentLength - (accumulatedLength - segmentDist)) / segmentDist
                let x = begin.x + ratio * (finish.x - begin.x)
                let y = begin.y + ratio * (finish.y - begin.y)
                return CGPoint(x: x, y: y)
            }
        }
        
        return dragPath.final ?? .zero
    }

    // MARK: - Calculate Perpendicular Angle (Angle primarily based on drag path)
    non-public func calculatePerpendicularAngle(from index: Int) -> Double {
        guard dragPath.rely > 1 else { return 0 }

        guard dragPath.rely > index else { return 0 }

        let begin = dragPath[index]
        let finish = dragPath[index + 1 < dragPath.count ? index + 1 : index]

        let dx = finish.x - begin.x
        let dy = finish.y - begin.y
        
        let perpDx = -dy
        let perpDy = dx

        let angle = atan2(perpDy, perpDx) * 180 / .pi

        return angle - 90.0
    }
}

// MARK: - Dragged Letter Mannequin
struct DraggedLetter: Identifiable {
    let id = UUID()
    let character: Character
    let place: CGPoint
    let angle: Double
}

The problem:
The textual content is positioned alongside the trail accurately, however the angle of the textual content doesn’t appear to match the floor of the trail, particularly on sloped or curved paths.
The letters ought to be standing “on the floor” of the trail, whatever the path’s path. The textual content ought to at all times face the “regular” or “perpendicular” path of the trail at every level.

What I’ve tried:
I calculate the angle primarily based on the path of the trail between two factors and use the perpendicular vector to set the angle for every letter.
The letters rotate most often, however on sure slopes or curves, the textual content nonetheless does not align as anticipated.

What I’m searching for:
Assist with guaranteeing the letters at all times face perpendicular to the trail at each level, particularly on sloped or curved paths.
A method to make sure the letters at all times look like standing upright, irrespective of the form or slope of the drawn path.

what I need you possibly can see on this picture I need textual content keep hooked up at path floor

enter image description here

on plain floor it is little bit okay however whereas in slop or round path it isn’t correct

enter image description here

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles