Reputation: 968
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
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
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
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