Xcode6 Interface Builder constraints not stretching subviews - ios

Xcode Interface Builder is driving me nuts and I've spent most of my day googling for a solution.
I have a storyboard with a CollectionViewCell that contains a UIImageView. The CollectionViewCell size is 125 x 125 and Autoresize subviews is checked.
The UIImageView has 4 constraints to its superview (leading, trailing, top, bottom), all set to 0. This should ensure that the UIImageView is sized to fill the superview. The UIImageView is sized correctly when the CollectionViewCell is shrunk to a smaller size, but IT DOES NOT WORK when the CollectionViewCell is stretched to a larger size.
EDIT: I've tried the same using a UILabel subview and the same thing happens.
Is this a known problem or is there any way to debug constraints?
Thanks

I've finally found a complete solution to the problem from http://www.nomtek.com/adjusting-your-app-to-ios-8/
Firstly, I had to ensure my CollectionViewCell had Autoresize Subviews checked in Interface Builder, then I had to add the following awakeFromNib method on the ConnectionViewCell subclass.
- (void)awakeFromNib {
[super awakeFromNib];
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
With these two things, the contents of my cells (UIImageView and UILabel) are stretched properly even with dynamic cell sizing using sizeForItemAtIndexPath.

I've made partial progress.
I was setting the cell size dynamically using sizeForItemAtIndexPath.
If I drop that method and set a larger Cell Size of the CollectionView's FlowLayout in Interface Builder, it works as expected. This means the constraints and subview size are updated at compile time.
I would still love to know how to dynamically update the constraints and subview sizes when using sizeForItemAtIndexPath. Any suggestions?

Related

UICollectionViewCell layout margins never update

I have a UICollectionView which basically appears like a table, it has a horizontal stack of UICollectionViewCell views. I want the standard UIViewController layout margins (16pt on the left and right on an iPhone X) to apply to the contents of the cells. This happens in a UITableView.
For some reason my cells initialise with the out of the box margins (8pts) and never update.
I have checked all these options in my cell's .xib, and can log out the layoutMargins property to reveal that the value is correct, but the subviews just never move.
Any ideas?
It turns out that, although you can't see it in Interface Builder, a UICollectionViewCell has a content view, which does not by default inherit layout margins.
This solved it for me:
- (void)awakeFromNib
{
[super awakeFromNib];
self.contentView.preservesSuperviewLayoutMargins = YES;
...
}
Be careful on devices/views with safe area insets set, this may knock around the bottom insets of your cell.

How to make a UIView expand to contain a UIImageView or a UITextView with auto layout and IB?

I have a UIView that contains two subviews - a UIImageView or a UITextView. The UIImageView has a fixed height and width.
The UITextView has variable size.
Only one of the UIImageView and UITextView would be displayed at a time. I plan to accomplish this programmatically by setting hidden = YES.
I would like the UIView to hug whichever child view is not hidden with no margin.
I would like to accomplish this with IB and autolayout if possible because the rest of the view is built this way.
So far I have created constraints that link the 4 edges of both of the subviews to the parent view and constraints for the height and width of the UIImageView. Naturally this creates a content priority ambiguity.
I would appreciate any advice.
Thank you!
So the contain view has to have trailing constraint and constraint with bottom which should have IBOutlet(s) references in viewcontroller and then when the text changes :
- (void)textViewDidChange:(UITextView *)textView {
[self.textView sizeToFit];
self.containerViewTrailingConstraint.constant = self.textView.contentSize.width;
self.containerViewConstraintWithBottom.constant = self.textView.contentSize.height;
[self.view layoutIfNeeded];
}
And when the textView is hidden then you have to set the self.containerViewTrailingConstraint.constant, self.containerViewConstraintWithBottom.constant in relation with the imageView

Enable scrolling in UITableViewController

I want to get my UITableViewController to scroll only when there is not enough room on the screen for all of its cells. I am using static cells that are designed to be shown in an iPhone 5, 6, and 6 plus size screen. However, when shown in the 4s screen, the bottom cells get cut off.
Ideally I would like to use AutoLayout to anchor the bottom of the tableview to the bottom of its superview as I have with other tableviews in my application, but Xcode doesn't allow me to add constraints to the UITableView in a UITableViewController. The reason I have to use the UITableViewController is because I am using a pod for a slide menu (https://github.com/SocialObjects-Software/AMSlideMenu) that subclasses UITableViewController.
I have tried to change the size of the UITableView's frame based on the screen size because I assumed a scroller would automatically be added if the cells took up more room than the containing frame. I used the following code to do so:
CGRect frame = self.tableView.frame;
[self.tableView setFrame:CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, [[UIScreen mainScreen] bounds].size.height - HEADER_HEIGHT)];
However, it had no effect on the way the table was displayed.
Does anyone know how I might be able to set the bottom of the UITableView dynamically and add a scroller to the cells when the screen is too small? I would also welcome any suggestions that might help avoid having to do this at all, as I would prefer not having to do anything too hacky.
You can set the alwaysBounceVertical property to false so the tableView will only scroll when its contentSize is larger than the table view's frame which you can constrain to your view however you like.
tableView.alwaysBounceVertical = NO;
UITableViewController height is determined by automatically calculating number of UITableViewCell you have presented. You can customize the cell height so that height of UITableViewController will automatically changed as you wished.

Springs and struts resizing broken on iOS 8 UITableViewCell

I'm trying to set up a simple UITableViewCell in Interface Builder without auto layout. I'm using the old springs and struts.
When I give a label a flexible width, it seems to lay out as if the UITableViewCell has a much larger width, even though the UITableViewCell tells me it has a width of 375 pixels in layoutSubviews:
- (void)layoutSubviews
{
[super layoutSubviews];
NSLog(#"Label width: %f", self.nameLabel.frame.size.width); // 695.0 for a label that stretches the whole width
NSLog(#"Superview (UITableViewCell) width: %f", self.nameLabel.superview.frame.size.width); // 375.0
}
On a simulated iPhone 5S (iOS 7 or 8), the superview is 320 but the UILabel spreads to 640.
On a simulated iPhone 6, the superview is 375 but the UILabel spreads to 695.
On a simulated iPhone 6 Plus, the superview is 414 but the UILabel speads to 734.
I don't have this problem with other views. For example, I'm able to add a UILabel to a UIViewController and have it stretch the width correctly. What is going on? And how do I fix this?
EDIT:
Interestingly, if I add the constraints programmatically during cellForRowAtIndexPath: then it seems to work as expected, so long as I use the older dequeueReusableCellWithIdentifier instead of dequeueReusableCellWithIdentifier:forIndexPath:. I want to keep all my constraints in Interface Builder though. Any ideas?
I played around a bit with this today. Looks like the frame of the label when it's instantiated relative to the frame of the content view is wrong. If I make the label the same size as the cell in the storyboard, then in awakeFromNib the contentView has a size of CGRectZero but the label has the size I set in the storyboard. Therefore, when you get to layoutSubviews and the contentView is resized to the right size (0,0,320,44), because of the flexible width mask, the label itself is resized along with the content view (width increases by 320 as well). That's why its appearing larger than intended.
The only way I could get around this (albeit, it feels like a gross hack and you should probably stick to auto-layout), was to set the label's frame relative to the content view in awakeFromNib.
- (void)awakeFromNib
{
[super awakeFromNib];
self.label.frame = self.bounds;
}

runtime expansion of uitableview footerview using autolayout

I have a UIView that is a footerview of a uitableview. At run time, the user enters text into a uitextview within the footerview that should adjust to the size of the text content with a height constraint in autolayout.
All other objects in the view (labels, imageviews) have appropriate constraints to accommodate the expansion of the textview.
HOWEVER the height of the overall footerview will not change size, and it is impossible to use autolayout on the tableview footerview height.
Does anyone have a solution? Thanks
Haven't found an actual, elegant, solution yet, but I've postponed fixing this by using a workaround:
Setting the frame of the view used as a footer to be as large as you might possible need. In my case this meant giving it about 60px of spare vertical room. Since it's the footer and there's nothing below it to reposition the user won't be affected by the workaround.
The contents of the footer view are pinned to the top and have enough space to expand when needed.
For the record: my view is loaded from a nib file.
Although in theory the size one gives to the top level view in interface builder is just for design-time and the runtime size should be calculated based on constraints and the resulting intrinsic size, for this specific case I found the height stays the same as it was in IB.
We can change the height of the footer view run time by the following code:
func methodToChangeTableViewFooterHeight()
{
var footerView:UIView = self._tableView.tableFooterView! as UIView
var frame:CGRect = footerView.frame
frame.size.height = self.heightCollectionCS.constant + 10
footerView.frame = frame
self._tableView.tableFooterView! = footerView
}
Here , self.heightCollectionCS.constant is the height constraint for our Collection View.
We can use text content height on that place.
You may try to set again the footer view each time you footer height changes, to inform the table it should change the footer height. Or use inset. From within the footer view:
SetNeedsLayout()
LayoutIfNeeded()
ownertable.TableFooterView = this
Sorry about that, misread that question long ago. You can access the footer directly through the tableview's property tableFooterView.
What you could do is create your default footer in a xib or in your viewDidLoad:. Once you need to increase the size of the footer, you can pull out the UIView from that property and edit its frame if necessary to make it larger.
So make sure the tableFooterView gets assigned a UIView because it is nil by default. To just make the height taller, you can use self.tableView.tableFooterView.frame = CGRectMake(whatever rect you need);

Resources