Undoing last drawn Polyline - ios

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.

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.

Related

Return the owner of a variable - Swift

So I have objects that in turn have sprites associated to them. a snippet of my Object class:
import SpriteKit
class Block {
var sprite : ColourSprite
}
So as you can see, it has a variable that is in fact a SKSprite (ColourSprite is my custom class that inherits from SKSpriteNode).
Now, at some points during the game, these sprites are deleted (i.e sprite.removeFromParent()), but the objects are obviously still somewhere.
I want to be able to send the objects to garbage collection once their sprites are gone. I am thinking that I can do something like sprite.getOwner() but I can't seem to find it. Is this possible?
The only other option I can think of is to manually check all objects and check each one's sprite but I feel that is long and wasteful.
You can check whether the Blocks are still in memory by using Xcode 8.3's new debug panel.
Just after you remove your sprites pause the program and go to that panel. See if there is any Block instances in the left panel. If there is, click on it to check what is retaining it.
If for example your GameScene is retaining the Block, you go to your GameScene and find the property. Then you can just set that to nil after you remove your sprite.

How does a (nonatomic, strong) property's lifespan work in iOS?

Say I have a property declared as : #property (nonatomic, strong) NSArray *menuArr; OR #property (strong) NSArray *menuArr; and set this property in viewDidLoad. How long will the device "remember" the information I have stored in the array?
The property is declared and set in a viewController that is embedded in a navigationViewController that is itself the first view controller in a TabBarViewController. In other words its the first view the user sees then they may navigate away from it and back.
Without getting into a debate over atomic vs nonatomic my question is this
Does a property (declared either way) lives on infinitely in the iOS environment or is its lifespan limited by factors such as time, memory usage elsewhere, turning off the device, etc
To avoid this being an "x y problem" here's why Im asking:
Im working on an app that includes a menu broken up into multiple categories with several items in each category .....as you might expect a menu to be. All of the menu items are stored on parse.com. At first I was doing a separate PFQuery on each page, one on the categories page to get the categories, when user selects a category a new page is pushed and a second PFQuery got all the items in the chosen category. This worked but the pages took quite a while to load, probably 10-15 seconds sometimes with no real indication that the app hadnt just frozen up.
To fix this I decided to run one PFQuery when the first view of the app is loaded in viewDidLoad getting all of the menu items and sorting the myself into nested arrays of categories containing items. I then store the menu array in a property on the viewController. Later, when I go to the menu I have the below in it's viewDidLoad:
//get e reference to the first view controller, the one that has the menu array
FirstViewController *myVC1ref = (FirstViewController *)[[[self.navigationController.tabBarController.viewControllers objectAtIndex:0] viewControllers] objectAtIndex:0];
//set thisviewController's `menuArr` property to point to the menuArr on the first viewController.
_menuArr=myVC1ref.menuArr;
My understanding is that this creates a pointer to the original array and does not actually create a second array (please correct me if Im wrong).
This method takes about 10-15 seconds to load and sort the array that one time but then navigation between pages is instant after that which is much better.
I plan to do queries in places to see if any menu items have been changed and if so re-download and sort the menu.
So far in my testing the app seems to remember the info in the array just fine throughout the day with normal unrelated phone usage but there has to be some limits to that right?
Your app's memory space will remain valid as long as your app is running. The system is not going to arbitrarily free memory out from under you. How could your app possibly function like that? The type and attributes of the property have absolutely no relevance at this level.
A low memory warning from the system is a request that you manually free up memory that you don't need. The app's memory will either be completely as you left it, or, after moving to the background and then being terminated, a blank slate. If you have data that needs to survive your program exiting, you need to make provisions to save it to disk and read it back.
iOS does have some tricks to preserve and restore your app's state, but that still only applies to a terminated application.

Is it possible to have a repeating object in Tiled/Sprite Kit?

I'm currently building a game using Xcode, sprite kit and now tiled. The game is simple, a person jumping from 1 block to another, but if they hit the side it will be game over. Now is their anyway through tiled to create a block for the person to jump on. And through out the game to keep repeating and coming on for the person to jump on, I would like the object to have different heights and widths? Please Help?!
What you can go for is creating a reuse mechanism that will reuse nodes instead of creating and destroying them. A mechanism well-known from UITableView or UICollectionView.
You would implement a method:
- (SKNode *)dequeueReusableNode;
that gets reused SKNode from some storage, say, NSMutableArray (grabs first object from array, then deletes it from array and returns it). If array is empty, method returns nil.
Then you check if you got a node from the dequeueReusableNode method, if it's nil, you create a new instance of a node. If it's not nil, you configure it with your data.
This goes for every visible node that should be reused.
Nodes that went completely off-screen are sent to reuseQueue – all their property values return to default state, and then node gets added to the end of the NSMutableArray that is the reuseQueue.

How to delete the last annotation on a mapView

I have added a button on my .xib file with which I want to delete the last annotation that has been added.
So on the Touch-Down action I have implemented this:
-(IBAction)DeleteAnnotation:(id)sender {
[mapview removeAnnotation:[mapview.annotations lastObject]];
}
and I have even tried it this way:
-(IBAction)DeleteAnnotation:(id)sender {
[self.mapview removeAnnotation:self.mapview.annotations.lastObject]];
}
where mapview is my MKMapView Outlet.
The problem I encounter with both ways is that I have to press this specific button quite a few times before an annotation is removed.
Furthermore, the annotations remove themselves in a quite random way.
Is there something I am doing wrong or is it a software and simulator matter?
The annotations property of MKMapView is not guaranteed to return the annotations in the same order that you added them.
Making the assumption that the annotations array property will return the annotations in the same order that you added them is most likely the reason for the "strange" behavior you see. Please see these related answers for some more details:
MKMapView annotations changing/losing order?
How to reorder MKMapView annotations array
To get the behavior you want (which I assume is simply "remove the last annotation that was added explicitly by my code"), here are three possible approaches (there may be others):
Simplest approach is to keep a reference in a strong property to the last annotation you add (update the reference when you call addAnnotation). When you want to remove the "last annotation added", pass that saved reference to removeAnnotation. For example:
//in the interface...
#property (nonatomic, strong) id<MKAnnotation> lastAnnotationAdded;
//in the implementation...
//when you add an annotation:
[mapview addAnnotation:someAnnotation];
self.lastAnnotationAdded = someAnnotation; //save the reference
//when you want to remove the "last annotation added":
if (self.lastAnnotationAdded != nil)
{
[mapview removeAnnotation:self.lastAnnotationAdded];
self.lastAnnotationAdded = nil;
}
Another option is to loop through the map view's annotations array and search for the "last" annotation (or whatever attribute you're interested in). Once you have a reference to the "last" one (which may not necessarily be the last object in the array), you can call removeAnnotation on it. This approach assumes you have some property in the annotation objects themselves that let you identify an annotation as the "last" one. This may not always be possible.
Another option is to keep your own array of annotations and add annotation objects to this array whenever you call addAnnotation. This is similar to keeping the single reference to just the "last annotation added" except you keep track of the entire list in an order you can rely on. To remove the "last" one, you would get lastObject from your array instead of the map view's (assuming you keep the array in that order). You have to make sure to keep your array in sync as you add/remove annotations from the map.

How to record all of the user's touches in iPhone app

note: This is an expansion (and clarification) of a question I asked yesterday.
I am conducting a research project where I want to record all of the user's touches in the iPhone app. After the experiment, I will be able to download the data and process it in either Excel or (more likely) Matlab and determine how many times they clicked on certain buttons, when they clicked certain buttons, etc. To do this, I would need to know:
a) When they touched
b) Where they touched
c) Which view they touched
The first two are easy, but the third I am having trouble with. I know I can do this to get the reference to the UIView that was touched:
CGPoint locationPoint = [[touches anyObject] locationInView:self];
UIView* viewYouWishToObtain = [self hitTest:locationPoint withEvent:event];
However, that will just give me a pointer to the view, not the name of the view that was touched. I could assign each view a tag, but then every time I create a new view I would need to remember to tag it (or, alternatively, log the address of each view when initialized and log it when the view is touched). Subclassing UIView and adding an automatic tag isn't really an option since I'm creating other UIButtons and UISliders and would need to subclass those also, which doesn't seem like a very good solution.
Does anyone know of a clean, easy way to do this?
For "Which view they touched", what information do you need?
Perhaps you could use a category to add a method to UIView. This method would generate a string containing information about the view. Such as:
its type e.g. UIButton etc.
its size and position
the title of the view, if it has one (e.g. the button title)
the parent view type and title
other stuff e.g. is the view enabled, what state it is in. anything you like.
For example: "Type:UIButton Title:"Back" Rect:{3,5,40,25}" or some such string.
This is very clean and gives you quite a lot of information to be going with.
You could add a category to UIView which would then be inherited by all UIView descended objects, although I'm not sure its any more efficient than tagging. Since a category can override methods then you could override init methods for automatic tagging I suppose.
http://macdevelopertips.com/objective-c/objective-c-categories.html
I'm not sure what you mean by the "name" of the view. If you mean the view name in Interface Builder, I don't believe it includes that in the instantiated objects. You could use the Tag attribute which is included, but that's just a number and not a name.

Resources