I have created a UIView using the following code within viewDidLoad (where 'secondview' obviously is the name of the UIView):
secondview = [[UIView alloc] initWithFrame:self.view.frame];
[secondview setBackgroundColor: [UIColor yellowColor]];
secondview.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:secondview];
Then within viewDidAppear I added constraints to this view:
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:secondview attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeRight
multiplier:1.0f constant:-20.0f];
[self.view addConstraint:constraint];
However, the constraints are not applied to the view (atleast not that I can see). Instead, the view simply seems to disappear from the screen. If the constraint code is commented out however, the view once again loads with the appropriate frame (obviously without the constraints being applied). When applying the same constraints to a Button or ImageView, the constraints are applied perfectly. This has lead me to think that the issue is because of 'initWithFrame' when creating the View, as neither the button nor ImageView actually require it's size to be specified.
What are your thoughts? What should I do differently?
For anyone who comes across this... I needed to add more than one constraint. That did the trick.
Related
I have a custom UIView subclass called CustomRectangle. I instantiate this in a ViewController and create all of its constraints in a ViewController. My goal is to create all the constraints inside this UIView subclass programmatically. The problem is that I don't know how to set the constraints there because I don't have reference to any of the other views in the Storyboard.
For example, if I want my view CustomRectangle to be centered based on another view, I would create an #IBOutlet in the ViewController for the other view and then use that to center CustomRectangle. I don't know if this is possible to do in a UIView subclass.
I want to do this based on MVC (Model View Controller) architecture.
What is the best practice for this? Any ideas on how to do this?
you should use initWithFrame method of CustomRectangle to do this.
for emample in UIView's Subclass,
- (id)initWithFrame:(CGRect)frame{
self = [super initWithFrame: frame];
if (self) {
[self setFrame:CGRectMake(0, 0, 50, 50)];
//add constraint here for example
[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute: NSLayoutAttributeNotAnAttribute multiplier:1 constant:50]];
}
return self;
}
Convert in swift this is objective c code!!
Hope this will help :)
There are a few things you should do:
Remember to set translatesAutoresizingMaskIntoConstraints = NO for the constraints to work. However, if you think views autolayout way, inside your view you should use autolayout for your subviews, and the view that instantiates your view should add constraints to customRectangle (a.k.a widht, alignment, etc)
In the init method, you can just add constraints that only depend on your view. Other constraints should be added from outside.
For Example:
CustomRectangle *customRectangle = [[CustomRectangle *customRectangle alloc] init];
// just to be sure
customRectangle.translatesAutoresizingMaskIntoConstraints = NO;
// just as an example
[self.view addConstraints[NSLayoutConstraint constraintWithItem:customRectangle attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0 constant:0]];
Hope it helps!!
I have 2 views that I have taken in storyboard but I haven't set any layouts in the storyboard.I want to set them by code.So here is what I did:
I read about autoResizingMask property of a view and 'translatesAutoResizingMaskIntoConstraints'.So setting 'translatesAutoResizingMaskIntoConstraints' to 'NO' makes sense.So I have done that but still I' not able to set the constraints programmatically.Now,as some constraints are automatically set in storyboard when we take the views from storyboard,I tried:
[self.view removeConstraints:[self.view constraints]];
After doing this,it removes all default constraints from the superview(i.e. self.view).But still I am not able to set constraints from code.What should I do?I need some guidance.
Here is my code that I have been using:
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
NSLayoutConstraint *layouts1 = [NSLayoutConstraint constraintWithItem:_redView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:_yellowView attribute:NSLayoutAttributeWidth multiplier:3.0f constant:100.0f];
[self.view addConstraint:layouts1];
Here is my problem, I have a scroll view scrollExerciseIndex that I use only as a scrolling bar, in this scroll view I place a UIView indexesView and I want it to be always at the center of the scroll view. For this I use layout constraints :
UIView * indexesView = [[UIView alloc] initWithFrame: CGRectMake(xPosition, 0, dimension*numberIndexes, dimension)];
[self.scrollExerciseIndex addSubview:indexesView];
[self.scrollExerciseIndex setContentSize:CGSizeMake(dimension*numberIndexes, dimension)];
if (xPosition != 0) {
NSLayoutConstraint * xCenterConstraint = [NSLayoutConstraint constraintWithItem:indexesView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.scrollExerciseIndex attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0];
[self.scrollExerciseIndex addConstraint:xCenterConstraint];
}
Here is the expected result :
Don't pay attention to all the element, just the bar at the bottom of the screen is my problem.
I have to create view programmatically because sometimes I will activate the constraints, sometimes not and I have to set the frame of the view dynamically. So for now I initialise the view indexesView like so :
UIView * indexesView = [[UIView alloc] initWithFrame: CGRectMake(xPosition, 0, dimension*numberIndexes, dimension)];
(I know, not very original)
I would like to know if there is a way to initialize the view programmatically but to say to auto-layout that it has no constraints on the position because right now if the screen turns in landscape mode there is a conflict as the scrollview's frame changes so the distance between the center of the scroll view (on which I set a constraint) and the position of the subview's frame (xPosition) is no longer the same.
As you can see, the view is no longer at the center of the scroll view and I have some constraints broken.
Will attempt to recover by breaking constraint
NSLayoutConstraint:0x7bed6c50 UIView:0x7bed6ad0.centerX == UIScrollView:0x7e273200.centerX
Thanks for your help.
Ok, I found what I was looking for by reading a book about Audio-Layout.
My problem was that audio layout would create constraints behind my back automatically. When using AutoLayout a type of constraints is created from non-autoLayout specifications (The used to describe interface when auto layout didn't exist). So constraints are created using the initial frame of the view. The only thing I had to do was :
[indexesView setTranslatesAutoresizingMaskIntoConstraints:NO];
to disable this creation of constraints from the frame, and then recreate explicitly the constraints for width and height if needed (which wasn't the case for me, but I still made the test) like so :
`NSLayoutConstraint * widthConstraint = [NSLayoutConstraint constraintWithItem:indexesView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:0 multiplier:1.0 constant:widthValue];
NSLayoutConstraint * heightConstraint = [NSLayoutConstraint constraintWithItem:indexesView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:0 multiplier:1.0 constant:heightValue];
[indexesView addConstraint: heightConstraint];
[indexesView addConstraint: widthConstraint];`
When adding constraints programmatically, don't forget to call : [indexesView setNeedsUpdateConstraints]; so the constraints are recalculated only when needed.
Last info that I read and can be useful in general, when adding a lot of constraints, the apple doc specifies that it is more efficient to use the method :
[myView addConstraints:(NSArray<NSLayoutConstraints *> *)] than to call addConstraint: for each constraint.
Hope it can be useful to someone.
I have an iOS view with autolayout enabled and have a UIToolbar with a UISearchBar and UISegmentControl contained with the toolbar. I want the UISearchBar to have a flexible width so I need to add a constraint to force this, but from what I can tell you cannot add constraints to items in a UIToolbar in Interface Builder. The options are all disabled.
Before AutoLayout I would accomplish this with autoresizingmasks.
Are constraints not allowed within UIToolbars/UINavigationBars?
How else can this be accomplished when using autolayout?
Autolayout constraints only work with UIViews and their subclasses.
While UIToolbar allows some UIView based items (such as UISearchBar and UISegmentedControl) they may have to coexist with UIBarButtonItems which do not inherit from UIView.
Until autolayout can work with UIBarButtonItems, do as you have done.
Your alternative is to roll your own toolbar with widgets based only on UIViews.
This can also be done right from a storyboard.
Just drag and drop items in the toolbar, and turn some of them into flexible or fixed space to get the desired effect. See the two examples below.
NB: this is a copy of my answer to Aligning UIToolBar items, I stumbbled upon both questions while looking for such a solution
You can do this in code, at least; I'm the type to forsake Interface Builder and go it in code anyway. IB seems to get in my way more often than not when it comes to adding or tweaking constraints. Here's what I've done in my custom UIToolbar subclass's -initWithFrame: method.
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self addSubview:self.label];
[self addConstraint:[NSLayoutConstraint
constraintWithItem:self.label
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterX
multiplier:1 constant:0]];
[self addConstraint:[NSLayoutConstraint
constraintWithItem:self.label
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterY
multiplier:1 constant:0]];
}
return self;
}
And since I like to lazy load as much as possible, here's my self.label instance variable (called when [self addSubview:self.label] gets messaged above).
- (UILabel *)label {
if (_label) return _label;
_label = [UILabel new];
_label.translatesAutoresizingMaskIntoConstraints = NO;
_label.textAlignment = NSTextAlignmentCenter;
return _label;
}
Seems to work for me. I'm not adding any UIBarButtonItems, though, so your mileage my vary.
What is the closest ancestor to self.navigationController.navigationBar and a subview of self.view in a view controller so I can add the constraint to that ancestor?
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectZero];
[self.searchBar setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:self.searchBar];
NSLayoutConstraint* cn = [NSLayoutConstraint constraintWithItem:self.searchBar
attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
toItem:self.navigationController.navigationBar attribute:NSLayoutAttributeBottom
multiplier:1.0 constant:0];
I tried doing
[self.view addConstraint:cn];
[self.navigationController.view addConstraint:cn];
but both times I get the error, "Does the constraint reference something from outside the subtree of the view? That's illegal."
Can someone explain the view hierarchy of a view controller in relation to the navigationController?
Thanks!
The view tree looks like this:
So, navigationController.view is the closest ancestor of the navigation bar and your view. I'm not sure why that didn't work. Where did you put that code?