dynamic MKMap points loading - ios

Im trying to find the best way to work with MKMaps with my needs.
My code is very long so ill just explain.
What do i need to do? (*)
understand what is the location that the user is interested in.
get points from the internet for this location.
load them into the map.
update the map (cluster annotations) with animation.
How do I do it now?
in regionDidChangeAnimated I call GetPoints.
GetPoints starts a NSURLConnection in order to get the points in the region (using region.span.latitudeDelta and region.span.longitudeDelta).
in connectionDidFinishLoading I'm calling AddPoints.
AddPoints is checking for every point if its already on the map, if not its added (not directly to the MapView but to a "hidden" AllPointsMap).
after adding all points into the AllPointsMap Im calling updateVisibleAnnotations.
updateVisibleAnnotations is updating the MapView (by separating the visibleMapRect to gridMapRects and shows only one MKAnnotation for each one) and takes care to animate the annotations in and out the way they should.
What is the problem?
it works! but its not so smooth, updateVisibleAnnotations takes a lot of time and the map is not responding in that time...
So I'm calling updateVisibleAnnotations in background, like that:
[self performSelectorInBackground:#selector(updateVisibleAnnotations) withObject:nil];
Its awesome, really smooth mapView animations, not disturbing user interface.
BUT from time to time i get "Collection <__NSArrayM: 0x76c11b0> was mutated while being enumerated".
I can't tell exactly what causing it. But my guess is that main thread is trying to add or remove annotations while background threads are using them.
I have tried using #synchronized in updateVisibleAnnotations so main thread is not interrupting background thread, but it remained the same.
I have tried using a self.waitinigForUpdate BOOL #property to indicate that updateVisibleAnnotations is running in background, still get the same error…
Makes me wonder: Maybe this is not the best way doing what I need to do (*), is it??

Related

Waiting with method execution until a condition is met in Swift

I have a MKMapView with some annotations added by code representing users sharing their location. I get updates about location changes via websockets and update the corresponding MKAnnotation.coordinate that represent the annotations in MKMapView.
This mostly works fine, I see the users moving on the map. The problem is that when a MKAnnotation.cooridnate is updated while the user pans or zooms the map then MKMapView crashes saying that a hash table was update while being evaluated. It is basically this problem: MKMapView crashing if zooming while adding annotations
Update: Crashes only occur when clustering is enabled, no need to even pan or zoom, just waiting a bit is usually enough.
The recommended solution is not doing anything (like updating MKAnnotation.cooridnate in my case) with MKMapView between regionWillChangeAnimated and regionDidChangeAnimated and I am tryintg to implement this.
The problem is that my data is being received via websockets so I cannot just "stop" listening for the new data between those 2 delegate methods. Theoretically I can but that would mean losing some data which is not a solution.
My idea was that I need some kind of queue that I will use to add my updates to MKAnnotation.cooridnate and will get paused in regionWillChangeAnimated and resumed in regionDidChangeAnimated.
I tried using DispatchQueue in the way described but it locks after a while. I am not sure if it is because of the high amount of operations being added to it or the calls to suspend and resume. If it does not lock it just crashes the app after a while.
My question is what mechanism will work here?
As stated in the question title, I basically need a way to make some methods wait for a condition, say toggling a flag between regionWillChangeAnimated and regionDidChangeAnimated that is reliable and can take the load.
If you want to not perform tasks while a certain condition is met, use a DispatchQueue and just suspend or resume it. And if this is for tasks that should be performed on the main queue, make the main queue the target for this queue that will be suspended and resumed:
let annotationChangeQueue = DispatchQueue(label: "...", target: .main)
But this begs the question: Under what condition should one should suspend and resume this queue? It would be reasonable to assume (as that other question you reference does) that one could suspend in regionWillChangeAnimated and resume in regionDidChangeAnimated. Alas, this appears to be insufficient. You’ll find that even if you make sure to not do any annotation changes until after regionDidChangeAnimated, it can still crash. And MKMapView doesn’t appear to expose any methods/properties that we can observe to avoid this.
I don’t think there is a proper solution to this problem given that Apple isn’t exposing any property that we could possibly detect when it’s safe to mutate our annotations.
FWIW, I am only able to manifest this issue if I turn on clustering. Also, while the focus of this question was on panning/zooming, I found I was able to reproduce the crash without any panning/zooming. Just was just updating annotations (that participate in clustering) and not touching my device at all, and it eventually crashed with this error. So while it’s easier to manifest with panning/zooming, I think the problem is more fundamental, namely how the map view handles updates to annotations (presumably, only those that may be clustered).

CLLocationManager with MKMapView avoiding multiple instances

I have a question regarding how to properly set up my app to deal with multiple views needing location information. I have a two views. One a normal UIViewController that has a table view, the other, a UIViewController that houses an MKMapView. Now, both of these views need location information. As it stands, I have a singleton that implements a CLLocationManager to get all the user location information for the app. However, I was thinking about some other possibilities because sending a NSNotification every time the location is updated to change the center of my map seems like not the preferred approach as opposed to using the tracking methods in MKMapView. So, I am thinking about a couple options.
When the MKMapView appears, pause the singleton CLLocationManager and then, when it disappears start it up again. This way, only one location manager is updating at a time and I could still use the user tracking on the MKMapView. However, there will still be two objects that collect location data which seems like bad coding.
Use only the singleton CLLocationManager and update the center of the map based on that by posting notifications in the app.
As it stands, I am leaning toward option 1. However, I am very interested in what the preferred approach to a MKMapView and another view that needs location information in regards to how to update location throughout the app and was hoping someone could fill me in on what the proper approach would be. Thanks

How to Load MKMapView faster?

I am building an app that contains a MKMapView. The first time user goes over this view, it takes a while for it to load, and things such as animating pin drops also don't work well as a result of it. It takes a bit for the location to stabilize if i choose another location from the simulator options.
Is there any way to load MKMapView very fast? May be run in background or what?I am confused? What do you guys do to load Map Faster?
put a splash screen on while it loads, and catch the event of the loading finishing is the delegate method mapViewDidFinishLoadingMap:
don't forget to set your ViewController (or other implementing object) as the delegate of the MKMapView object.

Is there any scenario that can cause ViewDidLoad to be called before didBecomeActive?

I know it's sounds silly but just to clear a point.
Is there any chance that view did load will be called before didBecomeActive ?
Is it totally impossible ?
EDIT
We have a crash that happens when user is coming back to the app from the background and we start to use openGL. The crash error points that we try to use openGL in the background.
It is important to say that our app lives in the background as a VOIP app.
We try to figure out if there is a chance that somehow we are triggering something in the background thats causes the app restart openGl in the background.
In the stack we see:
[VideoCallViewController viewDidLoad] (VideoCallViewController.m:283)
And few lines after that:
[GPUImageContext createContext]
And finally:
gpus_ReturnNotPermittedKillClient + 10
We are trying to figure out if there is a way that [VideoCallViewController viewDidLoad] was called in the background or that we must assume that we are in the foreground, and somehow moving to the background right after the viewDidLoad ?
Second option
The second option is that we are indeed moving to the background right after the viewDidLoad. The point here is that we are listening to AppWillResignActive and we pause the GPUIMage. So we can not understand why do we get the crash ?
Thanks
Thanks
When / Where do you instantiate the various GPUImage objects that you are using? Is it within viewDidLoad or possibly within init:?
It's just pure rampant speculation here since you didn't really post any code...
but if you are disposing of objects when the app heads to the background that are not then re-created when it comes back to the foreground (perhaps because the viewController was retained by a parent, and therefore init: was not called again but viewDidLoad was...) Then you may be trying to send OpenGL messages to objects that don't actually exist anymore.
On the (probably unlikely) chance that my speculation is right, you could easily fix it with the common "getter" pattern of:
- (GPUImageObjectOfInterest*)instanceOfObject {
if (!_classVariableOfThisType) {
_classVariableOfThisType = [[GPUImageObjectOfInterest alloc] init];
// custom configuration, etc...
}
return _classVariableOfThisType;
}
and then use [self instanceOfObject]; wherever you used to use _classVariableOfThisType
It's a low overhead, but reasonably foolproof way of making sure a key object exists under a wide range of app interruption / background & foreground & low memory conditions.
Don't be shy to post too much code though, we can read through an entire class if needed. Some of us like reading code! (and it will really help the quality of response you get...)

Trouble toggling the userInteractionEnabled property in iOS

I am developing a tic tac toe game for iOS and I am using a combination of UIButtons and UIImageViews to allow for user interaction and to display the moves made. My problem is that the buttons continue to accept user input before the cpu makes it's move, which breaks my game logic. I have made several attempts to toggle the userInteractionEnabled property, but I have only been able to turn it off. The engine that gets everything started in the game is my buttonPressed method. I also toggle the userInteractionEnabled property within this method and therein lies my problem: How do I re-enable the property after disabling user interaction? Is there a method that is called in between events that I can overwrite?
I have searched the web and I have searched through the developer documentation provided by Apple and I found information on the touchesBegan and touchesEnded methods. However, from what I understand, those methods need to be explicitly called which brings me back to my original problem of not being able to call those functions without the user's interaction.
If anyone can help, I would greatly appreciate it! I have been racking my brain over this for the past couple of weeks and I am just not seeing a solution.
I'd think that for a game like tic-tac-toe, calculating the countermove should be so fast that it can be done immediately in response to the first button press. If you're doing something complicated to calculate the next move, like kicking off a thread, you might want to reconsider that.
Let's say, though, that your game is something like chess or go, where coming up with a countermove might take a bit longer. Your view controller should have a method to make a move for the current player, let's call it -makeMove:. Your -buttonPressed action should call that method to make a move for the user. In response, -makeMove: should update the state of the game, switch the current player to the next player. If the new current player is the computer, it should then disable the controls and start the process of calculating the next move. Let's imagine that's done by invoking some NSOperation, so that coming up with the next move is an asynchronous task. Once the operation has come up with a move, it should again invoke -makeMove: (by calling -performSelectorOnMainThread:), which will again update the game state and the current player. This time, though, it should see that the new current player is not the computer, and so it should re-enable the controls.

Resources