I have a custom view that gets loaded from nib that has a number of subviews. Some of these subviews contain labels which can be a dynamic height. This view ends up being added as a subview to a view that is in a scroll view.
Think ScrollView > View > SubView > Dynamic Labels
I am using auto layout constraints and therefore need to set the height of SubView that is required to display all the content in itself, so that the ScrollView will scroll the length necessary to display everything in the SubView.
I know with a UILabel, you can do something like val neededLabelHeight = label.sizeThatFits(CGFloat(width: label.frame.height, height: CGFloat.greatestFiniteMagnitude)) to get the needed height to display on a single label, but when I attempt to do this on a whole custom view loaded from nib, it just has the height of the view I have in my nib file.
Is there something similar for doing it on a view with more subviews, other than calculating the needed hight of the view by summing all the needed heights of its subviews and vertical constraints?
Here is a simple example...
The XIB file looks like this:
Note: the Bottom Label has a constraint of >= 8 to the bottom of the view. That way you don't have to worry about IB showing an error during design. Any extra space at the bottom will automatically "disappear" at run-time.
The Storyboard looks like this:
The scroll view has a red background (to make it easy to see). The labels and views containing buttons are laid-out and constraints are set as we normally set them.
The "Blue" view - TheXIB Container View - has a Height constraint of 120, but is set as a Placeholder that will be removed at run-time, allowing the view loaded from the XIB to control its height. This will be the view to which we add our Labels Holder View from the XIB.
Here's how it looks with a small amount of text:
It's not scrollable, because there isn't enough content.
After tapping the "Add" buttons a few times, it looks like this:
and it is scrollable (we can see it's scrolled down in this image).
For folks who prefer using a "content view" as the "root" view of a scroll view, it works the same (orange view is the content view, inset by 8-pts on each side):
Here is a link to this example project: https://github.com/DonMag/LoadXIBIntoScroll
And here's a gif animation of it in action:
I've added a scrollview to a view in my app, the view is selected from a tab controller and as such is a container and not a UIViewController it is declared as below
class HomeViewController: Container, UIScrollViewDelegate {..
I've added the scrollview in the storyboard and added all of the components to it and I've assigned the delegate from the storyboard and I've placed this code in my ViewController
scrollview.contentSize = CGSize(width:1048, height:scrollview.frame.height)
scrollview.delegate = self
This how my ScrollView looks in my storyboard, you can see that scrollview is my UIScrollView, then I've added a UIView viewScroll and then added four views to viewScroll (View1, View2, View3 and View4), scrollview is sized at 375x340 and viewScroll is set at 1048x340
When I run the app it does not scroll. As it is not a UIViewController, do I need to approach this differently or is there something else I have missed?
I've added the scrollview in the storyboard and set the width as 1050 and height as 330
You should set the width and height of scroll view to be at most as large as its containing view. What you need to set to 1050 and 330 is contentSize of the scroll view - scrollView is only scrollable when its contentSize is larger than its bounds.
If you're setting up your scroll view using Interface Builder, then you'll need to add constraints to its children's edges. For example, in your case you'll have to add leading, trailing, top, and bottom constraints to your View Scroll view. Select it and add following constraints in interface builder:
After that there will be something like this:
When you add edge constraints to UIScrollViews child views, you are hinting the scroll view about its content size. And if scroll view knows its content size and it is larger than scroll view's bounds, you'll have scroll working.
I have a UIVisualEffectView which will act as a "container" view. Have set autoLayout to the container view. I have another 3 UIViews each having separate frame size and elements, ( view 1 has a Label and image, view 2 has a tableView and a label, view 3 have few more uilabels ).
I want to add each of these uiview's to the container view based on some button action.
Am trying to add the subviews using addSubView at each of the button actions.
I have added the sub uiview's in the storyboard under the container view's hierarchy and currently my logic depends on hiding the irrelevant uiview's and showing the subview which is relevant.
Though I have tried adding the uiview's using addSubView to the container's view, it does not load the views. Have tried doing the same flow using separate .xib but for no effect.
The container view needs to resize based on the subview's height and also load the subView.
Am I doing something wrong or is there some other way to do these design.
I've tried to create custom UIView, which contains another UIView as subview. But when I loaded it with "loadNibNamed", I cannot change the frame size of subview inside the custom view.
Pictures below is how I added the view to the viewcontroller. I still can change some property of subview, but not for the "frame". How can I change the frame size in my custom view ?
Snapshot:
Disable autolayout in MyView.xib. Constraints from XIB file resize redView back.
Go to the File inspector in interface builder, and untick "Use Auto Layout".
I have just started learning objective-C and the iphone sdk and I have a question that I hope someone can help shed some light on.
What is the difference in the following:
self.view = someView;
and
[self.view addSubView: someView];
Say for example, in a simple app, where we have only one controller and one container view (has a few image subviews).
What is the difference between the two statements? The reason that I'm asking is because I was tinkering around in some sample code and I noticed the view was being initialized with images as subviews like so:
if (self = [super initWithFrame:CGRectZero])
{
//adds some images as subviews here
}
As I understand it the initWithFrame: CGRectZero, creates a frame with size at [0,0,0,0] (essentially invisible).
When I directly set the view with
self.view = someView;
I notice the view actually displays the image. But when I add the view to as a subview of controller's 'default' view, it doesn't. So basically my question is, whats going on behind the scenes? Why is the first method "resizing" the frame and the second one not doing the same thing.
What you see on the screen of your iPhone is almost always a hierarchy of views.
When you look at, say, your inbox in Mail, you're seeing a bunch of views. There's a big containing view.[1] Within that, there's a navigation bar view, a table view, and a toolbar view. Within the navigation bar view, there's a button view on each side and a label view in the middle. Inside the table view, there are a bunch of table cell views, and each of those cells has several label views. The toolbar has five button views. I could go further and talk about the views inside those buttons and so on, but I'm sure you get the idea.
The view above any given view is its superview; the views below it are its subviews. So a table cell view has a table view as its superview and a bunch of label views as its subviews. The top view, the one that has all the other views inside it, is called the root view.
Each view has its own drawing surface. The rectangle formed by that drawing surface is called the frame. The frame of a view is relative to the frame of its containing view. So if one of our table cell's label subviews has its frame at (0,0), that means it will be in the table cell's top left corner, even if the cell is halfway down the screen.
When you're writing a view controller, self.view is that root view I mentioned earlier; all the other views are subviews of that one (or subviews of its subviews, etc.). One of the features of a view controller is that it automatically resizes its self.view to fit the screen. (The available area will be smaller in the middle of a phone call: the status bar is twice as high then, so there's less space for your app. It will also be smaller if your view controller is being managed by a navigation controller or tab bar controller, but that's a different story.) But just because you resize its root view doesn't mean that the root view's subviews will automatically resize. To do that, you need to set their autoresizing mask (a property which tells the view how it should react when its superview changes size):
someView.autoresizingMask = UIViewAutoresizingFlexibleWidth
| UIViewAutoresizingFlexibleHeight;
(There's a graphical way to set up the autoresizing mask in Interface Builder—click the ruler icon in the inspector window and look at the "Autosizing" section.)
Even that's not enough, though, if someView isn't the right size to start with. To do that, adjust its frame before you add it as a subview of self.view:
someView.frame = CGRectMake(
0, // all the way to the left
0, // all the way at the top
self.view.frame.size.width, // same width as the root view
self.view.frame.size.height, // same height too
);
So why would you ever use subviews if you have to do all this twiddling that the root view does for you? Simple: you can only have one root view, but one view is almost never enough for what you need to do. If you really need only one view, of course, you can just set it as the root view and go on your merry way, but chances are, things are more complicated than that.
[1] I'm simplifying a bit here, but that's fine for right now.
When you add a view as a subview, you need to make sure that you're actually adding to an existing view.
self.view = view sets the controller's view. Without this (either in code or done with a XIB) you'll never see anything as the controller has no view to show.
[self.view addSubView: someView] assumes that self.view is already set. If it doesn't, you're adding someview as a subview of nil, and it will never get seen.
Basically, think of self.view as the big container, and all the subviews are just pieces inside of it. If you don't need any subviews, setting self.view to a UIImageView or UIWebView is fine. If you do need subviews, you'll need a big, empty container view in which to put them.
In your case, I'm betting self.view is never set, and you're adding your image views to nil.
Setting the view controller "view" property only changes the view it is managing.
Adding a view as a subview of another view, actually adds the subview underneath the other view.
They are very different things, as one adjusts a view controller and the other alters a view hierarchy.
As a guess, the reason you didn't see anything the first way was the frame for the subview you were adding was CGRectZero (0 in size).