I have a view and a subView which is of type UIView with constraints so that it's width is equal to its superview.(I use auto Layout)
However,when i try to get frame property of this subview in viewController in
- (void)viewDidLoad {
NSLog(#"%#",NSStringFromCGRect(self.subview.frame));//get subview's frame
}
method,I always get {{0, 20}, {600, 320}} which means its width is 600.
But the actual width is 320,did this mean that this subview hasn't been layouted when this method is called,and how could I get this actual width in this method?
Any help is appreciated,Thanks.
subview's frame isn't valid in viewDidLoad -- it hasn't been laid out yet. Move your code to viewDidLayoutSubviews.
If you find you have a dependency which requires you to look at the frame in viewDidLoad, you probably don't have your constraints designed correctly. Look for areas where you can add more relationships between views. If that doesn't help, try modifying constraints programmatically -- when using autolayout, you almost never want to look at or explicitly change a view's frame.
Related
I've got an UIImageView inside a root view of a controller, and I've set it to be 90% of the screen width and given it an aspect ratio as constraints to set the dimensions.
In the code I'm trying to do something with respect to the size of the UIImageView at runtime, however when I get the frame.size.height or frame.size.width of the UIImageView they are clearly wrong and way too small.
At first I was accessing the size in viewDidLoad(), after which I found quite a few posts suggesting to do it either in viewWillLayoutSubviews(), viewDidLayoutSubviews(), or viewWillAppear(). Unfortunately I've tried all of those and none of these contexts seem to provide the right value. I suspect it may have something to do with auto layout but I'm not quite sure how to get around this. Any insight as to why this may be would be appreciated
viewDidLoad is too early. At this time, the views have the frames they were given in the storyboard. Ditto for viewWillAppear.
In viewWillLayoutSubviews, the view controller's top-level view has its correct frame, but its descendants do not.
In viewDidLayoutSubviews, the view controller's immediate subviews have their correct frames, but more distant descendants (“grandchildren” and so forth) don't.
If the image view is a direct subview of the view controller's view, then its frame is up to date in viewDidLayoutSubviews.
If the image view is a more distant descendant, then there is no method you can override in the view controller that will be called after the image view's frame has been updated but before the image view is visible on screen. Here are two options in this case:
Create a custom subclass of UIView to be the superview of the image view. When the superview's layoutSubviews runs, after it calls super.layoutSubviews, the image view's frame is up to date.
Create a hidden UIView that is a direct subview of the view controller's top-level view. Use constraints to make this hidden view's frame exactly match the image view's frame. Hidden views participate in layout, so when viewDidLayoutSubviews is called, this hidden view's frame is up to date, and is the same as the image view's frame will eventually be (except that the hidden view's frame is in the top-level view's geometry, which might be different than the geometry of the image view's superview).
My viewController has one view with images and labels and one textView
Im new in objective c.
My problem is to add ScrollView in my ViewController with 2 custom views(UIView and UITextView).(image in the link) I have tried many things posted here in Stack but nothing works for me.
Thank YOU!
Here is what i have :
self.scrollView.contentSize=self.scrollView.frame.size;
self.scrollView.frame=self.view.frame;
[self.view addSubview:self.scrollView];
Adjusting view's frame was the technology of 5 years ago. You should never set the frame manually, not anymore. Instead start learning Autolayout and Constraints.
These tutorials may help:
https://www.raywenderlich.com/115440/auto-layout-tutorial-in-ios-9-part-1-getting-started-2
https://www.appcoda.com/auto-layout-guide/
You are setting the content size equal to the frame size before you actually set the frame, so it's probably just 0.
You need to just switch the calls around:
self.scrollView.frame=self.view.frame;
self.scrollView.contentSize=self.scrollView.frame.size;
[self.view addSubview:self.scrollView];
The other thing to keep in mind is that because you are setting the frame of a nested view to the frame of its superview, your layout will break (or at least not do what you expect), if the origin of your superview ever changes. If the origin is 0, 0, then you are fine for the moment, but otherwise you may want to set the subview (scrollView) frame to be equal to the superview (self.view) bounds instead of the frame, like this:
self.scrollView.frame=self.view.bounds
I've created a view controller using interface builder, while size classes is on.
the view has leading and trailing constraints to it's super view which is the view controller's view. something like this.
|-(14)-(view)-(14)-|
When i'm running the app it looks great, the view has those 14pt insets on both sides, though when I print it's width I get 576 which is wider than the screen width. In interface builder the view's frame width is set to 576 (as the canvas size when size classes is on) but at run time the constraints should override this I guess.
How come the view appears right, but it's frame shows a bigger width ??
When did you print the width ?. I guess in ViewDidLoad. Because you use Interface Builder and enable size classes, so by default, the width and height are 600x600. At the time view did load, the size still refer the size from Interface builder until view did appear, it will be automatically scaled according to the screen size. That why your UIView is wider than the screen.
Here is what I printed out from a view controller built using Interface builder and enable size classes. Run on iPhone 6S plus
[Size] at view did load {{0, 0}, {600, 600}}
[Size] at view did appear {{0, 64}, {414, 623}}
You can use the below methods to update your view:
// Allows you to perform layout before the drawing cycle happens. layoutIfNeeded forces layout early
[view setNeedsLayout];
[view layoutIfNeeded];
I'm adding a view controller with a table view as a subview of a main view controller. I'm setting the constraints for position and size, i'm not givin a specific frame for the view, but i noticed that the header's labels are in a wrong frame, they have a x position of -30, instead of zero.
This happens only if i use constraints, if i use normal setframe method this problem doesn't happen.
I've tried with layoutIfneeded, or layoutSubviews but nothing seems to work
I've got a UIViewController with an iPad xib in portrait orientation. When my iPad is in landscape orientation and I put that view controller into my UISplitViewController's detail pane, it gets automatically resized to fit which is great.
However, when I'm configuring my views in -viewDidLoad, the final size of the views is not yet in landscape orientation/size.
The -didRotateFromInterfaceOrientation: method and the other methods associated with it do not get called when the UIViewController is loaded already in landscape, so when and where am I able to properly set the contentSize of the scroll view and it's contents?
I only want my scroll view to scroll vertically, so I want to make sure that my contentView inside the scrollView is re-fit to the width of the view controller in whatever orientation it is in.
No matter what .frame or .bounds I check, whether it's on my view, my scroll view, or even the detail view controller of my SplitViewController show me what my ACTUAL size is. When in landscape using a UISplitViewController, the left hand side is 320px wide which means the right hand side shouldn't be more than 704px wide, but whenever I check the frames and the bounds of my view and my scrollview, they report as 768px wide which is not correct.
My scrollview is CLEARLY only 704px wide because I can see the scroll indicators correctly.
What am I missing?
Here is my code in -viewDidLoad...
CGSize textSize = [self.purchase.textDescription sizeWithFont:self.labelTextDescription.font constrainedToSize:allowedSize lineBreakMode:UILineBreakModeWordWrap];
CGRect textFrame = self.labelTextDescription.frame;
textFrame.size.height = textSize.height;
self.labelTextDescription.frame = textFrame;
CGRect contentFrame = self.contentView.frame;
contentFrame.size.height += textSize.height;
if (contentFrame.size.width > self.scrollView.frame.size.width) {
contentFrame.size.width = self.scrollView.frame.size.width;
}
self.contentView.frame = contentFrame;
[self.scrollView addSubview:self.contentView];
self.scrollView.contentSize = self.contentView.frame.size;
The proper time to lay out your views and set your scroll view's contentSize is during the layout phase of the run loop. During this phase, UIKit sends the layoutSubviews message to any view that has been marked as needing layout and is in a window. A view is automatically marked as needing layout when various things happen, including when it is first added to a window hierarchy, when it is given a new subview, and when its size changes. You can also manually mark a view as needing layout by sending it the setNeedsLayout message.
By the time a view receives the layoutSubviews message, UIKit has already sent layoutSubviews to any of the view's ancestors (its superview and up) that needed it, and it has already had its frame adjusted based on its autoresizing mask or autolayout constraints, and its own subviews' frames have already been adjusted based on their autoresizing masks or autolayout constraints.
If self.view is already a custom subclass of UIView, the best approach is simply to override layoutSubviews in that class. Put your layout code there, and set the scroll view's contentSize there.
If you're not using a custom subclass, and you don't want to create one, then you can do the layout in your view controller's viewWillLayoutSubviews or viewDidLayoutSubviews method, if you're deployment target is iOS 5.0 or later. You can probably guess when these messages are sent. :)
During autorotation, all of these messages (layoutSubviews, viewWillLayoutSubviews, and viewDidLayoutSubviews) are sent inside the autorotation's animation block, so if you do your layout in one of these methods, you also get the benefit that the changes to your layout will be animated during the autorotation animation.
First of all, if you don't want to get the view resized when rotating check the autoresize mask. If you built the view in the interface builder then check that it's not changing it's size when the superview does:
You can check the current interface orientation of your UIViewController with self.interfaceOrientation. This might help you if you want to set up you view manually.
Furthermore, if you need to adjust the sizes manually then set the desired frames to your views before (or after) the rotation is performed:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration;
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation;
I would recommend query the status bar orientation to get the current rotation in viewDidLoad I would recommend not relying on auto-resizing for scroll views as it tends to get tricky. Check the status bar the resize proportionally based on that.
[UIApplication sharedApplication].statusBarOrientation