0

Im have a subclass of UIView, called PinView, which contains an image. Basically PinView gets added multiple times to my app, and then I perform a transform on PinView objects. The issue is that when I add a lot of PinViews, my app gets sluggish because I am transforming each PinView.

Ideally, I want to have one 'static' PinView that gets added to a UIView multiple times but i only have to transform it once. Currently this doesn't seem to work. Every time I add the static PinView to my UIView, it will only ever appear in the UIView once (due to it only being able to have one superview I'm guessing).

Im struggle to find out the best way to go about solving this problem - how do I use a single pointer to a PinView, add it multiple times to a UIView and be able to perform a transform on the PinView which gets passed on to PinViews displayed in my UIView? (by the way, the transform is always the same for all the PinViews).

Im assuming this will be the best way to get a performance improvement, if this is not the case please let me know.

UPDATE:

- (void)layoutSubviews { CGAffineTransform transform = CGAffineTransformMakeScale(1.0/self.zoomValue, 1.0/self.zoomValue); NSMutableArray *mut = nil; PinView *pinView = nil; CallOutView *callOut = nil; //get all dictionary entries for(NSString *identifier in self.annotationsDict.allKeys){ //get the array from dictionary mut = [(NSArray *)([self.annotationsDict objectForKey:identifier]) mutableCopy]; //change layout if not nil if([[mut objectAtIndex:PIN] isKindOfClass:[PinView class]]){ pinView = ((PinView *)[mut objectAtIndex:PIN]); pinView.transform = transform; [mut replaceObjectAtIndex:PIN withObject:pinView]; } if([[mut objectAtIndex:CALL_OUT] isKindOfClass:[CallOutView class]]){ callOut = ((CallOutView *)[mut objectAtIndex:CALL_OUT]); callOut.transform = transform; [mut replaceObjectAtIndex:CALL_OUT withObject:callOut]; if(pinView !=nil)callOut.center = CGPointMake(pinView.center.x, pinView.center.y - pinView.frame.size.height); } [self updateAnnotationsKey:identifier forObject:[NSArray arrayWithArray:mut]]; mut = nil; pinView = nil; callOut = nil; } } 

UPDATE:

Removed the above and now just have:

- (void)layoutSubviews { CGAffineTransform transform = CGAffineTransformMakeScale(1.0/self.zoomValue, 1.0/self.zoomValue); for(UIView *view in self.subviews){ view.transform = transform; } } 

1 Answer 1

2

This can't be done I'm afraid. Each UIView instance can only be added to the screen once.

If all your views have similar transforms, you might have more luck using something like CAReplicatorLayer, which is a system for automatically creating duplicates of CALayers with different transforms.

That will only works if your views are all arranged in a grid or circle or something though. If they are just dotted randomly, it won't help.

If you are trying to draw more than 100 views, you're probably just bumping up against the fundamental performance ceiling of Core Animation on iOS.

The next approach to try would be to use OpenGL to draw your pins, perhaps using a library like Sparrow or Cocos2D to simplify drawing multiple transformed images with OpenGL (I'd recommend Sparrow as it integrates better with other UIKit controls - Cocos is more appropriate for games).

UPDATE:

This code is unnecessary:

 mut = [(NSArray *)([self.annotationsDict objectForKey:identifier]) mutableCopy]; if([[mut objectAtIndex:PIN] isKindOfClass:[PinView class]]){ pinView = ((PinView *)[mut objectAtIndex:PIN]); pinView.transform = transform; [mut replaceObjectAtIndex:PIN withObject:pinView]; } 

The code below is sufficient, because setting the transform doesn't modify the pointer to the object, so it will update the object in the array even if the array isn't mutable, and array objects are declared as 'id' so they don't need to be cast if you assign them to a variable of a known type.

 mut = [self.annotationsDict objectForKey:identifier]; if([[mut objectAtIndex:PIN] isKindOfClass:[PinView class]]){ pinView = [mut objectAtIndex:PIN]; pinView.transform = transform; } 

I would also think you can remove the isKindOfClass: check if you only ever use those array indices for those object types. It may seem like a good precaution, but it carries a performance penalty if you're doing it over and over in a loop.

But for 10 views, I just wouldn't expect this to be that slow at all. Have you tried it without moving the callout centres. Does that perform better? If so, can you limit that to just the callouts that are currently visible, or move them using CGAffineTransformTranslate instead of setting the centre (which may be a bit quicker).

Sign up to request clarification or add additional context in comments.

10 Comments

I don't think subclassing and static variables work way you think they do. If you have identical transforms for multiple views then they'll all be on the same place on screen (even if you could add the same view multiple times, which you can't). If they are in different places they need to be different objects.
Another option is to draw the images yourself inside drawRect: or similar.
Applying the transform is unlikely to be the bottleneck. It's actually rendering the transformed view that takes time. I may be wrong, if your transform is very complex, try generating your transform just once and setting that as a static CSTransform3D or CGAffineTransform variable, but a I say, I doubt that's where it's slow. You could always add some code examples to your question to get more specific help.
@Jesse: Core Graphics is much slower than Core Animation, so if core animation is too slow, I doubt drawRect will be faster (Core Graphics doesn't use hardware acceleration for transforms or drawing, Core animation does although it's still a lot slower than pure OpenGL).
Thanks for the update. The container of the pins is in a scrollView. When i zoom in with the scrollView I need the pins to stay in the same place in their container view, but they need scaling smaller, so they appear to be the same size on the screen when zooming in - if that makes sense.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.