问题
I am trying to create a custom keyboard programatically. I have managed to get the functionality of the keyboard working but am struggling with setting the constraints programatically.
I have managed to find code to constrain the buttons to be equal sizes in their rows but cannot customise it enough to set the width (or proportional width) of certain buttons.
Here is a picture of the keyboard currently

Each row is a separate UIView filled with UIButtons corresponding to each key. I then constrain the buttons inside each view to their superview.
The issue is that I want the space button to be a set proportion of the screen and also the 4th row buttons to be the same width (currently they are very slightly wider only having 9 buttons instead of 10 across)
Although it is written for swift I have adapted quite a bit of code from the Custom Keyboard tutorial: here
- (void)addConstraintsToView: (UIView *)view {
NSArray * buttons = view.subviews;
for (UIButton * button in buttons) {
NSInteger index = [view.subviews indexOfObject:button];
NSLayoutConstraint * topConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeTop multiplier:1.0f constant:1.0f];
NSLayoutConstraint * bottomConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeBottom multiplier:1.0f constant:-1.0f];
NSLayoutConstraint * leftConstraint;
// If the first button in the row
if (!index) {
leftConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeLeft multiplier:1.0f constant:1.0f];
}
else {
leftConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:buttons[index-1] attribute:NSLayoutAttributeRight multiplier:1.0f constant:1.0f];
NSLayoutConstraint * widthConstraint = [NSLayoutConstraint constraintWithItem:buttons[0] attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:button attribute:NSLayoutAttributeWidth multiplier:1.0f constant:0];
[view addConstraint:widthConstraint];
}
NSLayoutConstraint * rightConstraint;
// If the last button in the row
if (index == buttons.count - 1) {
rightConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeRight multiplier:1.0f constant:-1.0f];
}
else {
rightConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:buttons[index+1] attribute:NSLayoutAttributeLeft multiplier:1.0f constant:-1.0f];
}
[view addConstraints:@[topConstraint, bottomConstraint, rightConstraint, leftConstraint]];
}
}
This is the code currently constraining the buttons to be equal widths.
The main problem is that although there is lots of 'tutorials' on how to use NSlayoutConstraint they are mainly provided for a small number of views. They then use then names and set the constraints.
This one is probably the best, this one has the problem that I mentioned, each button is allocated and used as a local variable. This question is similar but subtly different and for a much smaller number of views.
For this as we are adding a much larger number of views then having each button as its own variable would be much too complex. Instead using a couple of if statements inside the loop would be much more elegant if I can figure it out.
What I really want it to be able to set certain buttons to either fixed widths or proportional widths. Eg the shift button and delete button to be 15% of the screen each or the shift and delete button to be fixed at 48 pixels.
Any help would be greatly appreciated
回答1:
Thanks to @rdelmar for helping me toward this solution
Although there is lots of information on NSLayoutConstraints online they take a bit of time to get used to. In this case I was over thinking the problem and needed someone to point out the obvious.
So in this case I am trying to change the widths of the buttons so all I need to worry about is the width constraint. The rest can be ignored.
The difficulty of this case adjusting the code gained from other sources to suit my need.
NSLayoutConstraint * widthConstraint;
if ([button.titleLabel.text isEqualToString:@"SHFT"] || [button.titleLabel.text isEqualToString:@"DEL"]) {
// Make sure to set the button it is in relation to - in this case we can set it with relation to any of this middle buttons so buttons[1] is fine
widthConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:buttons[1] attribute:NSLayoutAttributeWidth multiplier:1.5f constant:0];
}
else if ([button.titleLabel.text isEqualToString:@"space"]) {
widthConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:buttons[1] attribute:NSLayoutAttributeWidth multiplier:3.0f constant:0];
}
else {
widthConstraint = [NSLayoutConstraint constraintWithItem:buttons[1] attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:button attribute:NSLayoutAttributeWidth multiplier:1.0f constant:0];
}
[view addConstraint:widthConstraint];
As I am adjusting certain specific buttons I need to make sure I only change the constraints of them with the if statements. Then using the multiplier you can set their width as a multiplier of the other buttons in the view.
Hopefully this helps someone in the future even if it just the custom keyboard code translated from swift to objective c
NOTE: Remember to set the translatesAutoresizingMaskIntoConstraints for the view you are constraining. eg
view.translatesAutoresizingMaskIntoConstraints = NO;
If you leave this out then it won't work even if your constraints are all correct
来源:https://stackoverflow.com/questions/29598009/how-can-i-programatically-constrain-uibuttons-in-a-uiview-to-proportional-widths