How to set constraints on multi line labels within a tableview cell - objective-c

I have 4 custom labels inside a custom Tableview cell. The tabelview cell's height is a fixed number.
I am setting the auto layout constraints of the labels programatically.
I've allowed the labels to be as tall as the content inside the labels (NumberOfLines=0).
I've set the width constraints correctly and I've also set the padding constraints inside a loop like so:
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[previousLabel]-20-[customLabel]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(customLabel, previousLabel)]];
Everything is laid out correctly except when one OR more of the labels have too much content and they overflow into the next cell.
I want to programatically add constraints (I think it might be compression resistance and content hugging values) so that the labels stop at the end of their tableview cell.
====
Here is the code where I setup some of the constraints:
for (UIView *subView in cell.contentView.subviews) {
if ([subView isKindOfClass:[AFRCustomLabel class]]) {
customLabel = (AFRCustomLabel *)subView;
if (!previousLabel) {
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-40-[customLabel]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(customLabel)]];
} else {
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[previousLabel]-20-[customLabel]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(customLabel, previousLabel)]];
}
previousLabel = customLabel;
[cell.contentView addConstraints:constraints];
}
}
Here is a screen shot of the label's text overflowing. Keep in mind 2 labels could overflow.

Related

Centring images to a view using autolayout Objective C

I have two images - profileImageView, profileImageViewOfLoggedInUser which is 20px apart from each other and i want to centre them to the view.
Below is my source code
static CGFloat const MeetingDetailNameLabelMarginX = 20;
NSDictionary *views = #{
#"imageView": self.profileImageView,
#"imageViewForLoggedInUser": self.profileImageViewOfLoggedInUser,
#"nameLabel": self.nameLabel,
#"companyNameLabel": self.companyNameLabel,
#"positionLabel": self.positionLabel,
#"statusLabel": self.statusLabel,
};
NSDictionary *metrics = #{
#"imagePaddingLeft": #(MeetingDetailImageViewMarginX),
#"imagePaddingTop": #(MeetingDetailImageViewMarginY),
#"nameLabelPaddingLeft": #(MeetingDetailNameLabelMarginX),
#"nameLabelPaddingRight": #(MeetingDetailNameLabelMarginRightX),
#"nameLabelPaddingTop": #(MeetingDetailImageViewSize + MeetingDetailImageViewMarginY),
#"imageSize": #(MeetingDetailImageViewSize),
#"nameLabelHeight": #(nameLabelFrame.size.height),
#"otherLabelHeight": #(MeetingDetailOtherLabelHeight),
#"dateLabelWidth": #(self.dateLabel.frame.size.width),
#"statusLabelWidth": #(statusFrame.size.width),
#"statusLabelMarginLeftFromView": #(MeetingDetailImageViewMarginX),
};
// image left and width
[self.detailContainer addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"[imageView(imageSize)]"
options:0
metrics:metrics
views:views]];
[self.detailContainer addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[imageView]-imagePaddingLeft-[imageViewForLoggedInUser(imageSize)]"
options:0
metrics:metrics
views:views]];
// image top and height
[self.detailContainer addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-imagePaddingTop-[imageView(imageSize)]"
options:0
metrics:metrics
views:views]];
[self.detailContainer addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-imagePaddingTop-[imageViewForLoggedInUser(imageSize)]"
options:0
metrics:metrics
views:views]];
Please let me know the code to be added.
I get the UI as seen in below screenshot on adding the following code:-
NSLayoutConstraint *centerXConstraint = [self.detailContainer.centerXAnchor constraintEqualToAnchor:_profileImageView.centerXAnchor];
[self.detailContainer addConstraint:centerXConstraint];
If you want to center ImageView relative it's superview, you shouldn't specify some strange margins or something else. All you have to do is specify 2 constraints for centering and 2 constraints for size of imageView.
From iOS 9 you can use these simple api:
NSLayoutConstraint *centerXConstraint = [superView.centerXAnchor constraintEqualToAnchor:imageView.centerXAnchor];
NSLayoutConstraint *centerYConstraint = [superView.centerYAnchor constraintEqualToAnchor:imageView.centerYAnchor];
NSLayoutConstraint *imageViewHeight = [imageView.heightAnchor constraintEqualToConstant:heightValue];
NSLayoutConstraint *imageViewWidth = [imageView.widthAnchor constraintEqualToConstant:widthValue];
Then, you are able to constrain second image view relative to first image view (since first one is already constrained). You can use VFL for it, or whatever.

Set PKPaymentButton width to superview?

I'm adding a PKPaymentButton programmatically (because it doesn't seem like I can simply set a UIButton to PKPaymentButton in storyboard). I'd like my PKPaymentButton to be width of its superview. However, this does not seem to be working. (The PKPaymentButton maintains a predefined width of 140pts.) I've tried setting the horizontal contentHuggingPriority of my PKPaymentButton to UILayoutPriorityDefaultLow but that seems to have no effect. Any ideas how to get a full-width PKPaymentButton that adjusts to the width of my screen?
I've created container view in storyboard, and then added to it PKPaymentButton with necessary constraints.
Here is simple method from my UIView category that makes it easy:
-(void) setupWithContentView:(UIView *) contentView
{
contentView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:contentView];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[contentView]|"
options:0
metrics:nil
views:#{#"contentView":contentView}]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView]|"
options:0
metrics:nil
views:#{#"contentView":contentView}]];
}

UIStoryboard how to replace constraints programmatically?

I have a view controller laid out in a storyboard with autolayout enabled and am looking for a way to change constraints to allow for my view to rotate into landscape and rearrange buttons on the screen. When I try the code below, I get about two dozen "unable to satisfy constraints, breaking constraint..." messages that I cannot really decode.
Is there a way to dynamically replace constraints from a storyboard with constraints that I specify programmatically? I want to completely override the layout of the buttons that I defined in a storyboard.
-(void)updateViewConstraints
{
[super updateViewConstraints];
self.loginButton.translatesAutoresizingMaskIntoConstraints = NO;
self.getStartedButton.translatesAutoresizingMaskIntoConstraints = NO;
self.takeTourButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.loginButton removeConstraints:self.loginButton.constraints];
[self.getStartedButton removeConstraints:self.getStartedButton.constraints];
[self.takeTourButton removeConstraints:self.takeTourButton.constraints];
one = self.loginButton;
two = self.getStartedButton;
three = self.takeTourButton;
NSDictionary *metrics = #{#"height":#50.0,#"width":#100.0,#"leftSpacing":#110.0};
NSDictionary *views = NSDictionaryOfVariableBindings(one,two,three);
[self.view removeConstraints:activeTestLabelConstraints];
[activeTestLabelConstraints removeAllObjects];
if(isRotatingToLandscape)
{
[self registerConstraintsWithVisualFormat:#"|-[one(two)]-[two(three)]-[three]-|" options:NSLayoutFormatAlignAllTop | NSLayoutFormatAlignAllBottom metrics:metrics views:views];
[self registerConstraintsWithVisualFormat:#"V:[one(height)]-|" options:0 metrics:metrics views:views];
}else
{
[self registerConstraintsWithVisualFormat:#"|-leftSpacing-[one(width)]" options:0 metrics:metrics views:views];
[self registerConstraintsWithVisualFormat:#"[two(width)]" options:0 metrics:metrics views:views];
[self registerConstraintsWithVisualFormat:#"[three(width)]" options:0 metrics:metrics views:views];
[self registerConstraintsWithVisualFormat:#"V:[one(height)]-[two(75)]-[three(100)]-|" options:NSLayoutFormatAlignAllCenterX metrics:metrics views:views];
}
}
Updated with Rob's answer, here's the method to remove constraints that I use
-(void)removeConstraintForView:(UIView*)viewToModify
{
UIView* temp = viewToModify;
[temp removeFromSuperview];
[self.view addSubview:temp];
}
It sounds like you want to just remove all the constraints on a view. Since constraints on a view are often held by an ancestor of the view, it's not obvious how to remove all of the constraints easily. But it is actually pretty easy.
Removing a view from the view hierarchy removes any constraints betwixt the view and other views outside of it. So just remove your view from its superview and then add it back:
// Remove constraints betwixt someView and non-descendants of someView.
UIView *superview = someView.superview;
[someView removeFromSuperview];
[superview addSubview:someView];
If there are any constraints betwixt someView and its descendants, those constraints might be held by someView itself (but cannot be held by any descendants of someView). If you want to remove those constraints also, you can do so directly:
// Remove any remaining constraints betwixt someView and its descendants.
[someView removeConstraints:[someView constraints]];

UIViewController viewDidLoad

I've got a new project and I can work with iOS 5+ feature, so I choose to use both AutoLayout and UIViewController childControllers capabilities.
The screen I'm trying to create is simple :
1. The top area has lot of "complex" controls.
2. The bottom area is a tableview to visualize results.
What I want
I want the controls area to take all necessary place (ie the place I gave into Interface Builder). The TableView will shrink as needed.
Problem
When loaded from a XIB the UIViewController.view frame has ALWAYS a size of 0x568. I was expected 0x200 (if 200 was the size I configured in Interface Builder).
What works : with AutoLayout
I created a XIB with all my element. I did the outlet on the "root view" and I did outlet and all views containers.
Then in my viewDidLoad method I added the containers and configured constraints.
-(void) setupConstraints
{
NSMutableArray *constraints = [NSMutableArray arrayWithCapacity:1];
// Views dictionary.
UIView *topView = self.topView;
UIView *table = self.tableTracks;
NSDictionary *views = NSDictionaryOfVariableBindings(topView, table);
// Views metrics.
NSNumber *topViewHeight = [NSNumber numberWithFloat:self.topView.frame.size.height];
NSDictionary *metrics = NSDictionaryOfVariableBindings(topViewHeight);
// Pin the topView to the top edge of the container.
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[topView(==topViewHeight)][table]|" options:0 metrics:metrics views:views]];
// Pin the topView edges to both sides of the container.
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[topView]|" options:0 metrics:nil views:views]];
// Pin the table edges to both sides of the container.
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[table]|" options:0 metrics:nil views:views]];
[self.view addConstraints:constraints];
}
With this, I can simple resize the topView view with Interface Builder and the TableView will be resized as needed (no compression resistance, I don't care if we can't see the table).
What DO NOT works : with AutoLayout and ChildControllers
Then for simplicity I choose to use ChildControllers (lot of outlet required to perform custom UIFont setup, too bad XCode can't handle them yet!).
I made the following modifications :
Created HomeTopViewController classes + XIB. Created HomeTableViewController classes + XIB.
Copied all views from the origin to the correct XIB. Add the UIViewController references + wired the outlets.
The root container of HomeTopViewController is configured to 200px height.
Wired my containers to the view outlet of my childs controllers.
Then I updated my setup code to the following :
-(void) _addChildViewControllersAndSetupConstraints {
// Get required metrics variables
NSNumber *topViewHeight = [NSNumber numberWithFloat:self.topViewController.view.frame.size.height];
CFShow(self.topViewController.view);
// log is : <UIView: 0xa209c20; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0xa2097e0>>
NSLog(#"self.topViewController.view.frame.size.height = %f", self.topViewController.view.frame.size.height);
// Informs controllers the ADD operation started
[self addChildViewController:self.topViewController];
[self addChildViewController:self.tableViewController];
// Add view to the view's hierarchy
self.topViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
self.tableViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.topViewController.view];
[self.view addSubview:self.tableViewController.view];
// Setup constraints
NSMutableArray *constraints = [NSMutableArray arrayWithCapacity:1];
// Views dictionary
UIView *topView = self.topViewController.view;
UIView *table = self.tableViewController.view;
NSDictionary *views = NSDictionaryOfVariableBindings(topView, table);
// Views metrics dictionary
NSDictionary *metrics = NSDictionaryOfVariableBindings(topViewHeight);
// Pin the topView to the top edge of the container.
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[topView(==topViewHeight)][table]|" options:0 metrics:metrics views:views]];
// Pin the topView edges to both sides of the container.
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[topView]|" options:0 metrics:nil views:views]];
// Pin the table edges to both sides of the container.
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[table]|" options:0 metrics:nil views:views]];
// Adds constraints
[self.view addConstraints:constraints];
// Informs controllers the ADD operation ended
[self.topViewController didMoveToParentViewController:self];
[self.tableViewController didMoveToParentViewController:self];
}
Problem
No matter how I size the UIView container in HomeTopViewController the line CFShow(self.topViewController.view); will always give me frame = (0 0; 320 568) when I expected 200px.
I did configure the layout size to "Freeform" or "None".
What I want to avoid as much as possible
I would hate having to create other outlets then View on HomeTopViewController and HomeTableViewController and storing the initial frame / height into a UIViewController property.
Questions
At load time, are all controller.view.frame property sized on the screen ? (be it 3,5 or 4 inches)
How can I solve this without hardcoding the size somewhere // creating additionnal outlets ?
This would be easier if you used a storyboard. You can add container views to you main controller's view and size them how you like, and you automatically get view controllers embedded in these container views that are set to the correct size. These container views (next to the regular UIView in the object list) are only available from a storyboard, not xibs. This will require almost no code, and you probably won't have to add any constraints manually.

How to increase the height of NSTableHeaderView?

I need to implement a headerview with specific size and gradient. I have to insert images in certain cells of the headerview.Tried to create the cells for the headerview using the following code,but i was not able to customize the headerview.
[[tableColumn headerCell] setImage:[NSImage imageNamed:#"sampleHeader"]];
If I use the overridden subclass of headerview, I was not able to view the images or text in the header cell.Please provide me any pointers to solve this issue.
I was able to insert images and text by subclassing the NSTableHeaderCell.How to increase height of the NSTableHeaderView?
If I subclass both NSTableHeaderView and NSTableHeaderCell , was not able to view anything in the
headercell.I used the following code for setting headerview and headercell
[tableView setHeaderView:CustomHeaderView];
[tableColumn setHeaderCell:[[[CustomHeaderTableCell alloc] initImageCell:
[NSImage imageNamed:#"sample"]]autorelease]];
I have the same issue as given in the below url
http://lists.apple.com/archives/cocoa-dev/2002/Jun/msg00331.html
You don't need to subclass NSTableHeaderView.
I was able to change the height of the header view using the following snippet in the controller class:
-(void)awakeFromNib {
NSRect frame = tableView.headerView.frame;
frame.size.height = 26;
tableView.headerView.frame = frame;
}
It should be noted that the scroll view takes care of the layout. It automatically changes the frame of the headerView as necessary, but leaves the height intact. Resizing the clip view etc as suggested in the other answer is not necessary.
You can also create a NSTableHeaderView object, initialize it with a frame(rect with height and width) and set that NSTableHeaderView object to your table view.
NSTableHeaderView *tableHeaderView = [[NSTableHeaderView alloc] initWithFrame:NSMakeRect(0, 0, 120, 60)];
[myTableView setHeaderView:tableHeaderView];
[tableHeaderView release];
Following link helped me in solving the issue.
http://lists.apple.com/archives/cocoa-dev/2003/Feb/msg00676.html
You need to set the Frame for NSClipView, NSTableHeaderView and the CornerView
This is how I implemented the same in Code.
for(NSView * subview in [topScrollView subviews])
{
for(NSView * subSubView in [subview subviews])
{
if([[subSubView className] isEqualToString:#"NSTableHeaderView"] && [[subview className] isEqualToString:#"NSClipView"])
{
[subSubView setFrameSize:NSMakeSize(subSubView.frame.size.width, subSubView.frame.size.height+5)];//HeaderView Frame
[subview setFrameSize:NSMakeSize(subview.frame.size.width, subview.frame.size.height+5)];//ClipView Frame
}
}
if ([[subview className] isEqualToString:#"_NSCornerView"])
{
[subview setFrameSize:NSMakeSize(subview.frame.size.width, subview.frame.size.height+5)]; //CornerView Frame
}
}