Remove subviews from an UIView perfomantly? - ios

Hi i'm writing a iOS tiled Map component in Monotouch that requires me to remove and add a large number of (markers)UIViews from a parent UiView as part of the Main thread.
I am specifically implementing clustering for the pins. As they become to close together they cluster into nodes displaying the number of pins the node represents. Depending on the zoom level i need to add and remove pins as they move in and out of nodes
Adding the views is fine as this can be done in bulk with
_parentView.AddSubviews (viewsToAdd.ToArray());
plus it can be chunked.
but the remove is killing my performance as each individual view has to be removed with a
view.RemoveFromSuperview ();
view.Dispose ();
Is there any way to speed up the operation?
I looked into putting the views into an NsArray and using performSelector remove from superview but i couldn't find the correct syntax for that in mono touch
thanks Luke

I think you should consider recycling your views instead of disposing them and creating new ones. This is something Apple's MKMapView provides, but you don't mention if you are using it or not. It works by adding a number of MKPlacemark objects, that are represented by MKAnnotationView recyclable views as you scroll around the map.
There is a reasonable example in Xamarin's Field Service App of using MKMapView's DequeueReusableAnnotation method. Scroll down to the MapViewDelegate nested class.
If you are using another map library, like Google Maps, you should consider keeping a Queue<T> of your views in order to recycle them. That way you can merely call AddSubview and RemoveFromSuperview and not create new objects over and over.

Related

how to get visible overlays in MapKit? (i.e. MKOverlay/MKOverlayRender from a Mapkit Ivew)

How does one get a list (array) of currently visible overlay from a MapkitView?
Background - for example I want to show direction arrows to the center of certain Overlay types on my mapkitview, however how do I get the visible ones? There seems to be no way to do this I can see? So do I need to got through all overlays (actually ~8000) and do my own check to see what would be showing on the screen? Seems a waste if MapKit would have already effectively done this as part of deciding what overlays need to be displayed at a given time.
I've been tinkering with some similar problems and the most efficient way I could figure out was to add the overlays as annotations as well, since MKOverlay implements MKAnnotation. Then you would be able to use annotationsInMapRect for the currently displayed mapRect. However this would also return any regular MKAnnotations, and only uses the calculated middle of the overlay. The only way (so far as I figured) to only get the overlays would be to iterate over each overlay and use:
-(BOOL)intersectsMapRect:(MKMapRect)mapRect;
on the currently visible mapRect.
If you found another way I'd be happy to hear!

What advantage does CALayer confer w.r.t. this example (rather than just using UIView)? [duplicate]

Both have most of the same attributes, both support different kind of animations, both represent different data.
What are the differences between a UIView and a CALayer?
On iOS, every UIView is backed by a Core Animation CALayer, so you are dealing with CALayers when using a UIView, even though you may not realize it. Unlike NSViews on the Mac, which evolved before Core Animation existed, UIViews are intended to be lightweight wrappers around these CALayers.
As I describe in the similar question "When to use CALayer on the Mac/iPhone?", working directly with CALayers doesn't give you significant performance advantages over UIViews. One of the reasons you might want to build a user interface element with CALayers instead of UIViews is that it can be very easily ported to the Mac. UIViews are very different from NSViews, but CALayers are almost identical on the two platforms. This is why the Core Plot framework lays out its graphs using CALayers instead of other UI elements.
One thing UIViews provide over CALayers is built-in support for user interaction. They handle hit-testing on touches and other related actions that you would need to build yourself if managing a hierarchy of CALayers. It's not that hard to implement this yourself, but it is extra code you'd need to write when building a CALayer-only interface.
You will often need to access the underlying layers for a UIView when performing more complex animations than the base UIView class allows. UIView's animation capabilities have grown as the iOS SDK has matured, but there are still a few things that are best done by interacting with the underlying CALayer.
From the Ray Wenderlich blog (Tutorial)
CALayers are simply classes representing a rectangle on the screen
with visual content. “But wait a darn minute,” you may say, “that’s
what UIViews are for!” That’s true, but there’s a trick to that:
every UIView contains a root layer that it draws to!
Simply speaking,UIView inherit from UIResponder, handle events from users, contains CALayer, which inherit from NSObject, mainly focus on rendering, animation etc.
UIView is a container for CALayers. Using UIKit.
CALayer where we draw the contents. Using CoreGraphics
If you work with custom control like features it would be great to go ahead with single view containing more layers for accurate native rendering. Since CALayers are weightless than UIView.
To create common skeleton for Mac and iOS, follow the design for your app using CALayers. Since it is available in both platform.
UIView having feature like touch events achieved using delegates -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event, tochesStart like events and other UIKit features.
To work with CALayers use Core Graphics knowledge.For any simple view rendering UIView is enough.
UIView: Views have more complex hierarchy layouts. They can receive user interactions like taps, pinches, cliks and more. Working with UIViews happens on the main thread, it means it is using CPU power.
CALayer: Layers on other hand have simpler hierarchy. That means they are faster to resolve and quicker to draw on the screen. There is no responder chain overhead unlike with views. Layers are drawn directly on the GPU. It happens on a separate thread without burdening the CPU.
For more details: https://medium.com/#fassko/uiview-vs-calayer-b55d932ff1f5
The big difference is UIView is designed for CocoaTouch on mobile device. It adds some event handler which CALayer did not provide.

MapKit overlays to add floor plans to map

I am trying to add an overlay to iOS MapKit in order to show floor plans. However, I am unsure about the best way of doing this. If I add the floor plans by using an MKOverlay, will the floor plans be loaded lazily or do I need to find out which part of the map is being displayed and update overlays thereafter? I also looked at using MKTileOverlay, as it is using lazy loading, but I have the impression that it should be used for completely covering of the map and not only to add to the existing one. Is this correct?
Yes, you are right MKTileOverlay can cover whole map with tiles and Yes it is using lazy loading.
Use MKOverlay if you are not willing to replace native look and fill of map. You can also achieve lazy loading for MKOverlay too.
Note: MKTileOverlay will not remove your already existing MKAnnotations and MKOverlay.
MKOverlay is a protocol, not a class. You want MKTileOverlay as of iOS 7. Things are lazy loaded according to what portion of the map is currently shown.
You might find this useful as a reference on how the tiles are numbered and organized:
https://www.mapbox.com/foundations/how-web-maps-work/
As for covering Apple's map, this will happen by default, but you can also adjust the MKOverlayLevel used to place it between Apple's base map and labels layer, or you can use canReplaceMapContent = YES if you want to disable Apple's maps entirely.

iOS: Collision Objects & PanGestureRecognizer

I have implemented the PanGetsureRecognizer for dragging my buttons, images and other components on my iPhone screen, but now I have a new conflict concerning the Objects Collision.
I have implemented my need in that way:
if(CGRectIntersectsRect(menu1Drag.frame, menu2Drag.frame)){
// blablabla
}
Unfortunately, it's not working. I am wondering if there are specific statements for collision objects with PanGesture. I have took a look on many sites, but they are using the same statements like above.
Any ideas?
Check whether menu1Drag and menu2Drag is in the same level of view hierachy.
Try out log the detail of their frame before if(CGRectIntersectsRect(menu1Drag.frame, menu2Drag.frame)).

Adding annotations to a map are too slow - iOS

I've got a performance problem regarding adding annotations to maps in iOS (using the standard mapkit methods - hence using Apple maps). I'm trying to add a large number of annotations (10000+) in bulk, using the (void)addAnnotations:(NSArray *)annotations method. The annotation objects are nothing special - a very basic class that implements MKAnnotation with only the constructor defined as follows:
- (id)initWithTitle:(NSString *)ttl andCoordinate:(CLLocationCoordinate2D)c2d {
if(self = [super init]) {
title = ttl;
coordinate = c2d;
}
return self;
}
In order to test, I've created a basic app that simply adds the annotations to a basic map view on load, and it takes around 6-7 seconds on an iPad 2. Very acceptable performance. However, things get complicated when I embed the same basic map view in my own application. It's the exact same logic, but when I do it in my app, it takes around 50 seconds to load all the annotations.
My app has a bunch of views within each other. To pinpoint the problem, I've started embedding the map view starting from the lowest level, until I reach the top level. I've done profiling in each step to see if it helps the performance, but I don't see any results - it's still around 50 seconds. Right now, my app basically just loads and adds the annotations to the top and only level map view, ideally the same as the standalone app, but for whatever reason, I still see the annotation rendering taking around 50 seconds. The only reasonable explanation I have is in regards to some of the app settings I have that somehow interferes with the map performance. I've done a bunch of reading to come up with a solution to no avail.
Anyone have any ideas about what might be the problem here? Thanks!
There is no actual "problem". I mean, you have more than 10000 allocations, draws and renderings that are actually having impact on the performance of the application.
My suggestion is to intercept the user navigation on the map. Use something like [mapView visibleMapRect] to get the visible portion of the map on the screen. Get the coordinates of the rect (maybe a little bit bigger so that cannot be noticed by the user when moving the map) and load only the annotations within that rect.
As the user moves, you only have to load the annotations that are within the new rect MINUS the annotations that were already loaded in the previous rect. I guess you can also remove the annotation when they go out of the screen.
This resembles a little bit what Apple suggests to do when dealing with scrolling views. I remember the application in which the user would have to load the content of the previous and next section of the scrollview (and to release the content of the previous-1 view) so to have a slow memory consumption and still maintaining a good user experience.
Try to think about what the user is looking at, try to show only the annotations that matter that portion (or region as they are called in the MKMapView jargon) and I think your app responsiveness will just improve right away.

Resources