My storyboard has UIImageView which is automatically positioned and scaled by autolayout constraints.
When application runs, this UIImageView is properly resized and positioned according to device screen size and autolayout constraints.
How I can get UIImageView frame after it is displayed on the screen?
Common UIImageView.bounds and UIImageView.frame values return original values which were used in storyboard and does not reflect new UIImageView size.
In order to get the right frame/bounds of your UIImageView after resizing, you need first ask auto-layout to update that layout using [yourImageView layoutIfNeeded]. that will solve your constraints and update your yourImage.bounds.
[myImageView layoutIfNeeded];
NSLog(#"w: %f, h: %f", myImageView.bounds.size.width, myImageView.bounds.size.height);
Check these methods:
- (void)viewWillAppear:(BOOL)animated {
// view is about to be added to hieararchy
}
- (void)viewDidAppear:(BOOL)animated {
// view was added
}
- (void)viewDidLayoutSubviews {
// VC just laid off its views
}
Related
I have UIPageViewController that contains simple UIViewControllers. The View of the VC contains an UIImageView (aspect fit) at the top and a UITextView at the bottom.
The LayoutConstraints for the ImageView are:
Fixed Width: Which has an Outlet to the VCs-Class
Fixed Height: Which has an Outlet to the VCs-Class
Center X is equal to the superviews centerX
Top is equal to Superviews MarinTop (+8)
The LayoutConstraints for the ImageView are:
Top is equal to ImageView bottom
Bottom is equal to superviews MarginBottom (+8)
Width is equal superviews width (Multiplicator is 0.8)
This works as expected.
But when the VC is loaded and has the image to display, I want to change the size of the imageView. There is a minimum and a maximum size and if a image view is smaller or larger it needs to be resized. Otherwise the the imageview has to get the size of the image.
So I calculate the size for the actual image and set the constant for the two LayoutConstraints. The values are correct but the ImageView does not change.
I tried to set them in viewWillLayoutSubviews but no change.
What am I doing wrong?
Try update your views inside viewDidLayoutSubviews:
- (void) viewDidLayoutSubviews {
// setup your constraints here
}
Hope this helps.
Write your change constraint code in the below provided block
dispatch_async(dispatch_get_main_queue(), {
})
Try changing the vertical spacing between the ImageView and the textfield to be flexible (>=0). If that doesn't solve the issue, add:
[view setNeedsLayout];
[view layoutIfNeeded];
After you adjust the size of the imageview to size of the new image.
I'm loading a view from a storyboard programatically, but there's an issue with my subview heights (I'm using Autoresize Subviews etc).
When the view loads, the main UIView appears to be the correct size, but the subviews that depend on the constraints to dynamically calculate their final height don't seem to be resizing from the sizes they were set at in interface builder.
Loading the view like so:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
ApplicationRecordingViewController* recordingController = (ApplicationRecordingViewController*)[storyboard instantiateViewControllerWithIdentifier:#"recordView2"];
[self presentViewController:recordingController animated:NO completion:nil];
Orange area's height is from top of main view to top of grey area. Grey area is fixed height
The strange thing is, if I set the simulated size in interface builder as the correct view for my phone, the sizes work out. I guess the initial heights for subviews are 'baked' into the view, but then (usually) resized when the view actually loads?
Note: The orange area should be completely filled by the camera view I'm loading into it.
Incorrect height
Correct height if I change simulated layout
Would love to know what I'm doing wrong!
Edit 1: Constraints
Camera is the orange bit. The Camera bottom constraint is currently set as the bottom of the view + enough room for the gray bar. Have also tried setting the bottom to match the top of the gray bar!
Link to Storyboard with Scene
I know you're all better than me, but I experienced the same in my last project. My Camera View inside a UIView is not in full screen or resizing and fitting the whole UIView, and I also posted it on my daily blog.
Implement viewDidLayoutSubviews -- Inside that method, layout again your video preview layer.Do not alloc init anything inside that method, just re-layout your views. After that, your video preview will fill the height and width of its parent, or whatever you've been targetting.
The issue also has something to do with the hierarchy of the constraints and views.
You already got the answer.
'if I set the simulated size in interface builder as the correct view for my phone, the sizes work out.'
You may call some functions to get height in viewDidLoad or viewWillAppear.
Change Code like this
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.view setNeedsLayout];
[self.view layoutIfNeeded];
CGFloat height = [self getHeight];
}
or
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
CGFloat height = [self getHeight];
}
As i can see in your storyboard constrain you have set bottom space to bottom layout guide 150 . i think this is the issue . if you give fixed height to bottom view then no need to set constant as from bottom layout guide --> 150 .
Your old constrain :- >
Just set bottom space of orangeview from greenview = 0.
Check new constrain ,
Here is your storyboard link ,
Storyboard Download link
Here is your output,
After investigating the layout a little more, I found the camera view (orange) seemed to be the correct size, but the live camera preview wasn't.
Solution was to move the camera library (SCRecorder)'s init method into viewDidAppear (was in viewDidLoad before).
My thinking is iOS doesn't autolayout the camera preview view in the same way as normal UIViews, etc.
#interface ApplicationRecordingViewController () {
SCRecorder *_recorder;
}
#end
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
_recorder.previewView = _previewView;
}
Perhaps you can try to get rid of these two hook.
I have parentViewController and it contains Container with customViewController with autoresizing masks (flexible width and height). Container's width is ~80% of parentViewController view.
When I rotate device viewWillTransitionToSize:withTransitionCoordinator: is called but with "wrong" size. I get parentsViewController view size.
Here is part of customViewController
-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
NSLog(#"viewWillTransitionToSize %#", NSStringFromCGSize(size));
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
[self resizeViewToWidth:size.width];
[self performSelector:#selector(didRotate) withObject:nil afterDelay:1.0];
}
-(void)didRotate
{
NSLog(#"didRotate %#", NSStringFromCGSize(self.view.frame.size));
}
//it creates this log
//viewWillTransitionToSize {667, 331}
//didRotate {617, 311} //this is correct size after applying autoresizing masks
Why is it called only once when I see at least two different sizes?
I don't want to calculate view size if autoresizing masks can do it for me.
How do I let apply autoresizing masks first and then get size after applying?
What is correct way of using viewWillTransitionToSize while using autoresizing masks?
I solved it following way. It is actually overriding autoresizing mask but I couldn't find better solution and this fits my requirements.
I use method sizeForChildContentContainer:withParentContainerSize: of UIContentContainer. In method I check for child container and apply changes same as autoresizing would do by itself. magicNumber is abbreviation for custom calculation.
-(CGSize)sizeForChildContentContainer:(id<UIContentContainer>)container withParentContainerSize:(CGSize)parentSize
{
if ([container isKindOfClass:[customViewController class]])
{
return CGSizeMake(parentSize.width - magicNumber, parentSize.height);
}
return [super sizeForChildContentContainer:container withParentContainerSize:parentSize];
}
How can I vertically center an image inside a scrollView?
I'm using storyboards in Xcode 5. The main view is embedded inside a navigation controller, and "Adjust scroll view insets" option is enabled in main Storyboard. This main view has a scrollView which size is equal to the main view size.
The imageView is inside the scrollView and it's the same size as the scrollView. Content mode is set to AspectFit.
So, hierarchy is as follows:
- UINavigationController
- UIView
- UIScrollView
- UIImageView
The image may be landscape or portrait, and can be any size (it's loaded at runtime). This is why imageView is the same size as the scrollView.
How can I vertically center the image inside the scrollView?
EDIT:
As commented before, I have set imageView's contentMode to AspectFit because the image may be too big, so I need it resized. The problem I have is that the image is not center of the scrollView.
You can check screenshot at link and download source code at link.
It will be good to use auto layout as mentioned by #Douglas. However, if you prefer the traditional way, you can also make it work.
I'll first give you the answer and then explain it to you. You should first delete the image view from the storyboard ( I'll explain it later), and then add the viewWillAppear method.
- (void)viewDidLoad
{
[super viewDidLoad];
// 1. Add the image view programatically
UIImageView * imageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"portrait.jpg"]];
[_scrollView addSubview:imageView];
_imageView = imageView;
}
- (void)viewWillAppear:(BOOL)animated
{
// 2. calculate the size of the image view
CGFloat scrollViewWidth = CGRectGetWidth(_scrollView.frame);
CGFloat scrollViewHeight = CGRectGetHeight(_scrollView.frame);
CGFloat imageViewWidth = CGRectGetWidth(_imageView.frame);
CGFloat imageViewHeight = CGRectGetHeight(_imageView.frame);
CGFloat widthRatio = scrollViewWidth / imageViewWidth;
CGFloat heightRation = scrollViewHeight / imageViewHeight;
CGFloat ratio = MIN(widthRatio, heightRation);
CGRect newImageFrame = CGRectMake(0, 0, imageViewWidth * ratio, imageViewHeight * ratio);
_imageView.frame = newImageFrame;
// 3. find the position of the imageView.
CGFloat scrollViewCenterX = CGRectGetMidX(_scrollView.bounds);
CGFloat scrollViewCenterY = CGRectGetMidY(_scrollView.bounds) + _scrollView.contentInset.top / 2 ;
_imageView.center = CGPointMake(scrollViewCenterX, scrollViewCenterY);
}
Here is the explanation:
You should not put the imageView in the storyboard, otherwise the frame of the imageView will be fixed by the storyboard, and will not change with the size of the image. Even if you choose UIViewContentModeScaleAspectFill, the frame of the imageView is still not changed. It just add some white space around the image.
Now the imageView has the same size as your image. If you want it to be fully displayed, you need to calculate the frame yourself.
Pay attention to the _scrollView.contentInset.top / 2, this is why you need to put the codes in viewWillAppear instead of viewDidLoad. The _scrollView.contentInset.top is the height of the navigation bar and is calculated automatically for you before willViewAppear.
You put your image view in a scrollView, I guess you want to zoom in and out. If this is true, add self.imageView = imageView; and the bottom of viewDidLoad. Set the delegate of _scrollView to self and add the following method:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return _imageView;
}
I made a comment, but then took a look at your project. You are almost there. I ran through the following steps and have gotten the result you are looking for.
First, make sure you have auto layout turned ON!!!
In your storyboard click on your scroll view. You had a scroll view that was the same size as the view. You are going to put on some constraints. Down at the bottom of the story board you will see some icons.
The fourth one over looks sort of like an I-beam on its side, it is the pin button. After selecting the scroll view, click on this and it will bring up a pop up menu.
For the scroll view click on all the bars around the middle block so you pin the scroll view to the sides of the main view.
You will notice they are all red now.
Then go and click on the imageview. Once again you had it set to the size of the view. Using the pin button again, you are going to pin just the Width at 320 and the Height at 568. When you are done you are then going to use the next button over.
This is the align button. Click on that after you have selected your image view. You are going to click on Horizontal Center in Container, and Vertical Center in Container.
Next you will need to add one method to your ViewController.m file.
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[_scrollView setContentSize:CGSizeMake(self.view.frame.size.width, self.view.frame.size.height)];
}
Start up the simulator and let her rip! You will get one warning though. It says the content size is ambiguous for the scroll view. But that's OK, because you will set it on viewDidLayoutSubviews.
Hope that helps, or helps someone out. Autolayout and scroll views are a bit tough!!
EDIT#1
if you want to then make the image view scalable, by pinch zooming you can do the following.
Make sure you made the .h file follow the UIScrollViewDelagate.
#interface ViewController : UIViewController <UIScrollViewDelegate>
This will allow the scroll view to be able to access the delegate methods of the scroll view. The method you are looking for is called..
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.imageView;
}
Then in the viewDidLoad method of your .m file do the following.
_scrollView.minimumZoomScale = 0.5;
_scrollView.maximumZoomScale = 4.0;
_scrollView.delegate = self;
The underscore and the variable name is the same as self.variable. Either will work.
That should do it. Let me know if it works or if you have any other questions. ENJOY!
These are the ones u can use, the 3 modes of ImageView content display.You can do this by dynamically setting them or u can set them in storyboard too, click on the ImageView and go to properties tab-bar and choose from there, run them and select which output u want.
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.contentMode = UIViewContentModeScaleToFill;
imageView.contentMode = UIViewContentModeScaleAspectFill;
Hope this helps
If you want to center image in an imageview use
imageView.contentMode=UIViewContentModeCenter;
image retains it's size in this this content mode. Alternatively you can use other content modes as per your requirement.
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,
I have a UIViewController with a UICollectionView and a UIView at the bottom. The way I put it together is displayed in the image below
The yellow square is the UICollectionView and the red is the UIView. This works out just fine. But now I want to resize the UIView because it sometimes contains more info and needs to be bigger. So I tried this:
[self setFrame:CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, self.frame.size.height + 10)];
But this expands the UIView at the bottom and it is not visible. I guess this is because I do not have the correct constraints? I also tried to subtract the origin.y with the same amount and this works only the UICollectionView doesn't get resized with the new height. So how do I tackle this problem?
If you are using autolayout, you should not be setting the frame from your code. Instead you should modify the constant of a constraint that is causing your view to be the incorrect size. You can have an IBOutlet to that constraint and you can change it's constant property. Then call setNeedsLayout on your view controller's view
When setting constraints on your storyboard or in a xib file, animations perform animations on the constraints instead of the sizes and positions.
First create a outlet reference of the constraint which will change (in your case the top space of your UIView to the top layout guide) in the header file of your view controller.
When you want to animate a view, you now have to update its constraints and ask to layout the views.
For example :
[UIView animateWithDuration:0.2
animations:^{
viewYConstraint.constant -= 44;
[self.view layoutIfNeeded];
}
]
//Now don't forget to update constraints
[self.view updateConstraints];