Autolayout: UIImageView not rotating with device orientation - ios

There's probably a dozen SO questions with similar titles, and as far as I can see, I've incorporated the advice from every one of them, with no luck. When I rotate the device from portrait to landscape, I want to change the background image. However, in every experiment I have tried, the UIImageView in landscape remains portrait sized.
I am using Autolayout, configured in IB like so:
My view hierarchy is configured like so:
When I rotate the device, I want to rotate everything in UIView viewBackground (the image and all of the buttons). I believe I can manage the button movements through constraints, but need to rotate UIImageView image view background myself. So, I added the following code:
-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
int scaleFactor = self.traitCollection.displayScale;
NSString *source = #"drawn";
NSString *orientation;
if (size.width > size.height)
{
orientation = #"Landscape";
}
else
{
orientation = #"Portrait";
}
NSString *platform;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
platform = #"iPad";
}
else
{
platform = #"iPhone";
}
NSString *filename = [NSString stringWithFormat:#"%# %# %# %.0fx%.0f.png", source, platform, orientation, size.width * scaleFactor, size.height * scaleFactor];
// CONFIRMED: we have assembled the correct new image name.
viewBackground.frame = CGRectMake(0, 0, size.width * scaleFactor, size.height * scaleFactor);
viewBackground.autoresizesSubviews = YES;
viewBackground.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
imageViewBackground.autoresizesSubviews = YES;
imageViewBackground.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
imageViewBackground.image = [UIImage imageNamed:filename];
}
The problem: The correct image appears on the screen, but as as mentioned above, it still has the portrait dimensions. I'm certain the image changes as the artwork is significantly different, with elements being stacked horizontally versus vertically. But the new picture is cut off horizontally in landscape mode.
I have tried putting all sorts of wild values into newRect, and none of them make any difference. The image never actually moves, even if I radically change the origin in newRect. so my current theory is that my changes to the view sizes are being ignored/over written.
The Question: What am I missing that's preventing the resizing of the image to landscape mode? Is there some other auto sizing aspect I'm not addressing?

So, the problem turned out to be yet another variable in the saga. There also must be a constraint on the UIImageView that is 0 on each side, with "Constrain to Margins unchecked." Apparently, when I did that step earlier, it didn't actually take.
I have to admit, though, I really don't understand what that constraint is doing for me. It clearly makes the rotation work, but I don't get why. Since that's technically the answer to my original question, I'll gladly award the answer if someone can explain how this constraint enables this scenario.
EDIT:
In case anyone stumbles upon this answer in the future... I see now that setting the aforementioned constraint with all 0 buffers between the UIImageView and the UIView pins the edges of UIImageView to the edges of the UIView. When the device rotates, the viewController resizes the UIView, and with this constraint, the UIImageView resizes also. I was able to remove the majority of my code. The only code I need is to select a portrait or landscape image, and I was able to remove everything that resized the UIView and UIImageView.
The image now changes size properly when rotating just by using the one constraint. I only need code to decide whether to show a landscape or portrait background. (And I vastly simplified that code by storing my images in an asset catalog so the code just selects the name of "portrait" or "landscape", but the size of the image is auto selected based on the device).

Related

UITableViewCell Transform Breaks in iOS 11

In an app I've built, I had a table view showing an image to the left of each cell by running the following code inside of cellForRowAtIndexPath:
cell.imageView.image = [UIImage imageNamed:myImage];
But the image was too big, so I shrunk it:
cell.imageView.transform = CGAffineTransformMakeScale(0.3, 0.3);
This worked just fine in iOS 10. But once I upgraded to the newest Xcode with the iOS 11 SDK, the images got enormous. It turns out that that second line of code transforming the image view is now doing nothing: I can comment it out, change the 0.3's to something else, etc., and it doesn't make any difference. CGAffineTransformMakeScale still has documentation in the new Xcode, so I'm assuming it wasn't deprecated, but then why did this break with iOS 11, and how do I fix it? Any ideas? Thanks in advance! Please note, I'm using Objective-C.
Edit:
Just tried 3 changes to the code:
Change the second line to cell.imageView.transform = CGAffineTransformMakeScale(0.0000001, 0.0000001);. Nothing happens (i.e., the image views and the images inside them are still just as huge).
Change the second line to cell.imageView.transform = CGAffineTransformMakeScale(0, 0);. The image disappears from the image view, but the image view is still the same size, and you can tell because it still displaces the text in the cell and pushes it far to the right.
Remove the first line of code (no longer assigning an image to the imageview). The imageview disappears, and the text moves all the way to the left of the cell.
Perhaps this can help shed some light on what's going on?
So if you are trying to adjust the image size to fit the imageView you should actually use the imageView's contentMode property like so:
cell.imageView.contentMode = UIViewContentModeScaleAspectFit;
or in Swift for others
cell.imageView.contentMode = .scaleAspectFit
This keeps the dimensions of the image and fits the maximum size image onto the imageView without changing the dimensions
You could also try UIViewContentModeScaleAspectFit (or .scaleAspectFill in Swift) which basically fills the dimensions of the imageView entirely, but if the picture is wider or taller than the image view it crops what can't fit.
Here are all the contentModes directly from a Obj-C and Swift source files:
typedef NS_ENUM(NSInteger, UIViewContentMode) {
UIViewContentModeScaleToFill,
UIViewContentModeScaleAspectFit, // contents scaled to fit with fixed aspect. remainder is transparent
UIViewContentModeScaleAspectFill, // contents scaled to fill with fixed aspect. some portion of content may be clipped.
UIViewContentModeRedraw, // redraw on bounds change (calls -setNeedsDisplay)
UIViewContentModeCenter, // contents remain same size. positioned adjusted.
UIViewContentModeTop,
UIViewContentModeBottom,
UIViewContentModeLeft,
UIViewContentModeRight,
UIViewContentModeTopLeft,
UIViewContentModeTopRight,
UIViewContentModeBottomLeft,
UIViewContentModeBottomRight,
};
public enum UIViewContentMode : Int {
case scaleToFill
case scaleAspectFit // contents scaled to fit with fixed aspect. remainder is transparent
case scaleAspectFill // contents scaled to fill with fixed aspect. some portion of content may be clipped.
case redraw // redraw on bounds change (calls -setNeedsDisplay)
case center // contents remain same size. positioned adjusted.
case top
case bottom
case left
case right
case topLeft
case topRight
case bottomLeft
case bottomRight
}
EDIT:
Since I see you're also interested in changing the dimensions of the imageView itself ("to leave more room for the text") what I would suggest is actually, either in storyboard or programmatically use AutoLayout to add your own imageView and have it sized and placed how you want it, instead of using the default imageView on a cell which is meant as a convenient/standardized tool.
If you are unfamiliar this I would google for an AutoLayout tutorial. Or maybe "using AutoLayout to create custom UITableViewCell"
If you dont actually want to create your own subclass you can try setting the `cell.imageView.frame = ..." somewhere to manually resize it to what you want, then setting its content mode to make sure the image fits nicely still.
See this question: How do I make UITableViewCell's ImageView a fixed size even when the image is smaller
Found an answer to my own question, with credit due to Paul's answer from this question: How to resize a cell.imageView in a TableView and apply tintColor in Swift
CGSize itemSize = CGSizeMake(50, 50);
UIGraphicsBeginImageContextWithOptions(itemSize, false, 0);
CGRect imageRect = CGRectMake(0, 0, itemSize.width, itemSize.height);
[cell.imageView.image drawInRect:imageRect];
cell.imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
I still don't know why the old CGAffineTransformMakeScale doesn't work anymore, but this gets the job done.

How to overlay two images in ios6 with transparency

I am trying to overlay two images and put text on top in a view that I have. I have this working perfectly in ios7. Here is a screen shot of the results
Right now the gradient is simply an image on top of the other image as seen here in my layout
This works great except for when I test on my phone with ios6. Then everything goes nuts as seen here. *I've actually deleted the gradient layer and ran the app again and the background image remains the same size (about half of what it should be).
As you can see, the background image is only half of what it should be, and the second image is not overlaying. I've been at this for 5 hours and can't seem to find a solution that works.
Here is the code that sets the background image
-(void) SetDetails
{
if(_curInfo)
{
_lblTopName.text = _curInfo.company_name;
if(!_curInfo.img)
{
showActivity(self);
dispatch_queue_t aQueue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(aQueue1, ^{
_curInfo.img = getImageFromURL([NSString stringWithFormat:#"%#%#", g_serverUrl, _curInfo.imgPath]);
dispatch_async(dispatch_get_main_queue(), ^{
hideActivity();
[_imgCompany setImage:_curInfo.img];
});
});
}
[_imgCompany setImage:_curInfo.img];
/* FIX IMAGE SIZE */
_imgCompany.contentMode=UIViewContentModeScaleAspectFill;
CGRect photoFrame = _imgCompany.frame;
photoFrame.size = CGSizeMake(320, 180);
_imgCompany.frame=photoFrame;
[_imgCompany setClipsToBounds:YES];
_lblDistance.text = [NSString stringWithFormat:#"%.2f miles", _curInfo.distance];
_lblReward.text=_curInfo.reward;
CGFloat scrollViewHeight = 0.0f;
for (UIView* view in scroller.subviews)
{
scrollViewHeight += view.frame.size.height;
}
[scroller setContentSize:(CGSizeMake(320, scrollViewHeight))];
}
}
Any help is greatly appreciated. I'm not opposed to drawing the gradient onto the image either.
Additional Info:
Here is how I have the two image views setup.
You need to unstick the 'opaque' option on your image view, given that it isn't opaque.
As to the other spacing issue, I'd guess it's a mismatch between iOS 7 view controllers always acting as if they had wantsFullScreenLayout set to YES but the default having been NO under iOS 6. The rest of your image is probably underneath your navigation bar. It looks like you're part trying the interface builder and part doing programmatic layout — why did you add the code underneath FIX IMAGE SIZE and what happens if you remove it?

Giving absolute values to ui component - doesn't work for iPad

I have added a text box programatically. My code is as follows:
t = [[UITextField alloc] init];
[t setFrame:CGRectMake(55,200,200,50)];
[self.view addSubview:t];
When i run it on the iPhone simulator it works perfectly (the textfield positioned at the exact point that i want), but when i run it on the iPad the positions are not proper. I was thinking that it will auto adjust it self when the screen size increase/decrease. How am i going to correct this ?
note: I am not going to use interface builder or story board.
Setting absolute values work just as expected. That’s why the are called absolute values. The problem are flexible/dynamic values.
It will not adjust automatically until you tell it to do so. Here are your options:
Manually check for User Interface Idiom and set two different values. This is the correct way of detecting iPhone and iPad UI:
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) ...
(Create some function for this.)
Calculate the values based on screen or superview size:
CGSize screen = [[UIScreen mainScreen] bounds].size;
frame.size.width = screen.width / 2 - 40;
Set autoresizing mask, which set up rules on now subviews are resized, when superview changes size:
view.autoresizingMask = UIViewAutoresizingMaskFlexibleWidth;
Must be combined with some fixed or calculated frame values. Autoresizing is useful only when the view resizes, not when you set it for the first time, so this is not exactly your case. For full description of how this works, see UIView documentation.
Use Auto Layout, which is powerful, but quite complex soluton to dynamic layout. At first you would need to read about it and experiment to fully understand how to use it.
If you have a view, whose size and position cannot be described with some kind of rules, you will have to write the coordinates directly (option 1).
Hey mate please use the below code if you are not comfortable using the storyboard or xib file.
NSString *deviceType = [UIDevice currentDevice].model;
t = [[UITextField alloc] init];
if([deviceType isEqualToString:#"iPhone"]) // For iPhone
{
[t setFrame:CGRectMake(55,200,200,50)];
}
else // For iPad
{
[t setFrame:CGRectMake(55,200,600,100)];
}
[self.view addSubview:t];
Hope this helps you.

UIImgePickerView Images taken in landscape stretched when displayed

In the UIImagePicker the user takes photos, then the photos are saved, and loaded into a tableview, now when the images are taken normally (portrait) they are a perfect size in the image view because this is how I set it. But when the user takes an image with the device in landscape, the image looks skewed and looks very distorted. (See picture)
Top is portrait picture, bottom is landscape
So does anyone have any suggestions on how this can be done?
Any help would be amazing
If it is taken in Landscape, then you show it another frame with size width > height. Or try using the following code and see if it improves something.
imageViewTemp.clipsToBounds = YES;
imageViewTemp.contentMode = UIViewContentModeScaleAspectFit;
The UIImageView stretches images to fit its size. This is what you are seeing. To stop it, you might change the size of the image view, like so:
CGRect newFrame = CGRectMake(imageView.frame.origin.x,
imageView.frame.origin.y,
image.size.width,
image.size.height);
imageView.frame = newFrame;

UIScrollView - paging UIImage larger than the screens width

Am currently developing an iPad app which uses a UIScrollView. The UIScrollView is populated with UIImage(s) and all the images are larger than the iPads width, twice the width, 1536px. What I would like it to do is when swiped/flicked it will scroll to the second image, i.e. 1536 and the third image to 3072 and so on. Its just to see a quick image when sliding across. I've had a look at scrollViewDidEndDragging but it gets really nasty and jumpy at times.
Is there a way of setting the animation to scroll by 1536 each time apart from the last UIImage in the UIScrollView? I know you can use setContentOffset but if I use this in the above method it doesn't work as its using the current transition still and therefore making it very jumpy.
Edit
for (i = 1; i <= kNumImages; i++)
{
NSString *imageName = [NSString stringWithFormat:#"cocktail_%d.png", i];
UIImage *image = [UIImage imageNamed:imageName];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
CGRect rect = imageView.frame;
rect.size.width = kMainImageWidth;
rect.size.height = kMainImageHeight;
rect.origin = CGPointMake(xpos, 0);
imageView.frame = rect;
[mainImgScrollView addSubview:imageView];
[imageView release];
xpos += kMainImageWidth;
}
Edit
All I needed to do was make the UIScrollView 1536px wide and it worked. The app will have 6 images to start with and more in the future. Hopefully there won't be a memory issue to start with?
Thank you for your help.
The page size is the UIScrollView size. It is perfectly acceptable to make the UIScrollView itself larger than the width of the iPad screen. If you make the UIScrollView 1536px wide, then each page will be 1536px wide.
To allow the user to scroll around and see each picture, what you want is a scrollview containing a row of scrollviews. The outer scrollview is the one in the window, it is 1536px wide, and it is just for paging. The inner scrollviews are the width of the screen and they have their contentSize set to the image size so that the user can scroll around in each one and see the image.
(However, you're going to want to rethink your architecture, since an app into which you have predrawn multiple images 1536px wide will not run (because it will exceed the available memory for the device). The WWDC2010 videos include an excellent talk on this very topic, i.e. how to design a paging scroll view that lets you page from image to image.)
I think you want to turn on paging on your UIScroll view. This will make it "snap" to the width of the scroll view. It will behave like the home screen on the iPad/iPhone does. You may need to make your scrollView's frame wider than the screen also to get the paging effect correct. You may encounter some lag no matter what due to the size of your images.

Resources