Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stick UIView to the right side of it's superview programmatically

I am desperately trying to stick one of my UILabels to the right edge of it's superview while the label's width is variable (it's a time so the thing is getting bigger and should be expanding to the left, this is done using sizeToFit inside of the label when text is set).

So far I have tried loads of things but closest I got with:

_elapsedTimeRightConstraint = [NSLayoutConstraint constraintWithItem:_elapsedTimeView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeRight multiplier:1.0 constant:-150];

While the label is initially set to 150px width. But when I modify the constant, it all goes to hell.

_elapsedTimeRightConstraint.constant = (_elapsedTimeView.frame.size.width * -1);
[self layoutIfNeeded];

So my question is, how do I align trailing edges of a view and it's superview (so it sticks to the right) when the width of the subview is constantly changing. I have been using FLKAutoLayout elsewhere in the project so if this can be done this framework easily than great, but basic autolayout solution would be amazing too!!!

like image 407
Ondrej Rafaj Avatar asked Oct 01 '15 16:10

Ondrej Rafaj


People also ask

What method or methods do you call to add a subview to another view?

To add a subview to another view, call the addSubview(_:) method on the superview. You may add any number of subviews to a view, and sibling views may overlap each other without any issues in iOS.

How do I get Subviews in UIView Swift?

If you need a quick way to get hold of a view inside a complicated view hierarchy, you're looking for viewWithTag() – give it the tag to find and a view to search from, and this method will search all subviews, and all sub-subviews, and so on, until it finds a view with the matching tag number.

What is a UIView?

UIView can be defined as an object by using which we can create and manage the rectangular area on the screen. We can have any number of views inside a view to create a hierarchical structure of the UIViews. The UIView is managed by using the methods and properties defined in the UIView class that inherits UIKit.


1 Answers

First, make sure that translatesAutoresizingMaskIntoConstraints is set to NO, if you are creating the label programmatically.

The first constraint you need is "label.trailing = superview.trailing".

[NSLayoutConstraint constraintWithItem:label 
                             attribute:NSLayoutAttributeTrailing
                             relatedBy:NSLayoutRelationEqual
                                toItem:superview
                             attribute:NSLayoutAttributeTrailing
                            multiplier:1.f
                              constant:0.f]

This will pin the right edge (on left-to-right languages) of the label on the right edge of the superview.

You will now need a constraint for the Y position.

In my test, I have vertically centred the label with the following constraint:

[NSLayoutConstraint constraintWithItem:label 
                             attribute:NSLayoutAttributeCenterY
                             relatedBy:NSLayoutRelationEqual
                                toItem:superview
                             attribute:NSLayoutAttributeCenterY
                            multiplier:1.f
                              constant:0.f]

Now comes the trick!

Every time you change the text on the label, you need to recalculate the frames with AutoLayout.

[superview setNeedsLayout];
[superview layoutIfNeeded];

AutoLayout will:

1) Ask the label of its new size (based on its text).

2) Adjust the size of the label.

3) Pin the trailing edge of the label to the trailing edge of the superview.


Further research

The issue with UILabel is that when you're using AutoLayout and you set text, its intrinsicContentSize changes, but it doesn't trigger a layout update.

A way to enforce this without subclassing UILabel is to use Objective-C runtime.

@interface UILabel (AutoLayout)

- (void)swz_setText:(NSString*)text;

@end

@implementation UILabel (AutoLayout)

+ (void)load
{
  NSLog(@"Swizzling [UILabel setFont:]...");

  Method oldMethod = class_getInstanceMethod(self, @selector(setText:));
  Method newMethod = class_getInstanceMethod(self, @selector(swz_setText:));

  method_exchangeImplementations(oldMethod, newMethod);
}

- (void)swz_setText:(NSString*)text
{
   if (![text isEqualToString:self.text]) {
       [self setNeedsLayout];
   }
   [self swz_setText:text]; //This now points to "setText:" - not a mistake!
}

@end

In this category, I'm "enhancing" setText: implementation by calling setNeedsLayout if the text changes.

Now you just need to invoke layoutIfNeeded on the superview to recalculate/realign the label frame.


Click here for the playground (Swift 2.0 - Xcode 7) where I've tested my code.

I hope this helps.

like image 59
Matteo Pacini Avatar answered Oct 02 '22 21:10

Matteo Pacini