Summon
Summon

Reputation: 968

Programmatically adapt uiviews to iPad / iPhone screens

I make an app that has many views that subclass from UIView. The size and the orientation of these views is random and the state of a screen of the app can be saved. When the user saves a screen on the same device that he opens it, then the screen state is OK. Everything is positioned correctly. But, if the user saves the screen state on an iPhone and opens it from an iPad the views are not positioned correctly. Actually the views appear shorter or longer, the center seems to be saved correctly, but the rotation of the views and their size (bounds property) are not working OK.

These are the two methods that save and restore the state of the view

- (void)encodeWithCoder:(NSCoder *)aCoder {
    // Save the screen size of the device that the view was saved on
    [aCoder encodeCGSize:self.gameView.bounds.size forKey:@"saveDeviceGameViewSize"];

    // ****************
    // ALL properties are saved in normalized coords
    // ****************

    // Save the center of the view
    CGPoint normCenter = CGPointMake(self.center.x / self.gameView.bounds.size.width, self.center.y / self.gameView.bounds.size.height);
    [aCoder encodeCGPoint:normCenter forKey:@"center"];

     // I rely on view bounds NOT frame
    CGRect normBounds = CGRectMake(0, 0, self.bounds.size.width / self.gameView.bounds.size.width, self.bounds.size.height / self.gameView.bounds.size.height);
    [aCoder encodeCGRect:normBounds forKey:@"bounds"];

     // Here I save the transformation of the view, it has ONLY rotation info, not translation or scalings
    [aCoder encodeCGAffineTransform:self.transform forKey:@"transform"];
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {

        // Restore the screen size of the device that the view was saved on
        saveDeviceGameViewSize = [aDecoder decodeCGSizeForKey:@"saveDeviceGameViewSize"];

        // Adjust the view center
        CGPoint tmpCenter = [aDecoder decodeCGPointForKey:@"center"];
        tmpCenter.x *= self.gameView.bounds.size.width;
        tmpCenter.y *= self.gameView.bounds.size.height;
        self.center = tmpCenter;

        // Restore the transform
        self.transform = [aDecoder decodeCGAffineTransformForKey:@"transform"];

        // Restore the bounds
        CGRect tmpBounds = [aDecoder decodeCGRectForKey:@"bounds"];
        CGFloat ratio = self.gameView.bounds.size.height / saveDeviceGameViewSize.height;
        tmpBounds.size.width *= (saveDeviceGameViewSize.width * ratio);
        tmpBounds.size.height *= self.gameView.bounds.size.height;
        self.bounds = tmpBounds;
    }
    return self;
}

Upvotes: 2

Views: 631

Answers (3)

occulus
occulus

Reputation: 17014

Can self.gameView.bounds change depending on what device you're using? If the answer is yes, I see a problem because you're storing the centre after normalisation by the gameView bounds size, but when you restore centre you're un-normalising it by multiplying it by the stored value of gameView.bounds, rather than the value of gameView.bounds for the current device.

Also, the bit of code for // Restore the bounds looks wrong, because in the line that sets tmpBounds.size.width = you're doing a calculation involving a width and ratio that is a ratio of two widths. In other words, there's no height involved which looks wrong. Maybe it should be the following?

// note: height, not width, used on RHS
tmpBounds.size.width *= (saveDeviceGameViewSize.height * ratio); 

In order to generally proceed with fixing this (if the above doesn't help): you need to

a) verify that the thing you're implementing makes sense (i.e. the algorithm in your head), and

b) verify that you're doing it right by analysing your code and doing some judicious debugging/NSLogging on the save + restore code

Upvotes: 0

nielsbot
nielsbot

Reputation: 16022

What about separating the view states between your iPhone and iPad version? For each device, if a configuration for that device hasn't been saved, just use the default? I haven't seen your UI, but I think in general a solution like this would work well, be easier to implement, and also follow users' expectations.

Upvotes: 1

Infinite Possibilities
Infinite Possibilities

Reputation: 7466

Very much depends on when you apply your transformation. I would propose the following steps when you load your view:
1. load the view bounds
2. load the view centre
3. load the transformation

There is also a problem with this code. You set your bounds after you set the center. This is completely wrong, because the bounds property will not be working always. Try to do the steps I've described and report back please with the result, but it should work.

Upvotes: 0

Related Questions