Sunday, September 18, 2011

UIViewAutoresizing Has Limits

If you're going to try to resize stuff seriously -- all the way down, then all the way up -- UIViewAutoresizing sucks. Why? All the information it uses comes from the current state. Over resizes to smaller sizes it will lose precision. This means that a "flexible sides" subview that was 180 pixels from the left side could end up at 18 pixels from the left side. So you'll need to store your subviews' sizes. You get something cheesy like this:

#pragma mark All the storing position stuff since UIViewAutoSize sucks
-(NSMutableDictionary *)positions {
    if (!positions)
        self.positions = [NSMutableDictionary dictionary];
    return positions;
}

- (void) repositionElements {
    for (NSValue *key in [self.positions allKeys]) {
        [self reposition:[key nonretainedObjectValue]];
    }

}

- (void) storePositionsForElements {
    for (UIView *view in self.subviews) {
        if (CGRectIsEmpty(view.frame))
            return;
        [self storeFirstPosition:view];
    }
}


-(void)storeFirstPosition:(UIView *)element {
    NSValue *key = [NSValue valueWithNonretainedObject:element];
    [self.positions setObject:[NSValue valueWithCGRect:element.frame] forKey:key];
}
     
 - (void)reposition:(UIView *)element {
     NSValue *key = [NSValue valueWithNonretainedObject:element];

     CGRect firstFrame = ((NSValue*)[self.positions objectForKey:key]).CGRectValue;
     float newX = firstFrame.origin.x / firstBounds.size.width * self.bounds.size.width;
     float newY = firstFrame.origin.y / firstBounds.size.height * self.bounds.size.height;
     float newWidth = firstFrame.size.width / firstBounds.size.width * self.bounds.size.width;
     float newHeight = firstFrame.size.height / firstBounds.size.height * self.bounds.size.height;
     element.frame = CGRectMake(newX, newY, newWidth, newHeight);   
 }

This would allow you to store everything in an NSMutableArray called positions (make sure it's not nil, for God's sake). You would call storePositionsForElements in your initWithCoder (so it stores the NIB's positions), and then call repositionElements from your setFrame override.

If categories let you store stuff, I would monkey patch UIView and get a much better solution. 

No comments: