GWed
GWed

Reputation: 15653

Adding object to multiple views

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;
    }


}

Upvotes: 0

Views: 316

Answers (1)

Nick Lockwood
Nick Lockwood

Reputation: 40995

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).

Upvotes: 2

Related Questions