Search code examples
iosswiftuipangesturerecognizer

how to pan after the pan gesture moves certain amounts


I need help ,I made a view that I divided with lines and when I touch it make a rect between the lines, like this image,enter image description here

and I made a pan gesture to move the rects that I draw, between the lines, it works but thats not what I want ,when I move my finger little bit it moves and thats the problems, even when I move just the y it recognise the smallest amount of x and move both, I need to move my rect after the pan is more than the divisions then move the rect ,i want when I select my rect and move my finger to the division upper or lower or left or right one from the existing one that I'm in then move. so I came up with this but it doesn't work the right way, the height of the division is noteheight and the width between them is timeSlotWidtho ,

@objc func handlePanGesture(_ gestureRecognizer: UIPanGestureRecognizer) {
    
    let touchLocation = gestureRecognizer.location(in: self)
    let location = touchLocation
    let timeSlotWidtho = bounds.width / CGFloat(numT)
    let note = CGFloat(numN) - location.y / noteHeight
    let startTime = Double(Int(location.x / timeSlotWidtho))
    let duration = Double(numT) / Double(numT)
    let noteHeight = bounds.height / CGFloat(numN)  
  
     var lastPanY: CGFloat = 0
     var lastPanx: CGFloat = 0

    switch gestureRecognizer.state {
        
    case .began:
        // Check if the user has touched a note rectangle
        
        // Check if a note with the same parameters already exists
        for (index, existingNote) in notes.enumerated() {
            if existingNote.note == Int(note) && existingNote.startTime == startTime && existingNote.duration == duration {
                lastPanY = location.y
                lastPanx = location.x
                selectedNoteIndex = index
                setNeedsDisplay()
                
            }
        }
        
    case .changed:
        // Move the selected note rectangle
        let translation = gestureRecognizer.translation(in: self)
        
     
        
        // Check if a note with the same parameters already exists
        for (index, existingNote) in notes.enumerated() {
            if existingNote.note == Int(note) && existingNote.startTime == startTime && existingNote.duration == duration {
                
                let deltaY = lastPanY - translation.y
                let deltax = lastPanx - translation.x
                
                
                if abs(deltaY) > noteHeight  {
                   
                    let direction = deltaY > lastPanY ? 1 : -1
                    let newValue =  existingNote.note + direction
                    if newValue >= 0 && newValue <= numN - 1 {
                        notes[index].note = newValue
                        // do something with intValue
                        lastPanY = translation.y
                        
                     
                        
                    
                        selectedNoteIndex = index
                        setNeedsDisplay()
                       
                    }
                    
                }
                if abs(deltax) > timeSlotWidtho  {
                 
                 let directionl = deltax < lastPanx ? 1.0 : -1.0
                 let newValuel =  existingNote.startTime + directionl
                 if newValuel >= 0 && Int(newValuel) <= numT  {
                 notes[index].startTime = newValuel
                 // do something with intValue
                 lastPanx = translation.x
                 
             
                 selectedNoteIndex = index
                 setNeedsDisplay()
                 
                 }
                 }
                
                selectedNoteIndex = index
                setNeedsDisplay()
               
            }
        }
        
        
        
    case .ended:
        // the end
        setNeedsDisplay()
    default:
        print("wef")
        break
    }
}

Solution

  • I'm pretty sure this is what you're describing:

    enter image description here

    (At the end of the "video", I am demonstrating that starting a drag outside of the note does not affect the note.)

    If that is what you are after, just follow the instructions I already gave you in the comments. Make each note a view with a pan gesture recognizer. In that recognizer's target method's .changed state, look simply at the location of the first touch in your grid view and calculate the top left point of the grid rectangle that that touch is in. If the note's origin is not at that point, make it so.

    Here's my pan gesture recognizer target method. It assumes that, as I've just said, each note is a view with its own pan gesture recognizer. It also assumes we have a grid view called grid that knows how to report where the top left corners of its rectangles are, and that the note is a subview of the grid view:

    @objc func dragged(_ g: UIPanGestureRecognizer) {
        guard let note = g.view else { return }
        if g.state == .changed {
            let pt = g.location(ofTouch: 0, in: grid)
            let targetOrigin = grid.divisionOriginFor(point: pt)
            let currentOrigin = note.frame.origin
            if currentOrigin != targetOrigin {
                var newframe = note.frame
                newframe.origin = targetOrigin
                if grid.bounds.intersection(newframe) == newframe {
                    note.frame = newframe
                }
            }
        }
    }
    

    In that code, we assume that the grid view's divisionOriginFor(point:) is a method that looks at where the point is and decides what grid rectangle ("division") it is in and reports the top left corner of that rectangle. It might go like this (I assume that xct and yct are properties providing the number of lines/divisions for the grid):

    func divisionOriginFor(point pt: CGPoint) -> CGPoint {
        let qx = Int(pt.x) / Int(bounds.width / CGFloat(xct))
        let qy = Int(pt.y) / Int(bounds.height / CGFloat(yct))
        return CGPoint(
            x: bounds.width / CGFloat(xct) * CGFloat(qx),
            y: bounds.height / CGFloat(yct) * CGFloat(qy)
        )
    }