table view disappears when I add a constraint - ios

I have a container view:
and I have a table view that I add to the container programmatically:
self.searchResultsSourcesTVC = [storyboard instantiateViewControllerWithIdentifier:#"searchResultsSourcesTVC"];
[self.searchResultsContainer addSubview:self.searchResultsSourcesTVC.view];
The result here is that the table view is not automatically sized to fit in the container; it seems to extend south of the screen quite a bit, to the extent that the scroll bar can completely disappear south of the screen. But it does display the table and search results.
So I attempted to add a constraint (I am using auto layout) to make the vertical bounds of the table view match those of the container view:
UITableView *tableView = self.searchResultsSourcesTVC.tableView;
NSDictionary *views = NSDictionaryOfVariableBindings(tableView);
tableView.translatesAutoresizingMaskIntoConstraints = NO; // without this line there are conflicts
[self.searchResultsContainer addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[tableView]|" options:0 metrics:Nil views:views]];
[self.view layoutIfNeeded]; // not sure whether this line is necessary
Now there is no table at all. Just a blank view.
What am I doing wrong? What's the best way to add a table view to a container view programmatically and make the bounds of the table view coextensive with those of the container view? Thanks

It seems like you don't have enough constraints to fully describe what the size you need is. For example it seems like you don't have horizontal constraints. You generally need 4 constraints to fully express the size of the view you want to layout; specifically you need a center X, center Y, width, and height.
For example:
NSLayoutConstraint *con1 = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterX relatedBy:0 toItem:view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
NSLayoutConstraint *con2 = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterY relatedBy:0 toItem:view attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
NSLayoutConstraint *con3 = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:0 toItem:view attribute:NSLayoutAttributeWidth multiplier:1 constant:0];
NSLayoutConstraint *con4 = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:0 toItem:view attribute:NSLayoutAttributeHeight multiplier:1 constant:0];
NSArray *constraints = #[con1, con2, con3, con4];
[self.searchResultsController addConstraints:constraints];

Related

Autolayout issue with constraintWithItem VS constraintsWithVisualFormat

I have pretty simple UI that works well with constraintsWithVisualFormat, I tried to replace that with constraintWithItem and for some reason it is not working. I don't know what's wrong here.
The literal sentence I understood is
Vertically, the contentView should fill the entire height of its
superview with no padding.
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(contentView);
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView]|"
options:0
metrics:nil
views:viewsDictionary];
[containerView addConstraints:constraints];
//Below is my alternative code NOT working, but this should work too?
NSLayoutConstraint *constraints =
[NSLayoutConstraint constraintWithItem:contentView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeHeight
multiplier:1.0
constant:0];
[containerView addConstraint:constraints];
Your new code specifies the height, but not position. The visual format specification says nothing about the item’s height, but instead pins the top and bottom edges of the views to each other.
Instead of creating constraint for height, create two constraints, one for top margin, and one for bottom margin, pinning the edges of contentView to containerView.
You should change it from height to top. And also add similar constraint to bottom.
NSLayoutConstraint *constraints =
[NSLayoutConstraint constraintWithItem:contentView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
[containerView addConstraint:constraints];
This visual format:
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(contentView);
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView]|"
options:0
metrics:nil
views:viewsDictionary];
[containerView addConstraints:constraints];
should be replaced with two constraints:
NSLayoutConstraint *topConstraint =
[NSLayoutConstraint constraintWithItem:contentView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
[containerView addConstraint: topConstraint];
and
NSLayoutConstraint *bottomConstraint =
[NSLayoutConstraint constraintWithItem:contentView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0];
[containerView addConstraint:bottomConstraint];

Facebook Audience Network ios FBAdView width

I added a FBAdView to a UIView in the view controller.
(I set the view background to red)
the FBAdView was created with adSize of kFBAdSizeHeight50Banner.
The problem is that the FBAdView calculates its width when it is being added, but after rotating the device it doesn't calculate its width again
I tried using autolayout but it didn't work
code for adding the FBAdview (Adding to UILabel with red backgroud)
FBAdView *fbAdView = [[FBAdView alloc] initWithPlacementID:#"***************_***************"
adSize:kFBAdSizeHeight50Banner
rootViewController:self];
fbAdView.delegate = self;
[fbAdView loadAd];
[self.banner addSubview:fbAdView];
code for autolayout - doesn't work
// Width constraint, parent view width
[self.banner addConstraint:[NSLayoutConstraint constraintWithItem:fbAdView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.banner
attribute:NSLayoutAttributeWidth
multiplier:1
constant:0]];
// Height constraint, parent view height
[self.banner addConstraint:[NSLayoutConstraint constraintWithItem:fbAdView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.banner
attribute:NSLayoutAttributeHeight
multiplier:1
constant:0]];
// Center horizontally
[self.banner addConstraint:[NSLayoutConstraint constraintWithItem:fbAdView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.banner
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
// Center vertically
[self.banner addConstraint:[NSLayoutConstraint constraintWithItem:fbAdView
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.banner
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0]];
print screen
when the banner is added it fit the screen width
after rotating it doesn't change its width (like its parent red view)
I think you're missing some constraints.
Adding these lines (might need some minor changes) makes your code work:
self.banner.translatesAutoresizingMaskIntoConstraints = NO;
fbAdView.translatesAutoresizingMaskIntoConstraints = NO;
// Width constraint
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.banner
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:1
constant:0]];
// Height constraint
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[banner(==50)]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(banner)]];
I've posted a sample project at https://github.com/overlordnyaldee/BannerAutoLayout

Programmatically using constraints to center a UIIMageView

I'm working on an iOS project in which I need to programmatically use constraints for my views. I'm used to storyboards, but am using xibs for this particular project due to the nature of the project specification.
I have a MainViewController, in which I create the following properties in the .h file:
#property (nonatomic, strong) IBOutlet UIImageView *backgroundImageView;
#property (nonatomic, strong) IBOutlet UIImageView *logoImage;
I added these UIImageView instances to my XIB file, and selected the appropriate images via the Attributes inspector.
In my .m file, I have a addConstraints method, which is called in viewDidLoad. Inside this method, I make sure to turn off converting autoresizingMasks into constraints:
self.backgroundImageView.translatesAutoresizingMaskIntoConstraints = NO;
self.logoImage.translatesAutoresizingMaskIntoConstraints = NO;
Then, I've set up constraints so that the background image takes up the entirety of the superview:
id mainView = #{#"background": self.backgroundImageView};
//Set constraints so that the background takes up the entirety of the superview
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[background]|" options:0 metrics:nil views:mainView]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[background]|" options:0 metrics:nil views:mainView]];
Finally, I set constraints so that the logo view is in the center of the view (which is where I am going wrong):
// Width constraint
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.logoImage
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeWidth
multiplier:0.5
constant:0]];
// Height constraint
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.logoImage
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0]];
// Center horizontally
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.logoImage
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
// Center vertically
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.logoImage
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0]];
The error I receive is
*** +[NSLayoutConstraint constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:]:
Constraint items must each be an instance of UIView or subclass'
, which I don't fully understand, as my constraint items (two UIImageView instances) are either subclasses of UIView, but I may be misunderstanding this. Can anyone help point out where I'm going wrong?
Try that :
// Width constraint
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.logoImage
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.backgroundImageView
attribute:NSLayoutAttributeWidth
multiplier:0.5
constant:0]];
// Height constraint
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.logoImage
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.backgroundImageView
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0]];
// Center horizontally
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.logoImage
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.backgroundImageView
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
// Center vertically
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.logoImage
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.backgroundImageView
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0]];
You were trying to define constraints between your UIImageView and your view controller. You must define constraints between views which are subviews of a same view.

Constraint added programmatically to a xib view not working

I have a view(self.printSettingsView) created from xib. I add this view as a subview to another view(self.view). I programmatically add constraints as follows:
[self.printSettingView setTranslatesAutoresizingMaskIntoConstraints: NO];
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:self.printSettingView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0];
[self.view addConstraint:leftConstraint];
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:self.printSettingView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.topBar
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0];
[self.view addConstraint:topConstraint];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:self.printSettingView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:1.0
constant:0];
[self.view addConstraint:heightConstraint];
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:self.printSettingView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:0];
[self.view addConstraint:widthConstraint];
All the other constraints take effect except for height.
What could i be doing wrong here???
Thanks
Without knowing the constraints on your xib file or what you expect to happen vs what is happening (a screenshot would be helpful) it's hard to say. However I do have one suggestion where maybe the logic isn't correct.
The second constraint is pinning printViewSettings top to the bottom of the topBar, that part makes sense. The next one however sets the height of printViewSettings to the height of its superview. This may not jive with what you want because your superview contains your topBar as well and so may be larger than you expect. What you might actually want is a constraint that pins the bottom of printViewSettings to the bottom of the superview instead.
Sorry guys about the incomplete information. The problem in a gist was that i was assigning a constraint to the subview in my main view and whatever be the constant of the constraint the size of the subview remained constant. I found that the issue was the subview in turn had components(subviews) with fixed height constraints. I made them proportional to the height of the parent view and it works now.

Using Auto Layout to change spaces equally on resizing

If I have a view in my app with 3 subviews (as an example) with the following spacing between them:
TOP - 40 points - SUBVIEW 1 - 60 points - SUBVIEW 2 - 80 points - SUBVIEW 3 - 80 points - BOTTOM
I understand how I can use auto layout to ensure each of my subviews keeps it's height and width, and I can get everything to be aligned on either a 3.5 OR 4 inch iPhone screen.
But I can't work out what kind of constraint I need to make it so that if it were aligned for a 3.5" screen, and then went to a 4" screen, each spacing would increase proportionally (e.g. 40 points would go to 47, 60 to 71, 80 to 95 - or thereabouts).
Is this possible? Or do I need to make all the spacing equal between the elements? (If so how would I still get it to resize equally?)
I'm new to Auto Layout so if I've missed anything off, or haven't made it clear what I means please let me know, thank you.
The Trick is to add not only one but TWO constraints between your views. One with "Greater Than or Equal" and one with "Less Then or Equal".
The minimum size (Greater Then or Equal) should be the spacing on the 3.5" display.
The maximum size (Less Then or Equal" should be the spacing on the 4" display.
Example:
TOP - 40 points - SUBVIEW 1 - 60 points - SUBVIEW 2 - 80 points - SUBVIEW 3 - 80 points - BOTTOM
TOP - SUBVIEW1:
Select both in Interface Builder. Add the constraint "Vertical Spacing" two times.
Set one to Greater Then or Equal 40.
Set the other to Lesser Then or Equal 47.
The sum of all Greater Then values + all heights of your views should be 480 pixel (3.5")
The sum of all Lesser Then values + all heights of your views should be 568 pixel (4")
I don't know any easy way to do this. I made one where all the spaces were equal, but to do that, I had to fill the spaces with invisible labels (just label with no title). So, I had my 4 visible objects and the 5 "spacer labels" all right on top of each other. I made the 4 visible objects all have explicit heights, and the spacers with no fixed heights, but set to be all the same:
-(void)viewDidLoad {
[super viewDidLoad];
NSMutableDictionary *viewsDict = [NSMutableDictionary dictionary];
for (int i=1; i<5; i++) { // Labels with titles
UILabel *b = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 150, 44)];
b.text = #"This is my label";
[b setTranslatesAutoresizingMaskIntoConstraints:NO];
[viewsDict setObject:b forKey:[NSString stringWithFormat:#"b%d",i]];
}
for (int i=1; i<6; i++) { // Spacer labels
UILabel *l = [[UILabel alloc ]init];
[l setTranslatesAutoresizingMaskIntoConstraints:NO];
[viewsDict setObject:l forKey:[NSString stringWithFormat:#"l%d",i]];
}
for (id obj in viewsDict.allKeys)
[self.view addSubview:viewsDict[obj]];
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[l1][b1][l2(==l1)][b2][l3(==l1)][b3][l4(==l1)][b4][l5(==l1)]|"
options:NSLayoutFormatAlignAllLeading
metrics:nil
views:viewsDict];
NSArray *constraints2 = [NSLayoutConstraint constraintsWithVisualFormat:#"|-[b1]"
options:0
metrics:nil
views:viewsDict];
[self.view addConstraints:constraints];
[self.view addConstraints:constraints2];
}
To make the spaces different, I think you have to use the longer form of expressing constraints, rather than the visual format. The below code seems to work for me. I'm using the same definition of the views as above, except that I cut the number of titled labels to 3 and spacers to 4 to match you question. The relative spacing should be as in your example, 2:3:4:4.
NSLayoutConstraint *con1 = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeTop relatedBy:0 toItem:viewsDict[#"l1"] attribute:NSLayoutAttributeTop multiplier:1 constant:0];
NSLayoutConstraint *con2 = [NSLayoutConstraint constraintWithItem:viewsDict[#"l1"] attribute:NSLayoutAttributeBottom relatedBy:0 toItem:viewsDict[#"b1"] attribute:NSLayoutAttributeTop multiplier:1 constant:0];
NSLayoutConstraint *con3 = [NSLayoutConstraint constraintWithItem:viewsDict[#"b1"] attribute:NSLayoutAttributeBottom relatedBy:0 toItem:viewsDict[#"l2"] attribute:NSLayoutAttributeTop multiplier:1 constant:0];
NSLayoutConstraint *con4 = [NSLayoutConstraint constraintWithItem:viewsDict[#"l2"] attribute:NSLayoutAttributeBottom relatedBy:0 toItem:viewsDict[#"b2"] attribute:NSLayoutAttributeTop multiplier:1 constant:0];
NSLayoutConstraint *con5 = [NSLayoutConstraint constraintWithItem:viewsDict[#"b2"] attribute:NSLayoutAttributeBottom relatedBy:0 toItem:viewsDict[#"l3"] attribute:NSLayoutAttributeTop multiplier:1 constant:0];
NSLayoutConstraint *con6 = [NSLayoutConstraint constraintWithItem:viewsDict[#"l3"] attribute:NSLayoutAttributeBottom relatedBy:0 toItem:viewsDict[#"b3"] attribute:NSLayoutAttributeTop multiplier:1 constant:0];
NSLayoutConstraint *con7 = [NSLayoutConstraint constraintWithItem:viewsDict[#"b3"] attribute:NSLayoutAttributeBottom relatedBy:0 toItem:viewsDict[#"l4"] attribute:NSLayoutAttributeTop multiplier:1 constant:0];
NSLayoutConstraint *con8 = [NSLayoutConstraint constraintWithItem:viewsDict[#"l4"] attribute:NSLayoutAttributeBottom relatedBy:0 toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
NSLayoutConstraint *con9 = [NSLayoutConstraint constraintWithItem:viewsDict[#"l1"] attribute:NSLayoutAttributeHeight relatedBy:0 toItem:viewsDict[#"l2"] attribute:NSLayoutAttributeHeight multiplier:.66 constant:0];
NSLayoutConstraint *con10 = [NSLayoutConstraint constraintWithItem:viewsDict[#"l1"] attribute:NSLayoutAttributeHeight relatedBy:0 toItem:viewsDict[#"l3"] attribute:NSLayoutAttributeHeight multiplier:.5 constant:0];
NSLayoutConstraint *con11 = [NSLayoutConstraint constraintWithItem:viewsDict[#"l1"] attribute:NSLayoutAttributeHeight relatedBy:0 toItem:viewsDict[#"l4"] attribute:NSLayoutAttributeHeight multiplier:.5 constant:0];
NSLayoutConstraint *con12 = [NSLayoutConstraint constraintWithItem:viewsDict[#"b1"] attribute:NSLayoutAttributeLeading relatedBy:0 toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1 constant:100];
NSLayoutConstraint *con13 = [NSLayoutConstraint constraintWithItem:viewsDict[#"b2"] attribute:NSLayoutAttributeLeading relatedBy:0 toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1 constant:100];
NSLayoutConstraint *con14 = [NSLayoutConstraint constraintWithItem:viewsDict[#"b3"] attribute:NSLayoutAttributeLeading relatedBy:0 toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1 constant:100];
NSArray *constraints = #[con1,con2,con3,con4,con5,con6,con7,con8,con9,con10,con11,con12,con13,con14];
[self.view addConstraints:constraints];

Resources