I am confused with the autolayout on xib/nib. I am comfortable with storyboard autolayout.
What I am trying to do is:
1. I have a storyboard with one view controller with one UIView called borderView. It is constrained correctly.It is resized correctly for iphone 5 and 6. Here is the screenshot of the storyboard:
This is the borderView in iphone 5: (I am trying to add the nib view as a subview to this border view)
2. I created a nib with UIView in it with the dimensions 300 x 300. I want this view to be added to the borderVIew in my ViewController. HEre is the screenshot for my nib.
Note: I didnt gave any height or width constraints anywhere. I just gave leading ,traliing,top and bottom.
and I am trying to add the nib to my borderview as follows:
This is the method in my viewController:
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
[[NSBundle mainBundle] loadNibNamed:#"AlertView" owner:self options:nil];
[self.borderView addSubview:self.myAlertViewFromNib];
}
and the result in the iphone 5 is:
As you can see, the nib view is not aligned in the center of the screen (as the border view is aligned center to the screen).
I have no idea of how to give constraints to nib itself. Can anyone please tell me if it is possible to do it in xcode or do I need to give the constraints programmatically?
You want to add the AlertView as a center aligned in the view controller
Rather than setting size from the xib just create the perfect constrained XIB view and define macros for sizes as below.
Step 1 have some macros for size
#define kAlertViewWidth 300
#define kAlertViewHeight 300
Step 2 in #interface .Please create on property like this in the viewcontroller
#property (nonatomic, retain) IBOutlet UIView *myViewFromNib;
Getter Method For the property
-(UIView *)myViewFromNib
{
if (!_myViewFromNib) {
_myViewFromNib = [[[NSBundle mainBundle] loadNibNamed:#"AlertView" owner:self options:nil] objectAtIndex:0];
_myViewFromNib.translatesAutoresizingMaskIntoConstraints = NO;
}
return _myViewFromNib;
}
//Step 3 in viewDidLoad method
-(void)viewDidLoad{
[self setUpConstraints];
}
Method which will add the required constraints
-(void)setUpConstraints
{
\[self.view addSubview:self.myViewFromNib\];
\[NSLayoutConstraint activateConstraints:#\[\[NSLayoutConstraint constraintWithItem:self.myViewFromNib attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0\],
\[NSLayoutConstraint constraintWithItem:self.myViewFromNib attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0\],
\[NSLayoutConstraint constraintWithItem:self.myViewFromNib attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0
constant:kAlertViewWidth\],
\[NSLayoutConstraint constraintWithItem:self.myViewFromNib attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0
constant:kAlertViewHeight\]\]\];
}
If you have no choice but to do it this way try setting the centre manually. i.e. yourView.frame.x = super.frame.size.width/2 - yourView.frame.size.width/2. This should centre it horizontally. If it won't let you, then you'd have to add in the constraints after adding in to subview.
Instead of a xib and a storyboard, use a storyboard and inside your borderview add another view (with everything you have in the xib) and constrain that view within your borderview.
Use the vertically center and horizontally center constraints to center the inner view. If you need to change the constraints later, create an outlet for them in your header file and adjust the priority.
If you still want to use a xib within a view, try using a third party library like Masonry to adjust your xib.
Related
My goal:
I'm trying to find a general recipe how to add views that I created in Interface Builder to a parent view that I create in Interface Builder as well, while letting their layout constraints determine the size of their respective parent views.
The key idea is to get cascading views with a variable size. A view should first determine its own size by resolving its layout constraints. Once it's done it has a fixed size from its superview's perspective so now the superview can determine its size by resolving its own layout constraints and so on.
My approach:
I create a new XIB file in Xcode and open it in Interface Builder. Next, I drag another UIView from the Object library to the XIB's view in order to add it as a subview. Using layout constraints I pin all four sides of the subview to its superview's edges:
Now I create another XIB file which is supposed to be the subview. I drag a UILabel and a UIButton to its view. I give the button a fixed height constraint and add more constraints to pin the label and the button to the view's edges:
Now comes the tricky part that I haven't been able to solve yet:
I couldn't find any way to add the view of this second XIB file to the subView of the first XIB using only Interface Builder (is this even possible?) so I tried it by writing some code. I created a UIView custom class and set the class value in Interface Builder accordingly. In the custom class I added this method:
- (void)awakeFromNib {
UIView *view = [[[NSBundle mainBundle] loadNibNamed:#"DynamicSubView" owner:self options:nil] objectAtIndex:0];
[self.subView addSubview:view];
view.translatesAutoresizingMaskIntoConstraints = NO;
[self addConstraintsForView:view];
}
- (void)addConstraintsForView:(UIView *)view {
[self addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.subView attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.subView attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.subView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.subView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0]];
}
The method addLayoutConstraintsForView: adds constraints to the view from the second XIB that pins all its four edges to the subView's edges. Now the subView should resize dynamically with the view it contains. However, it doesn't seem to work. Any idea or a good recipe how to deal with this?
Remark: The top most view here is intended to be a UITableViewCell. I want it to layout itself and pass its height to its tableView using the method introduced here.
My task is very simple - just display vertically scrolling text inside custom UIView.
I have an UIScrollView with some container with one UITextView in it. Container is needed to add more items later. I use IB(xib file) to add autolayout constraints like shown below:
But everything I see is:
And it's scrolling horizontally :-(
I've tried setting contentSize of UIScrollView:
At initWithCoder, didMoveToSuperview, willMoveToSuperview = no effect
At custom method, called from main viewcontroller = no scrolling at all, and I see the same picture.
Thanks a lot!
Edit: Text View's and Container View's intristic size are set to "placeholder". I understand that I should limit width of ContainerView, but it should work with all screen sizes and orientations, so setting width in code, IMO, worse than setting constraints.
Yeah! I've found the solution!
If someone facing the same problem:
You should add left and right constraints not to UIScrollView, but to superview of UIScrollView (to super-super-view).
Unfortunately, this cannot be done in IB, but you can do it within code:
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.view
attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual
toItem:self.containerView attribute:NSLayoutAttributeLeft
multiplier:1.0 constant:-30]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.view
attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual
toItem:self.containerView attribute:NSLayoutAttributeRight
multiplier:1.0 constant:30]];
Where self.containerView is view inside UIScrolllView and self.view is view outside UIScrollView
So:
You set vertical constraints to UIScrollView and horizontal constraints to top-level view.
I have a UIView defined in a .xib file. I need to set translatesAutoresizingMaskIntoConstraints = NO. This means that the frame is not translated to constraints so I need to set the size constraints by myself.
I have created a working category method for a UIView:
-(NSArray*)setSizeConstraints:(CGSize)size
{
NSLayoutConstraint* height = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:0 toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:size.height];
NSLayoutConstraint* width = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:0 toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:size.width];
[self addConstraint:height];
[self addConstraint:width];
return [NSArray arrayWithObjects:height, width, nil];
}
But I would like to set these constraints from the Xcode interface builder, but all my AutoLayout controls are greyed out:
Is there a way to do this in the interface builder?
This actually is possible.
Simply put the view you'd like to constrain into another view, like the highlighted view here.
Then, add your desired constraints.
Finally, pull the view you just added constraints to out of its parent view. You can now add constraints to that view.
As you've surely found out by now, it looks like there's currently no way to set Autolayout constraints at the main UIView level from Interface Builder.
I had a similar problem here (Launch Screen XIB: Missing Width / Height Constraints (Xcode 6)), while working with the launch screen of my app.
The only workarounds I found, if you want to keep working with IB, are:
Using a UIViewController and its UIView inside the XIB, instead of just a UIView
Working only with Storyboards (and, once again, UIViewController)
However, I understand these approaches may not be ideal or "clean", especially if you simply have a subclass of UIView and you want to draw its interface in a good old XIB.
A pretty simple question I reckon:
one UIViewController
one custom UIView
The controller only does:
-(void)loadView{
[super loadView];
self.sideMenu = [[sideMenuView alloc]init];
[self.view addSubview:self.sideMenu];
}
and in the UIView I would like to do something like:
self.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.superview attribute:NSLayoutAttributeLeading multiplier:1 constant:100];
[self.superview addConstraint:constraint];
So that when I create the UIView in the controller its constraints is already set in relation to the controller.
I have tried and nothing crashes but the UIView gets realy weird x and y coords
Maby I need to update the constraints? Or maby this isnt at all possible?
I'm not sure what ui behavior you are exactly looking for since it appears that you are trying to tie the leading space of your view to the leading space of it's superview. Being the leading space, the space on the left of the view, could it be that you are looking for the more common "stick my left side 100 pixels from my parents left border"? Anyway, in either case, I would connect an outlet from the controller to the custom view (i.e. myCustomView below) and then build the constraint in the UIViewController and not the UIView by overriding:
- (void)updateViewConstraints {
[super updateViewConstraints];
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:myCustomView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:myCustomView.superview
attribute:NSLayoutAttributeLeading
multiplier:1
constant:100];
[myCustomView addConstraint:constraint];
}
Apple has an interesting page with a table showing the various runtime entry points for autolayout at this address:
https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/Articles/runtime.html#//apple_ref/doc/uid/TP40010853-CH6-SW1
You might consider adding other constraints as well. Auto layout has the the tendency to exploit any freedom you leave unchecked in the worst possible way ;-)
So leading edge is not enough.
You need enough constraints to satisfy vertical and horizontal layout.
In one direction you need at least
one edge & width (or hight)
Or
Two edges ( implicit width or height )
Or
A horizontal (or vertical) center based constraint and an explicit width ( or height respectively)
The thing about width and height is that they can also be determined by intrinsic content size.
Add constraints after adding the view to the superview.
A bit late but PureLayout is pretty handy https://github.com/smileyborg/PureLayout
In my app I want to achieve this layout:
So parent view contains two sub views. First one ends exactly in a middle (height / 2) and second starts in a middle of parent view. I have found out that it is impossible to do that in the IB with constraints. So I used this code in viewDidLoad method:
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:firstView
attribute:NSLayoutAttributeHeight
relatedBy:0
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0];
[self.view addConstraint:constraint];
Now it works but only if the app runs on the iPhone. Because size of the view is like iPhone screen. If this app runs on the iPad, there is a problem because screen has different size so this parent view is longer. And constraint (code above) still takes 0.5 * size of the views size from the IB and not size from the iPad size of the view. Item toItem:self.view still takes size from the IB.
Result is that this view has a same size in the iPad as in the iPhone. In the iPad there is a large blank space and then there is a view with iPhone size.
Can you tell what I have to do to make it universal for various screen sizes? Thank you very much
This is possible using constraints, but it is made a bit fiddly by IBs rather annoying and inflexible constraint manager. Here is how I managed it:
In IB, set the two views with the correct frames
Add an equal height constraint between the two views
Reduce the priority of any default height constraints on either of the views. Unfortunately IB does not let you remove these entirely, but setting them to anything less than 1000 will make sure they are ignored.
In the view controllers viewDidLoad method, add the constraint you already tried.
eg
- (void)viewDidLoad
{
[super viewDidLoad];
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self.topView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0];
[self.view addConstraint:constraint];
}
Thats it. Screengrabs of the IB constraints are shown below:
Try this code . It will set constraint value dynamically
In your .h file , implement this lines.
#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#define IS_IPHONE_5 ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *TopsubviewheightConstraint;
Now create this constraint's as per given screen shot
connect TopsubviewheightConstraint height constraint from screen
implement this code in .m file
if (IS_IPHONE_5)
_TopSuperViewConstraint.constant = 275;
else if(IS_IPAD)
_TopSuperViewConstraint.constant = 502;
else
_TopSuperViewConstraint.constant = 230;
I hope it will help you.
you have 2 options.
create a second IB file for iPad
do everything by programm and use [[UIScreen mainScreen] bound]; instead of getting the sizes of parent ;)
I would do it without the constraints at all and set as follow:
// self.view is my container view
CGRect frame = [[UIScreen mainScreen] bound];
frame.size.height /= 2;
// upper View
upperView.frame = frame;
// lower View
frame.origin.y = frame.size.height;
// or alternatively
//frame.origin.y = CGRectGetMaxY(frame);
lowerView.frame = frame;
here you don't need any device specific options, everything is dynamic, bound to the size of your device's screen ;)
OK so I just figured out how to do this. Simply put the code into viewDidLayoutSubviews method and not to viewDidLoad. The solution I found in the topic Unable to set frame correctly before viewDidAppear.
Here is my code:
[subView1 setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height / 2)];
[subView2 setFrame:CGRectMake(0, self.view.frame.size.height / 2, self.view.frame.size.width, self.view.frame.size.height / 2)];
Thanks to all for effort!
Thanks to Tark's answer, I managed to to this using constraints as well:
Add Vertical Space constraint for TobView to Top Layout Guide (Using StoryBoard)
Add Vertical Space constraint for BottomView to Bottom Layout Guide (Using StoryBoard)
Add two height constraints for each view in ViewDidLoad
Code:
NSLayoutConstraint *constraint;
constraint = [NSLayoutConstraint constraintWithItem:_viewTop
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0];
[self.view addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:_viewBottom
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0];
[self.view addConstraint:constraint];
You can do this with constrains (no code required!)
1.- First create two UIview and manually set it's height to half of the size of the current device, positioning one over the other, just like this:
2.- Next you must set the constraints for each one of them like this (this will allow to the container fill the whole screen, one over the another):
Top container
Bottom container
3.- Finally you must select both containers and add a new constrain that specify that they will have in the same height
(remember to clid "Add X Constrains" for each step)
now it should be ready to put the label inside each container, and you will ready