Search code examples
iosobjective-cmkmapviewmkoverlaymkpolyline

Undoing last drawn Polyline


I have been working on a polyline overlay and have been helped very well when I was stuck, I am hoping for some more assistance again.

In my project I am drawing polylines on a map from touches began to touches moved. the coordinates of where my finger is dragged is added to my array and displayed.

When I want to clear my lines i simply empty the array, this is fine

However, If i want to remove/undo the last line drawn. I was under the impression that I could just minus the last value from my array, but while thinking more deeply I realized that I may have a problem. Each element in the array is a coordinate where my finger last touched. I havent tried this yet, but I am imagining if I only minus the last value of the array, I am going to spend a long time "erasing" a line which I drew. I was looking for a method, by which when i press "Undo" it erases the entire line i drew. Even if that line extends from North America to England.


Solution

  • The OP wishes to be able to undo the last line that was drawn. The problem he imagines is that removing a single element from the array would visually only remove a part of the line drawn by the user. What he wishes to do is to remove all the elements within the array that make up the line.

    I haven't done a lot on drawing but my solution to your problem should work.

    Do the following, create an enumerated data type that would create three possible states for each coordinate drawn to the screen/added to the array.

    This data type would look something like this:

    typedef NS_ENUM(NSInteger, PKLinePointState) {
        PKLinePointStart,
        PKLinePointMiddle,
        PKLinePointEnd
    };
    

    The idea here is that you will use this data type to determine which elements in your array are the starting points and which elements in your array are the ending points. This way you can make determine the lines in your array, and so therefor be able to determine how many elements to delete until you hit a coordinate that has status of PKLinePointStart.

    You don't want to store just coordinates in your array, instead you want to store a data model which will hold more information so that you can do the sexy things you wish to accomplish.

    The first data model will be used to store the start and ending coordinates and elements within the array. This will make it easier work with your coordinates. This is what it would look like:

    LinePointModel.h

    typedef NS_ENUM(NSInteger, PKLinePointState) {
            PKLinePointStart,
            PKLinePointMiddle,
            PKLinePointEnd
        };
    
    @interface LinePointModel : NSObject
    
    @property (assign) CGPoint linePointCoordinate; 
    @property (assign) PKLinePointState linePointState; 
    @end
    
    //you may want to create a custom initialiser so that the coordinate and state are initialised straight away.
    //It would look something like this -(instanceType)initWithLinePointCoordinate:(CGPoint)coordinate andLinePointState:(PKLinePointState)linePointstate;
    

    LinePointModel.m

    //I'll let you complete the initialiser method in the implementation file.
    

    Now that you've created your data model that will store your coordinates properly in the coordinates array, here's how I would add them to the array.

    The logic Phase 1:

    When the user taps on a screen - the touchesBegan method gets called, you would want to create an instance of the LinePointModel and have its coordinate value set to the first coordinate the user taps on the screen and would set the state to PKLinePointStart which would mark the beginning of the line. Add this element to the array.

    Then, when the user drags across the screen, you will receive multiple coordinates from the touchesMoved. Here you will create an instance of the same data model again setting the coordinate but this time the state will be stored as PKLinePointMiddle, we dont really care about these points, since we wont be checking against this state, but its still good to give it a value so that we know that any elements within the array that have this state only servers to form a point, points which eventually make a whole line. Here these instances will keep getting added to the array as many times as it needs until the user finally stops dragging - drawing the current line - on the screen.

    Soon as the the touchesEnded method gets called - signifying the end of the line - the user lifting up his finger, this is where you create your last instance of the the data model, set the coordinate accordingly, and finally the state would be the PKLinePointEnd state. Again, you would add this instance of within the array too.

    Now you have a meaningful custom array that keeps track of all the coordinates but lets you know which element has the starting state, midde and ending state.

    Every starting state is coupled with the ending state with as many middle elements needed to make up the line.

    If you want to stop here, you simply run the for loop, and determine the latest start and end elements added within your array. :)

    The logic Phase 2 - Improving the algorithm:

    Now you dont wan't to be looping through thousands of elements in an array just to determine how far in your array you have to go back to just to delete a line, that would be inefficient. This is where your second data model will be useful. This second data model will keep track of your lines and store which elements in your array make up its line by storing the start and end index or even better, store the array of the coordinates which is what we will do.

    LineArrayModel.h

    @interface LineArrayModel : NSObject
    
    @property (nonatomic, retain) NSMutableArray *arrayOfLinePoints;  //What matters.
    //@property (assign) int lineNumber; 
    //@property (assign) CGFloat lineThickness;
    //@property (strong, nonatomic) UIColor *lineColor;
    @end
    

    Using this data model, you would then create a temporary array whenever a user draws a line on the screen, and as soon as they are done drawing a line, you would grab the array and store it in an instance of LineArrayModel; and it's this instance that you would use to store in a new array called, myLinesArray. This is the array that you would then use to draw your lines on your screen.

    Then to simply undo a line you just remove the last instance of LineArrayModel in the array that stores all your lines, and then simply redraw your lines if necessary.

    Let me know how you get on.