简体   繁体   中英

Objective-C simple constraint to align UILabel to top right of a table view cell

I'm trying to add a label to the top-right of a table view cell programmatically.

[label addConstraint:[NSLayoutConstraint
    constraintWithItem:label
    attribute:NSLayoutAttributeRight
    relatedBy:NSLayoutRelationEqual
    toItem:cell
    attribute:NSLayoutAttributeRight
    multiplier:1.0
    constant:0]];

This gives an error saying that I referenced something outside the subtree of the view which I'm guessing is the cell itself? Do I need two separate constraints for top and right?

I'd prefer to do it right with constraints, however I've also tried manually getting the cell width to change the label's position. I would have to add a duplicate of the code in the orientation change method incase the user rotates the device. I'm open to doing it this way if I can't use constraints, but I'm not able to get the correct width of the cell, this is what I tried.

[label setFrame:CGRectMake(cell.frame.size.width-318, 10, 308, 30)];

It seems cell.frame.size.width is not getting the correct width of the cell.

EDIT: I worked out the cell width is relative to the content within it, so I changed the above line to get the with of the entire tableView tableView.frame.size.width which does the job. I would still like to get some feedback on constraints to see if that's possible.

EDIT: This is how I set up the cell and label...

NSString *cellId = @"cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if(cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellId];
}

UILabel *label = [[UILabel alloc] initWithFrame: CGRectMake(10, 10, 308, 30)];
label.text = @"Whatever text";

// Contraint added here

[cell.contentView addSubview: label];

EDIT: `Thanks to @frozendevil

Just to show the final code I used to set the width of the label after applying the correct constrains...

[cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:200]];

In general you shouldn't be constraining views in a UITableViewCell against the cell itself, but the cell's -contentView . Assuming you've followed general table view best practices, your label's -superview should be the cell's -contentView —adding constraints from the label to the cell directly is skipping a layer.


Edit: In tinkering with this problem for a few minutes I was unable to get the single constraint style code to behave properly (edit2: re-running it, it seems as though both flavors are working for me), but using the visual format worked for me:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; {
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if(!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];

        UILabel *label = [[UILabel alloc] init];
        label.text = @"Hallo!";
        label.backgroundColor = [UIColor redColor];
        [label sizeToFit];
        label.translatesAutoresizingMaskIntoConstraints = NO;

        [cell.contentView addSubview:label];

        NSDictionary *viewDict = @{@"label" : label};
        [cell.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[label]|" options:0 metrics:0 views:viewDict]];
        [cell.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[label]" options:0 metrics:0 views:viewDict]];

        /* Or this!
        [cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:label
                                                                     attribute:NSLayoutAttributeRight
                                                                     relatedBy:NSLayoutRelationEqual
                                                                        toItem:cell.contentView
                                                                     attribute:NSLayoutAttributeRight
                                                                    multiplier:1.0
                                                                      constant:0]];
        */
    }

    return cell;
}

If you want something more complex the easiest thing to do may be to create a custom UIView subclass that lays itself out internally and add it to the cell's -contentView .


Addendum:

There're a handful of ways to handle the width of the label here:

One is to call constraintWithItem:... and use the NSLayoutAttributeWidth attribute to pin the size. Another would be to use the visual format style to do essentially the same thing. The best would be to set the compression resistance on the label very high (using -[UIView setContentCompressionResistancePriority:forAxis:] ), so that it automatically adjusts to its contents without the need to change any values.

If you're uncomfortable with the visual format language for autolayout I would highly recommend watching the relevant WWDC videos and writing some dummy code to get comfortable with it. I've found it to be the most productive way to use autolayout.

Well I think this is answered already? But if you have a custom subclass of UITableViewCell, placing your label properly is easy by overriding -layoutSubviews :

@implementation MyCell : UITableViewCell

-(void)layoutSubviews
{
    [ super layoutSubviews ] ;

    CGRect bounds = self.contentView.bounds ;
    [ self.label sizeToFit ] ;
    self.label.anchorPoint = (CGPoint){ 1.0f, 0.0f } ;
    self.label.position = (CGPoint){ CGRectGetMaxX( bounds ), CGRectGetMinY( bounds } ;
}

@end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM