Hobbes the Tige
Hobbes the Tige

Reputation: 3821

Using Autolayout to position UITextField next to UILabel

I would like to use auto layout to position a UITextField next to cell.textLabel when a UITableView goes into edit mode. The code that I have works correctly but I get a message in the log that says some existing constraints had to be broken.

UILabel *label = cell.textLabel;
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(0.0, 0.0, 400.0, 22.0)];
textField.placeholder = cell.textLabel.text;
textField.translatesAutoresizingMaskIntoConstraints = NO;
textField.text = text;
[cell.contentView addSubview:textField];
[cell addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[label]-[textField]-|"
                                                             options:0
                                                             metrics:nil
                                                               views:NSDictionaryOfVariableBindings(label,textField)]];
[cell addConstraint:[NSLayoutConstraint constraintWithItem:textField attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:label attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0]];

Log message is:

(
"<NSAutoresizingMaskLayoutConstraint:0x1066abc0 h=--& v=--& H:[UITableViewCellContentView:0x9a7d070(934)]>",
"<NSAutoresizingMaskLayoutConstraint:0x10660a90 h=--& v=--& H:[UILabel:0x9a93ef0(914)]>",
"<NSLayoutConstraint:0x1065ea60 H:[UITextField:0x1065c660]-(NSSpace(20))-|   (Names: '|':UITableViewCellContentView:0x9a7d070 )>",
"<NSAutoresizingMaskLayoutConstraint:0x10660a50 h=--& v=--& UILabel:0x9a93ef0.midX == + 467>",
"<NSLayoutConstraint:0x10669280 H:[UILabel:0x9a93ef0]-(NSSpace(8))-[UITextField:0x1065c660]>"

)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x10669280 H:[UILabel:0x9a93ef0]-(NSSpace(8))-[UITextField:0x1065c660]>

I think that this is caused by conflicting restraints with the cell.textLabel width. So my question is if there is another way to have a textfield in a standard table view cell that stretches from the textLabel width to the end of the cell without breaking default constraints. I feel like I'm close but I can't quite get there. I've searched Google over for three weeks but can't quite get my head around this. I've also watched WWDC videos on auto layout (perhaps I'm just an idiot). Thanks for your help.

Upvotes: 0

Views: 2391

Answers (1)

Hobbes the Tige
Hobbes the Tige

Reputation: 3821

Instead of trying to manipulate Apple's standard cell, I took the plunge and wrote my own UITableViewCell subclass that mimics UITableViewCellStyleValue1. When the tableview goes into edit mode, in the simplest terms I hide the value label and display the textfield. For those who might be struggling with the same thing, I'm posting some code to help you get started:

@interface NXAlphaNumericTextFieldCell : UITableViewCell<UITextFieldDelegate,NumberKeyboardDelegate>

@property (strong, nonatomic)   UITextField *inputTextField;
@property (strong, nonatomic)   UILabel *titleLabel;
@property (strong, nonatomic)   UILabel *valueLabel;

@property (strong, nonatomic)   NSArray *xTitleLabelConstraints;
@property (strong, nonatomic)   NSArray *xTextFieldConstraints;
@property (strong, nonatomic)   NSArray *xValueLabelConstraints;

@end

And a few methods in the implementation:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        // Initialization code
        self.titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 0, 44.0f)];
        self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
        self.titleLabel.font = [UIFont boldSystemFontOfSize:16.0f];
        self.titleLabel.backgroundColor = [UIColor clearColor];

        self.valueLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 0, 44.0f)];
        self.valueLabel.translatesAutoresizingMaskIntoConstraints = NO;
        self.valueLabel.textColor = [UIColor colorWithRed:81.0/255.0 green:102.0/255.0 blue:145.0/255.0 alpha:1.0];
        self.valueLabel.backgroundColor = [UIColor clearColor];

        self.inputTextField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 100, 44.0f)];
        self.inputTextField.translatesAutoresizingMaskIntoConstraints = NO;
        self.inputTextField.autocapitalizationType = UITextAutocapitalizationTypeWords;
        self.inputTextField.autocorrectionType = UITextAutocorrectionTypeYes;
        self.inputTextField.clearButtonMode = UITextFieldViewModeAlways;
        self.inputTextField.delegate = self;

        [self.contentView addSubview:self.valueLabel];
        [self.contentView addSubview:self.titleLabel];
        [self.contentView addSubview:self.inputTextField];

        UILabel *textLabel = self.titleLabel;
        NSDictionary *labelTextFieldViewsDictionary = NSDictionaryOfVariableBindings(textLabel);
        self.xTitleLabelConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|-[textLabel]"
                                                                             options:0
                                                                             metrics:nil
                                                                               views:labelTextFieldViewsDictionary];
        UITextField *textfield = self.inputTextField;
        labelTextFieldViewsDictionary = NSDictionaryOfVariableBindings(textLabel, textfield);
        self.xTextFieldConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|-[textLabel]-50-[textfield]-|"
                                                                                 options:0
                                                                                 metrics:nil
                                                                                   views:labelTextFieldViewsDictionary];
        UILabel *valueLabel = self.valueLabel;
        labelTextFieldViewsDictionary = NSDictionaryOfVariableBindings(valueLabel);
        self.xValueLabelConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:[valueLabel]-|"
                                                                        options:0
                                                                        metrics:nil
                                                                          views:labelTextFieldViewsDictionary];

        [self setNeedsUpdateConstraints];
    }
    return self;
}


- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
    if (self.isEditing) {
        [self.inputTextField becomeFirstResponder];
    }
}

- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
    [super setEditing:editing animated:animated];

    [self addConstraints:self.xTitleLabelConstraints];
    if (editing) {
        if (self.inputType == kCellInputTypeAlphaNumeric) {
            self.inputTextField.keyboardType = UIKeyboardTypeAlphabet;
        } else if (self.inputType == kCellInputTypeEmail) {
            self.inputTextField.keyboardType = UIKeyboardTypeEmailAddress;
        } else if (self.inputType == kCellInputTypePhoneNumber) {
            self.inputTextField.keyboardType = UIKeyboardTypeNamePhonePad;
        } else {
            if (!self.numberKeyboard) {
                self.numberKeyboard = [[NumberKeyboard alloc] initWithNibName:@"NumberKeyboard" bundle:nil];
                self.numberKeyboard.textField = self.inputTextField;
                self.numberKeyboard.showsPeriod = YES;
                self.numberKeyboard.delegate = self;
            }
            self.inputTextField.inputView = self.numberKeyboard.view;
        }
        self.inputTextField.text = self.valueLabel.text;
        self.inputTextField.placeholder = self.titleLabel.text;
        self.valueLabel.hidden = YES;
        self.inputTextField.hidden = NO;

        [self removeConstraints:self.xValueLabelConstraints];
        [self addConstraints:self.xTextFieldConstraints];
    } else {
        [self.inputTextField resignFirstResponder];
        self.inputTextField.hidden = YES;
        self.valueLabel.hidden = NO;
        [self removeConstraints:self.xTextFieldConstraints];
        [self addConstraints:self.xValueLabelConstraints];
    }
}

- (void)updateConstraints
{
    [super updateConstraints];

    if (self.editing) {
        [self removeConstraints:self.xValueLabelConstraints];
        [self addConstraints:self.xTextFieldConstraints];
    } else {
        [self removeConstraints:self.xTextFieldConstraints];
        [self addConstraints:self.xValueLabelConstraints];
    }
}

Upvotes: 1

Related Questions