Reputation: 5545
Solution: For those who view this someday in the future, the solution I used was indeed viewDidLayoutSubviews
. The solution was actually rather complex - I had to calculate several scaling values and dynamically re-size the Art view every time the page needed a re-layout. There were several odd problems to handle, but after each was taken down the implementation feels pretty solid.
If anybody runs in to a similar problem later on, let me know and I can post the relevant code.
I've got a 'blank' UIView, a subview that is an UIImageView containing a single image, and a second subview that is basically a collection of CGContext arcs and lines and all that.
Where I'm at: I've placed the UIImageView subview on top of the UIView so that its image is the 'background'. Then I placed the CGContext arcs and lines subview on top of that (I'll call this the Art subview for clarity). All good. Everything displays perfectly and aligned.
Problem: When I rotate, things get screwy. The arcs and lines on the Art subview end up at the wrong coordinates, and depending on my autoresizingmask settings the image gets stretched, etc. I can fix one of these problems at a time, but I can't find the right combination to fix them both!
Things I've Tried: I've tried just about every autoresizingMask option & combination of options, but per above I can't quite lick the problem with those. Given that I also tried using some custom code in viewDidLayoutSubviews, but this felt really flimsy and much less extensible vs. using autoresizingMasks. So I abandoned the path after making some nominal progress.
What I've Learned: As long as I bind my UIImageView frame and the Art subview frame to the same dimensions, then the arcs and lines stay at the proper coordinates. That is probably the easiest way to state my goal: to have a UIImageView that stays in the correct aspect ratio (not just the image within, but the view itself), and then match the Art subview exactly to its frame - even as the screen rotates, etc.
Here is a diagram of what I'd like to achieve:
+
= UIView
~
= UIImageView subview
.
= Art subview
Portrait
Wherein the image within the UIImageView takes up basically the whole screen (though not quite), and the Art subview is layered on top of it with the dots below representing a crude line/arc.
++++++++++
+~~~~~~~~+
+~ . ~+
+~ . ~+
+~ . ~+
+~ . ~+
+~~~~~~~~+
++++++++++
Landscape
Wherein the UIImageView sublayer maintains its aspect ratio, and the Art sublayer stays 'locked' to the image within the UIImageView.
++++++++++++++++++++++++++
+ ~~~~~~~~ +
+ ~ . ~ +
+ ~ . ~ +
+ ~ . ~ +
+ ~ . ~ +
+ ~~~~~~~~ +
++++++++++++++++++++++++++
My View Controller (where I think the problem is - also removed all autoresizingMask settings to clean up the code)
- (void)viewWillAppear:(BOOL)animated {
// get main screen bounds & adjust to include apple status bar
CGRect frame = [[UIScreen mainScreen] applicationFrame];
// get image - this code would be meaningless to show, but suffice to say it works
UIImage *image = [.. custom method to get image from my image store ..];
// custom method to resize image to screen; see method below
image = [self resizeImageForScreen:image];
// create imageView and give it basic setup
imageView = [[UIImageView alloc] initWithImage:image];
[imageView setUserInteractionEnabled:YES];
[imageView setFrame:CGRectMake(0,
0,
image.size.width,
image.size.height)];
[imageView setCenter:CGPointMake(frame.size.width / 2,
frame.size.height / 2)];
[[self view] addSubview:imageView];
// now put the Art subview on top of it
// (customArtView is a subclass of UIView where I handle the drawing code)
artView = [[customArtView alloc] initWithFrame:imageView.frame];
[[self view] addSubview:artView];
}
the resizeImageForScreen: method (this seems to be working fine, but I figured I'd include it anyway)
- (UIImage *)resizeImageForScreen:(UIImage *)img {
// get main screen bounds & adjust to include apple status bar
CGRect frame = [[UIScreen mainScreen] applicationFrame];
// get image
UIImage *image = img;
// resize image if needed
if (image.size.width > frame.size.width || image.size.height > frame.size.height) {
// Figure out a scaling ratio to make sure we maintain the same aspect ratio
float ratio = MIN(frame.size.width / image.size.width, frame.size.height / image.size.height);
CGRect newImageRect;
newImageRect.size.width = ratio * image.size.width;
newImageRect.size.height = ratio * image.size.height;
newImageRect.origin.x = 0;
newImageRect.origin.y = 0;
// Create a transparent context @ the newImageRect size & no scaling
UIGraphicsBeginImageContextWithOptions(newImageRect.size, NO, 0.0);
// Draw the image within it (drawInRect will scale the image for us)
[image drawInRect:newImageRect];
// for now just re-assigning i since i don't need the big one
image = UIGraphicsGetImageFromCurrentImageContext();
// Cleanup image context resources; we're done!
UIGraphicsEndImageContext();
}
return image;
}
Upvotes: 3
Views: 1154
Reputation: 385998
There is no combination of autoresizing flags and content modes that will do what you want. Using viewDidLayoutSubviews
is one reasonable way to handle the layout. That's why it exists: the autoresizing flags are pretty limited.
A different approach is to change your -[ArtView drawRect:]
method so that the autoresizing flags can do what you want. You can make your drawRect:
method implement the same algorithm that UIImageView
implements for UIViewContentModeScaleAspectFit
.
Upvotes: 2