NSLayoutContraint to position a button in the center of a view - ios

I am playing around with Constaints, they seems quite something :)
I would like to know, what will be the VisualString to position a button in the center (horizontally and vertically) of the view.
Should would I be using
NSLayoutContraint(item: attribute: relatedBy: toItem: attribute: multiplier: constant)

With Restraint you only need to do it:
let imageViewHorizontalConstraint = NSLayoutConstraint(
item: imageView,
attribute: .CenterX,
relatedBy: .Equal,
toItem: self,
attribute: .CenterX,
multiplier: 1.0,
constant: 0
)
let imageViewVerticalConstraint = NSLayoutConstraint(
item: imageView,
attribute: .CenterY,
relatedBy: .Equal,
toItem: self,
attribute: .CenterY,
multiplier: 1.0,
constant: 0
)
addConstraints([imageViewHorizontalConstraint, imageViewVerticalConstraint])
Without Visual Format
// Center horizontally
[self addConstraint:[NSLayoutConstraint constraintWithItem:centerView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:centerView
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0]];
Visual Format
// Variables
NSDictionary *views = #{
#"view" : self.view,
#"superview" : self.view.superview,
};
NSDictionary *metrics = #{
#"width" : #(CGRectGetWidth(self.view.frame)),
#"height" : #(CGRectGetHeight(self.view.frame))
};
// Constraints
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[superview]-(<=1)-[view(width)]"
options:NSLayoutFormatAlignAllCenterY
metrics:metrics
views:views];
[self.view.superview addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[superview]-(<=1)-[view(height)]"
options:NSLayoutFormatAlignAllCenterX
metrics:metrics
views:views];
[self.view.superview addConstraints:constraints];

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[titleLabel]-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(titleLabel)]];
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[titleLabel]-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(titleLabel)]];
Edit :- This one will work better
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[titleLabel]" options:NSLayoutFormatAlignAllCenterX metrics:nil views:NSDictionaryOfVariableBindings(titleLabel)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[titleLabel]" options:NSLayoutFormatAlignAllCenterY metrics:nil views:NSDictionaryOfVariableBindings(titleLabel)]];

Related

Make constraints for subviews programmatically

I need to create a UISlider and put it above an existing slider.
I do know how to create constraints for a view if I want to attach it to its superview:
UIView *superview = view.superview;
[view setValue: [NSNumber numberWithBool: FALSE] forKey: #"translatesAutoresizingMaskIntoConstraints"];
NSLayoutConstraint *topConstraint =[NSLayoutConstraint
constraintWithItem: view
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem: superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0.0];
NSLayoutConstraint *bottomConstraint =[NSLayoutConstraint
constraintWithItem: view
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem: superview
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0];
NSLayoutConstraint *leadingConstraint =[NSLayoutConstraint
constraintWithItem: view
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem: superview
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0.0];
NSLayoutConstraint *trailingConstraint =[NSLayoutConstraint
constraintWithItem: view
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem: superview
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:0.0];
NSArray *constraints = #[topConstraint, bottomConstraint, leadingConstraint, trailingConstraint];
[superview addConstraints: constraints];
But the same method does not work when I need to attach two subviews together. Say, I have view1 as subview of superview. It has been created some time ago. Now I need another one (view2) to have the same positioning inside superview.
Something like
UIView *superview = view1.superview;
UIView *view2 = [[UIView alloc] init];
[superview addSubview: view2];
[view2 setValue: [NSNumber numberWithBool: FALSE] forKey: #"translatesAutoresizingMaskIntoConstraints"];
NSLayoutConstraint *topConstraint =[NSLayoutConstraint
constraintWithItem: view1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem: view2
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0.0];
NSLayoutConstraint *bottomConstraint =[NSLayoutConstraint
constraintWithItem: view1
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem: view2
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0];
NSLayoutConstraint *leadingConstraint =[NSLayoutConstraint
constraintWithItem: view1
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem: view2
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0.0];
NSLayoutConstraint *trailingConstraint =[NSLayoutConstraint
constraintWithItem: view1
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem: view2
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:0.0];
NSArray *constraints = #[topConstraint, bottomConstraint, leadingConstraint, trailingConstraint];
[superview addConstraints: constraints];
breaks everything.
Looks like you just have it backward...
You want to constrain view2 to view1:
[NSLayoutConstraint
constraintWithItem: view2 // <-- constrain this view
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem: view1 // <-- to this view
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0.0];

UIPickerView inside prototype tableviewcell has no selection indicator

I have a table view controller that has multiple prototype cells for different input controls like UISwitch, UITextField, and UIPickerView, when the view is loaded I determine which control is needed and only present that one prototype cell.
However for the cell with the UIPickerView I cannot get the selection indicator to appear and the pickerview ends up looking like this:
In my tableView:cellForRowAtIndexPath: method I explicitly enable the selection indicator:
UIPickerView * pickerView = [cell viewWithTag:InputTagPicker];
pickerView.dataSource = self;
pickerView.delegate = self;
[pickerView selectRow:self.pickerInitialIndex inComponent:0 animated:YES];
[pickerView setShowsSelectionIndicator:YES];
Why is the selection indicator not showing up?
I had a similar problem. I noticed that every time I would swipe to a different tab, this problem would go away. This meant that something funny was happening as the tableview(cell) was appearing into view.
Since UITableViewCell doesn't have a viewWillAppear() method, I managed to solve this problem by force removing the pickerView from the cell's list of subviews and then adding it back. This simulates the process of setting the selection before the pickerView is added as a subview to the cell, and fixes the issue.
Here is what you can try:
UIPickerView * pickerView = [cell viewWithTag:InputTagPicker];
pickerView.dataSource = self;
pickerView.delegate = self;
[pickerView selectRow:self.pickerInitialIndex inComponent:0 animated:YES];
[pickerView setShowsSelectionIndicator:YES];
// hack to make sure the selection indicator shows
// first remove the picker
[pickerView removeFromSuperview];
// then add it back
[cell.contentView addSubview:pickerView];
However, this would create the picker that is stuck to the left edge of the cell, so you need to programmatically set the constraints
// finally set the constraints to make sure it fits horizontally centered
//Trailing
NSLayoutConstraint *trailing =[NSLayoutConstraint
constraintWithItem:pickerView
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:cell.contentView
attribute:NSLayoutAttributeTrailing
multiplier:1.0f
constant:0.f];
//Leading
NSLayoutConstraint *leading = [NSLayoutConstraint
constraintWithItem:pickerView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:cell.contentView
attribute:NSLayoutAttributeLeading
multiplier:1.0f
constant:0.f];
//Center Y
NSLayoutConstraint *centerY = [NSLayoutConstraint
constraintWithItem:pickerView
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:cell.contentView
attribute:NSLayoutAttributeCenterY
multiplier:1.0f
constant:0.f];
[cell.contentView addConstraints:trailing];
[cell.contentView addConstraints:leading];
[cell.contentView addConstraints:centerY];
Here is a Swift 4 version:
// first remove the picker
pickerView?.removeFromSuperview()
// then add it back
cell.contentView.addSubview(pickerView!)
// and finally set the constraints to make sure it fits horizontally centered
cell.contentView.addConstraints([NSLayoutConstraint.init(item: pickerView as Any, attribute: .centerY, relatedBy: .equal, toItem: cell.contentView, attribute: .centerY, multiplier: 1, constant: 0),
NSLayoutConstraint.init(item: pickerView as Any, attribute: .trailing, relatedBy: .equal, toItem: cell.contentView, attribute: .trailing, multiplier: 1, constant: 0),
NSLayoutConstraint.init(item: pickerView as Any, attribute: .leading, relatedBy: .equal, toItem: cell.contentView, attribute: .leading, multiplier: 1, constant: 0)])

IOS 8 Keyboard Height And Key Effects

I want to change the height of keyboard in XCode 6 Beta 5. I searched code and found that by using NSLayoutConstraint, we can change the height of it but not work for me.
This is my code:
CGFloat _expandedHeight = 500;
NSLayoutConstraint *_heightConstraint =
[NSLayoutConstraint constraintWithItem: self.view
attribute: NSLayoutAttributeHeight
relatedBy: NSLayoutRelationEqual
toItem: nil
attribute: NSLayoutAttributeNotAnAttribute
multiplier: 0.0
constant: _expandedHeight];
[self.view addConstraint: _heightConstraint];
In order for this to work all the views that are added to the UIInputViewController's view need to use layout constraints so you can't add any subviews that use UIViewAutoresizing masks. If you want to use UIViewAutoresizing just add a subview like below then add all of your other views to that view.
UIView *mainView = [[UIView alloc] initWithFrame:self.view.bounds];
[mainView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:mainView];
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:mainView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:mainView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0];
[self.view addConstraints:#[widthConstraint, heightConstraint]];

NSLayoutConstraint errors with resizing UITableViewCell

I have a custom UITableViewCell with a subview that resizes using Auto Layout. This subview is a toolbar at the bottom of the cell. It has a height of zero when the cell is unselected and grows to 30 in the selected state. The cell toggles between 100 and 130.
Here is the init:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
_toolbarView = [[FOToolbarView alloc] init];
_toolbarView.translatesAutoresizingMaskIntoConstraints = NO;
_toolbarView.backgroundColor = [UIColor blueColor];
[self.contentView addSubview: _toolbarView];
[self setNeedsUpdateConstraints];
}
return self;
}
And here are the constraints
- (void)updateConstraints
{
[self.contentView addConstraint: [NSLayoutConstraint constraintWithItem: _toolbarView
attribute: NSLayoutAttributeWidth
relatedBy: NSLayoutRelationEqual
toItem: self.contentView
attribute: NSLayoutAttributeWidth
multiplier: 1.0
constant: 0]];
[self.contentView addConstraint: [NSLayoutConstraint constraintWithItem: _toolbarView
attribute: NSLayoutAttributeLeft
relatedBy: NSLayoutRelationEqual
toItem: self.contentView
attribute: NSLayoutAttributeLeft
multiplier: 1.0
constant: 0]];
[self.contentView addConstraint: [NSLayoutConstraint constraintWithItem: _toolbarView
attribute: NSLayoutAttributeTop
relatedBy: NSLayoutRelationEqual
toItem: self.contentView
attribute: NSLayoutAttributeTop
multiplier: 0.0
constant: 100.0]];
[self.contentView addConstraint: [NSLayoutConstraint constraintWithItem: _toolbarView
attribute: NSLayoutAttributeBottom
relatedBy: NSLayoutRelationEqual
toItem: self.contentView
attribute: NSLayoutAttributeBottom
multiplier: 1.0
constant: 0]];
[super updateConstraints];
}
The layout works as intended, but I am getting the following error:
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSLayoutConstraint:0x1f810f50 FOToolbarView:0x1f812900.top == + 70>",
"<NSLayoutConstraint:0x1f810dc0 FOToolbarView:0x1f812900.bottom == UITableViewCellContentView:0x1f8286d0.bottom>",
"<NSAutoresizingMaskLayoutConstraint:0x1f821230 h=--& v=--& V: [UITableViewCellContentView:0x1f8286d0(69)]>"
)
I've tried many things, without success. How can I get rid of this error?
Problem seems to be that autoresizingmask constraints from tableviewcell.contentview is being automatically added by the system. Try this:
self.contentView.translatesAutoresizingMaskIntoConstraints = NO;

Animating an UIImageView (View) from Point A to Point B with constraints programmatically

I have an image view that I have lined up on the center right of my screen. All is fine when adding the image view by itself with constraints. I need to animate the image view from that original position(Point A) to the bottom portion(Point B) of the screen; the problem is when I try to animate from Point A to Point B, the image view starts off in the upper left hand corner of the screen, like it was simply added with no constraints (as opposed to where I want it starting off in the center) .
Here is the code and some comments:
#define DEGREES_TO_RADIANS(x) (M_PI * x / 180.0) //defined above
- (void)viewDidLoad
{
UIImage *cardFaceDownImage = [UIImage imageNamed:#"b-top#2x.png"];
UIImageView *dealCardDown = [[UIImageView alloc] initWithImage:cardFaceDownImage];
dealCardDown.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *dealCardConstraintY = [NSLayoutConstraint constraintWithItem:dealCardDown attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1 constant:1];
NSLayoutConstraint *dealCardConstraintX = [NSLayoutConstraint constraintWithItem:dealCardDown attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1 constant:130];
[self.view addConstraint:dealCardConstraintY];
[self.view addConstraint:dealCardConstraintX];
[self.view addSubview:dealCardDown];
//IF I comment out the below code the UIImageView lines up where I want it (Point A)
//IF I don't coment it out, the imageview starts in the upper left hand corner of the screen
//where it normally would as if just adding an image view programatically without
//constraints would, but it does end up where I want it to (Point B)
[self.view removeConstraint:dealCardConstraintX];
[self.view removeConstraint:dealCardConstraintY];
dealCardConstraintY = [NSLayoutConstraint constraintWithItem:dealCardDown attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1 constant:1];
dealCardConstraintX = [NSLayoutConstraint constraintWithItem:dealCardDown attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1 constant:130];
[self.view addConstraint:dealCardConstraintY];
[self.view addConstraint:dealCardConstraintX];
[self.view setNeedsUpdateConstraints];
[UIView animateWithDuration:2.0 delay:2.0 options:UIViewAnimationCurveEaseIn animations:^(void)
{
dealCardDown.transform = CGAffineTransformRotate(dealCardDown.transform, DEGREES_TO_RADIANS(90));
[self.view layoutIfNeeded];
}
completion:^(BOOL finished)
{
}
];
}
..
You need to move change of constraint into animation block, so it will be animated properly.
This code works for me well.
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
UIView *dealCardDown = [[UIView alloc] initWithFrame:(CGRect){0,0,50,50}];
dealCardDown.backgroundColor = [UIColor redColor];
dealCardDown.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *sizeConstraint = [NSLayoutConstraint constraintWithItem: dealCardDown attribute: NSLayoutAttributeWidth relatedBy: NSLayoutRelationEqual toItem: nil attribute:NSLayoutAttributeNotAnAttribute multiplier: 1 constant: 50];
[dealCardDown addConstraint:sizeConstraint];
sizeConstraint = [NSLayoutConstraint constraintWithItem: dealCardDown attribute: NSLayoutAttributeHeight relatedBy: NSLayoutRelationEqual toItem: nil attribute:NSLayoutAttributeNotAnAttribute multiplier: 1 constant: 50];
[dealCardDown addConstraint:sizeConstraint];
[self.view addSubview:dealCardDown];
NSLayoutConstraint *dealCardConstraintX = [NSLayoutConstraint constraintWithItem:dealCardDown attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.f constant:0.f];
NSLayoutConstraint *dealCardConstraintY = [NSLayoutConstraint constraintWithItem:dealCardDown attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.f constant:-self.view.center.y+dealCardDown.frame.size.height/2];
[self.view addConstraint:dealCardConstraintX];
[self.view addConstraint:dealCardConstraintY];
[self.view layoutIfNeeded];
[UIView animateWithDuration:2.0 delay:0.0 options:UIViewAnimationCurveEaseIn animations:^(void)
{
dealCardDown.transform = CGAffineTransformRotate(dealCardDown.transform, 90*M_PI/180);
dealCardConstraintY.constant = 0;
[self.view layoutIfNeeded];
}
completion:^(BOOL finished)
{
}
];
}

Resources