I have two labels next to each other, but the right one truncates even when I set truncate rules on the left one.
My code:
// Club name
labelFirst = [[UILabel alloc] init];
labelFirst.adjustsFontSizeToFitWidth = NO;
[labelFirst setLineBreakMode:NSLineBreakByTruncatingTail];
[labelFirst setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.contentView addSubview:labelFirst];
labelSecond = [[UILabel alloc] init];
[labelSecond setAdjustsFontSizeToFitWidth:NO];
[labelSecond setTranslatesAutoresizingMaskIntoConstraints:NO];
[labelSecond setText:NSLocalizedString(#"IsCancelled", nil)];
[self.contentView addSubview:labelSecond];
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-73-[label]-[label2]-10-|" options:0 metrics:nil views:#{#"label": labelFirst, #"label2": labelSecond}]
[self.contentView addConstraints:constraint];
How should I do this?
This is where Content Compression Resistance Priority comes into play. Set these values higher and lower based on which one you want compressed first. Higher for the label that you want take priority in resisting compression. Lower for the label that you want compressed (truncated) first.
You can do it programmatically (default is 750):
[labelFirst setContentCompressionResistancePriority:749
forAxis:UILayoutConstraintAxisHorizontal];
Or in interface builder under the size inspector tab:
Here is another answer going into more detail on how they work.
NSLineBreakByTruncatingTail - it's default LineBreakMode value for labels, change it for labelSecond to have diference
Related
I have seen several questions regarding this topic; however, none of them resolved the issue, and for the life of me I cannot figure out why the constraints are not taking effect. (Maybe because I haven't slept in a while.. I know it's counterproductive).
I am new to IOS development, but I'm hitting it hard and a quick learner. If you could provide an explanation as to why my original code was not working that would be super helpful. Thumbs up to whoever can resolve issue!
Okay, so I'm developing an app that I've actually been working on for quite a while & I did a real sloppy job when I first began. So I'm basically rebuilding the app's code but completely cutting out the Interface Builder (Storyboard). I am trying to pin a UIButton to a UIView locally (I'd rather not declare it globally).
// parentView initialized //
parentView=[[UIView alloc]initWithFrame:CGRectMake(0, 0, screenWidth, screenHeight)];
[parentView setBackgroundColor:[UIColor clearColor]];
[self.view addSubview:parentView];
parentView.tag = 0;
// homeScreenView initialized //
homeScreenView=[[UIView alloc]initWithFrame:CGRectMake(0, homeScreenTitle.frame.size.height, screenWidth, screenHeight-homeScreenTitle.frame.size.height-height)];
[homeScreenView setBackgroundColor:[UIColor greenColor]];
[parentView addSubview:homeScreenView];
homeScreenView.tag = 2;
// chatMenuView initialized //
chatMenuView=[[UIView alloc]initWithFrame:CGRectMake(0, homeScreenTitle.frame.size.height+10, 100, screenHeight-height-10-10-homeScreenTitle.frame.size.height)];
[chatMenuView setBackgroundColor:[UIColor grayColor]];
[parentView addSubview:chatMenuView];
chatMenuView.tag = 3;
// chatMenuButton initialized //
chatMenuButton = [UIButton buttonWithType:UIButtonTypeCustom];
NSString *buttonText = [NSString stringWithFormat:#"CHAT"];
[chatMenuButton setTitle:buttonText forState:UIControlStateNormal];
[parentView addSubview:chatMenuButton];
[chatMenuButton sizeToFit];
chatMenuButton.center = CGPointMake(0,screenHeight/2);
UIImage *chatIcon = [UIImage imageNamed:#"GrayHouse1.png"];
[chatMenuButton setBackgroundImage:chatIcon forState:(UIControlStateNormal)];
chatMenuButton.tag = 5;
// pinChatButton //
NSLayoutConstraint *pinChat = [NSLayoutConstraint constraintWithItem:chatMenuView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:chatMenuButton
attribute:NSLayoutAttributeTrailing
multiplier:1
constant:0];
[self.view addConstraint: pinChat];
I would like to also add that all of this code is in the viewDidLoad method & all of the other views are declared in the header file (as IBOutlets).Basically when I run the code, the UIView leading margin is at position x = 100, and the button is at position x = 0 which is what it's suppose to be at prior to adding constraints which should also move the button to position x = 100.
So I'm basically rebuilding the app's code but completely cutting out
the Interface Builder (Storyboard)
First off - you're really swimming against the tide here. Everybody uses Interface Builder for basic layout, because visualizing the layout, and seeing the constraints is much easier in a visual editor. You should really save code-generated constraints for when you're trying to do something clever. Not to mention all that code to set the tag, etc, etc.
Having gotten that out of the way, Your constraint doesn't make a whole lot of sense to me - you're constraining the leading space on chatMenuView to be equal to the trailing space of chatMenuButton. I can imagine scenarios in which this'd be useful, but what you probably want for this is something more like:
NSLayoutConstraint *pinChat = [NSLayoutConstraint constraintWithItem:chatMenuButton
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:chatMenuView
attribute:NSLayoutAttributeLeading
multiplier:1
constant:100];
Finally, even if you are sure you want to create constraints in code, consider using the visual format, which is at least somewhat readable.
I'm working on an application that will have a picture of guitar fretboard like on a screenshot above. There will be notes displayed in different places of fretboard (represented by red circles- there will be much more of them than on the screenshot).
What kind of solution would you recommend to guarantee that the notes will be displayed in the right places of fretboard (which is just an image) and will not fall apart or distribute unevenly? Remember that the fretboard image will scale, depending on resolution, so notes positions coordinates should change accordingly.
If you're going to use an image that scales with the screen size, then this is one of the few cases where I would probably not use auto layout. I would create an array of doubles that would be the fraction of the distance from the left edge to the center of a particular space between the frets. So for instance, the value at index 3 (for the space where your left most red dot is) would be 0.2707 (36mm/133mm based on your image). You would then set the frame's origin.x value with that fraction times the width of the image.
You want to do all this in code I think and be able to activate based on fret location, string location etc... Use relative offsets for each string and fret from a known point.
This ties into your other question on SO about getting your fret image correct. Unless you can code the image accurately, then coding the note positions accurately is going to be tricky.
IMHO, you can not do this with auto layout, esp taking into account your other question: iOS autolayout - gap between image and top of the screen, despite constraints set
If you already managed to place the imageViews in the right places, you could create an UIView subclass that contains both the image and a label on top of it.
This would be my suggestion:
#interface NoteView()
#property (nonatomic, weak) UILabel *label;
#property (nonatomic, weak) UIImageView *imageView;
#end
#implementation NoteView
-(instancetype)init {
self = [super init];
if (self) {
[self setupContent];
}
return self;
}
- (void)setupContent {
UIImageView *imageView = [[UIImageView alloc] init];
[self addSubview:imageView];
[imageView setTranslatesAutoresizingMaskIntoConstraints:NO];
UILabel *label = [[UILabel alloc] init];
[self addSubview:label];
[label setTranslatesAutoresizingMaskIntoConstraints:NO];
// This adds vertical constaints between the view, the label and the imageView
// You can change the values to suit your needds
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[label]-10-[imageView]-0-|" options:0 metrics:nil views:#{ #"label": label, #"imageView": imageView }]];
// This makes the view be as big as the label
// I assumed the labels will be bigger than the images,
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[label]-0-|" options:0 metrics:nil views:#{ #"label": label}]];
// If the images are bigger use these constraints
// [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[label]-0-|" options:0 metrics:nil views:#{ #"label": label}]];
// This msakes sure that the label and the image have the same centers
[self addConstraint:[NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:imageView attribute:NSLayoutAttributeCenterX multiplier:1.f constant:0]];
[self setLabel:label];
[self setImageView:imageView];
}
// This would be a public configuration method.
- (void)setLabelText:(NSString *)text andImage:(UIImage *)image {
[self.label setText:text];
[self.imageView setImage:image];
}
#end
All you would need to do is place this custom view as you do with the images and if the frame changes call layoutIfNeeded on the custom view so that it layouts both the image and the label correctly.
Hope this helps. Let me know if you have questions.
i'm trying to set a constraint in code and don't get the expected result.
I have a UIView container with 3 buttons (sub views) and i'm trying to set one's leading space to be the average of the other two's leading spaces (so it'll be in the middle, horizontally).
The numbers i get seem to be right when compared to the numbers i see on the storyboard when i place the 3 buttons in the position i want.
I'm getting the leading space by their frame's x value (i've double checked that aligmentRectForFrame: gives the same results), and i average them.
I use:
NSLayoutConstraint *twitterConstraint = [NSLayoutConstraint constraintWithItem:middleButton attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeLeading multiplier:1.0 constant:average];
[twitterConstraint setPriority:UILayoutPriorityRequired];
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
[NSLayoutConstraint activateConstraints:#[twitterConstraint]];
the basic functionality works i.e. if i put a number instead of the average i see results. I'm getting unexpected results with the current code. specifically, i'm getting the "middle button" on the right hand side of the other 2 buttons.
help!
idan
Expanding on why #Ken Thomases answer in comments works: Auto Layout first goes from subview-up to collect information, and then goes superview-down to set frames. So this code is sampling values of its subview frames, but at the time it is executed (in updateConstraints or updateViewConstraints or somewhere else) those views' frames haven't yet been set to their auto-layout-approved values. Unpredictable results can happen.
Calling -layoutIfNeeded on this view forces the Auto Layout engine to do this for the subviews--actaully do the layout work. So then sampling those subviews can work.
Unfortunately in this method, problems are created both sampling the frames to get information and calling layoutIfNeeded, which duplicates the expensive layout operations. As noted, it requires the buttons to be all the same size. Regardless, it's probably fine for this use case.
The way to set items to be evenly spaced using the native Auto Layout system (and allowing different-sized items) is spacer views. It's inelegant, but necessary. It can be done manually in IB, and here's how to do it with Visual Format Language:
NSMutableDictionary *autoLayoutDict = [[NSMutableDictionary alloc] init];
NSDictionary *setDictionary = #{
#"leftLabel":self.leftLabel,
#"middleLabel":self.middleLabel,
#"rightLabel":self.rightLabel
};
[autoLayoutDict setValuesForKeysWithDictionary:setDictionary];
int numberOfSpacerViewsRequired = 2;
for (int i = 0; i < numberOfSpacerViewsRequired; i++) {
UIView *spacerViewX = [[UIView alloc] init];
spacerViewX.backgroundColor = [UIColor clearColor];
spacerViewX.userInteractionEnabled = NO;
spacerViewX.translatesAutoresizingMaskIntoConstraints = NO;
NSString *spacerViewKey = [NSString stringWithFormat:#"spacerView%i", i];
[autoLayoutDict setObject:spacerViewX forKey:spacerViewKey];
[self.view addSubview:spacerViewX];
}
NSArray *constraintsToAdd = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[leftLabel]-0-[spacerView0(spacerView1)]-0-[middleLabel]-0-[spacerView1(spacerView0)]-0-[rightLabel]"
options:NSLayoutFormatAlignAllCenterY
metrics:0
views:autoLayoutDict];
[self.view addConstraints:constraintsToAdd];
Say i have two labels, close to each ether, and one will maybe grow:
So if the left label will change and grow, i would like the right label to move to the right and give space, but not squeeze, like so:
Normally i just use:
CGFloat width = [self.priceLabel.text sizeWithFont:[UIFont systemFontOfSize:13]].width;
self.myLabel.frame = CGRectMake(self.myLabel.frame.origin.x, self.myLabel.frame.origin.y, width,self.myLabel.frame.size.height);
and move the right label to the end of of the left label,
But i'm using AutoLayout and looking for a way to make it possible
Thanks!!
You can start by trying the visual format:
NSString *visualFormat = #"|-[label1]-[label2]";
NSLayoutFormatOptions options = NSLayoutFormatAlignAllCenterY | NSLayoutFormatDirectionLeftToRight;
NSDictionary *views = NSDictionaryOfVariableBindings(label1, label2);
NSArray *layoutConstraints = [NSLayoutConstraint constraintsWithVisualFormat:visualFormat options:options metrics:nil views:views];
[view addConstraints:layoutConstraints];
If you also want to add a right margin and any available space in the middle you can use
NSString *visualFormat = #"|-[label1]-#1-[label2]-|";
Check the visual format guide for all possible options.
Basically the title explains the problem. Working with XCode, I have this button and I populate its title with different text sources (some are long texts some are short). I just want that the button resizes dynamically with the content using Autolayout.
Things not working:
sizeToFit;
Setting a height constraint of >= x;
setting the button's frame height = titleLabel height.
Nobody seems to know over the internet and I wonder how could it be possible? I think is one of the MOST COMMON FEATURES for a button.
Someone knows a way to help me? Am I doing something wrong with this idea? Is there some other way to achieve this?
Thank you to anyone who will answer. Really.
Ok i found this solution:
NSAttributedString *text = [[NSAttributedString alloc] initWithString:[self titleForState:UIControlStateNormal] attributes:nil];
CGRect rect = [text boundingRectWithSize:(CGSize){287, CGFLOAT_MAX}
options:NSStringDrawingUsesLineFragmentOrigin
context:nil];
NSLayoutConstraint *buttonConstraint = [NSLayoutConstraint
constraintWithItem:self
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem: nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0f
constant:rect.size.height];
[self addConstraint:buttonConstraint];
Which works but ONLY ONE TIME. I mean: as soon as the new title populates the titleLabel it says that there is already a constraint and It doesn't work anymore...
you can do like this
CGSize stringsize = [myString sizeWithFont:[UIFont systemFontOfSize:14]];
//or whatever font you're using
[button setFrame:CGRectMake(10,0,stringsize.width, stringsize.height)];