I have a set of views that represent cards, that should all fit into it's superview:
I want the container view to be aligned near the center of the screen, so I added these constraints for it:
Leading space with it's container;
Top space with the layout guide (the view controller is inside a navigation controller);
Center horizontally in the container;
Center vertically in the container.
This way the view that contains all the other small views is somehow pinned, and in landscape mode it gets more tiny so that it's subviews should shrink a lot vertically.
I added on all the subviews inside the view these constraints:
Same width;
Same height;
Leading, trailing, top and bottom space with any neighbor view.
To make it clearer this last step I'll also add this picture:
Now the first problem is that when the iphone simulator gets rotated to landscape, all the views are too much shrunk:
And I understand why: the top and vertical space from neighbors has a standard value, which maybe in landscape mode is too high relatively to the available vertical space, so they're too short. But how do I specify a constraint that takes a space in percentage (e.g.: vertical separation: 5% of the view's width)? The best to do in my case is to resize the subviews in the same way as if they were scaled to fit inside the new subview's bounds. How do I do that?
Another problem is that the app crashes if I rotate it to landscape and then to portait upside down:
2014-03-17 23:40:04.736 Matchismo[10284:60b] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSLayoutConstraint:0x109226d90 V:|-(NSSpace(20))-[PlayingCardView:0x109224330] (Names: '|':UIView:0x109226610 )>",
"<NSLayoutConstraint:0x109226e80 V:[PlayingCardView:0x109224330]-(NSSpace(8))-[PlayingCardView:0x109225080]>",
"<NSLayoutConstraint:0x109227150 V:[PlayingCardView:0x109225080]-(NSSpace(8))-[PlayingCardView:0x109225700]>",
"<NSLayoutConstraint:0x1092277e0 V:[PlayingCardView:0x109225d80]-(NSSpace(20))-| (Names: '|':UIView:0x109226610 )>",
"<NSLayoutConstraint:0x109227830 PlayingCardView:0x109225d80.height == PlayingCardView:0x109225700.height>",
"<NSLayoutConstraint:0x109227970 PlayingCardView:0x109225d80.height == PlayingCardView:0x109224330.height>",
"<NSLayoutConstraint:0x109227ba0 PlayingCardView:0x109225d80.height == PlayingCardView:0x109225080.height>",
"<NSLayoutConstraint:0x109227ce0 V:[PlayingCardView:0x109225700]-(NSSpace(8))-[PlayingCardView:0x109225d80]>",
"<NSLayoutConstraint:0x10922a0e0 UIView:0x109228f40.centerY == UIView:0x109226610.centerY + 32>",
"<NSLayoutConstraint:0x10922a130 V:[_UILayoutGuide:0x109229020]-(14)-[UIView:0x109226610]>",
"<_UILayoutSupportConstraint:0x109224980 V:[_UILayoutGuide:0x109229020(64)]>",
"<_UILayoutSupportConstraint:0x109221ff0 V:|-(0)-[_UILayoutGuide:0x109229020] (Names: '|':UIView:0x109228f40 )>",
"<NSAutoresizingMaskLayoutConstraint:0x10923eef0 h=--& v=--& V:[UIView:0x109228f40(271)]>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x109227ce0 V:[PlayingCardView:0x109225700]-(NSSpace(8))-[PlayingCardView:0x109225d80]>
Break on objc_exception_throw to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
(lldb)
But I can't see any conflicting constraints.
Solution
Exploiting ReedD's answer, I decided to use a collection view. This is the solution I came around with (in my case I need to use 16 cards in a 4x4 collection view, but the code can be rearranged to work in a generic way):
- (NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return 4;
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 4;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell* cell= [collectionView dequeueReusableCellWithReuseIdentifier:#"reuseIdentifier" forIndexPath:indexPath];
<Initialize the card view and add it to the cell>
return cell;
}
- (CGSize) collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return CGSizeMake(collectionView.contentSize.width/4.0 -5.0,collectionView.contentSize.height/4.0 -5.0);
}
- (UIEdgeInsets) collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
return UIEdgeInsetsMake(5, 5, 5, 5);
}
To address your last problem:
When ever you see the following in your crash log -
"<NSAutoresizingMaskLayoutConstraint:0x10923eef0 h=--& v=--& V:[UIView:0x109228f40(271)]>"
-- your first hunch would be to go and write this line for the view having memory address 0x109228f40
self.view.translatesAutoresizingMaskIntoConstraints= NO;
Have you tried to put a height constraint on the first card and set it to >= 20 (or any minimum value that could be reasonable for you)?
Also, are the score and the new game button constrained in any way to the card container? If so, you should try to remove any vertical spacing between them.
Have you thought of possibly using a UICollectionView? Then with the datasource delegate you could do something to the effect of this:
#pragma mark - UICollectionViewDatasource
- (CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
if (UIDeviceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
// Landscape
return CGSizeMake(50, 100);
}
// Portrait
return CGSizeMake(100, 200);
}
Related
I've tried some solutions found on stackoverflow such as
self.collectionView.clipsToBounds = NO;
[self.scrollView setContentOffset:CGPointMake(0, 0)];
And nothing is working.
My collection view has a cell in it and should display one cell at a time. I'm using paging and evertyhing is working fine except that when you go to the second page it cuts a little of the cell off by the screen. And every page you go after that cuts more of the cell of till you get to the last page where half the cell is cut off. I'm doing this on iphone.
What is happening is, your cell is not centered, so as you swipe between them, and the UICollectionView dynamically figures out the spacing of each cell, it gradually becomes more and more off centre, getting the effect you explain.
Ensure that your UICollectionViewCell + UICollectionViewLayout insets equal the width of the contentView of your UICollectionView.
e.g.
On a 320.0f,100.0f UICollectionView. Your cell could be 300.0f,90.0f. And your UICollectionViewLayout (if using a flowLayout), could have the edge insets set to 5.0f,10.0f,5.0f,10.0f.
E.g.
[(UICollectionViewFlowLayout *)collectionView.collectionViewLayout setItemSize:CGSizeMake(320.0f, 100.0f)];
[(UICollectionViewFlowLayout*)collectionView.collectionViewLayout setSectionInset:UIEdgeInsetsMake(5.0f, 10.0f, 5.0f, 10.0f)];
It was min spacing for lines was on 10 so I set it to 0. I think thats a bug in xcode 5 because my collection view is scrolling horizontal not vertical so one would think it would be my min spacing for cells would be wrong
Please make each item in a section, NOT all items in one section.
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return [self.items count];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return 1;
}
That's done.
This question already has answers here:
tableFooterView property doesn't fix the footer at the bottom of the table view
(10 answers)
Closed 8 years ago.
I have a UITableView with a footer, filled with a tabBar in a custom view, done using the following code:
- (CGFloat)tableView:(UITableView *)tableView
heightForFooterInSection:(NSInteger)section {
//differ between your sections or if you
//have only on section return a static value
return 49;
}
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
if(footerView == nil) {
//allocate the view if it doesn't exist yet
footerView = [[UIView alloc] init];
[footerView addSubview:self.tabBarView];
}
//return the view for the footer
return footerView;
}
Which is working lovely, apart from when the table has less rows than are needed to fill the screen, this causes the footer to move up the screen, as the table no longer creates empty rows, due to having a footer.
So, does anyone know of a way to either lock the custom footer to the bottom of the screen, or, to make the tableView create empty rows as it used to do?
Thanks!
Gareth
Unfortunately I don't think there is an easy way to do this, other than some view hierarchy trickery. When the contentSize of your UITableView is less than the frame size, you assign the footer view to self.view and position manually. When the contentSize of your UITableView is greater than the frame size, you use viewForFooterInSection. Let me know if this isn't clear or if you'd like to see some sample code on how to do this.
I have the same problem for uitableview controller, I solved this by adding view in the window and remove this object in viewWillDisappear.
My Solution link
The cell is not indenting according to is level.
I have configured indentation width inside cellForRowAtIndexPath.
- (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath{
NSInteger theLevel=0;
if ( indexPath.row==1) {
theLevel=5;
}
return theLevel;
}
In the below image I changed frame frame of button and label according to its level.
If you have added subviews to the cell then you need to set the subviews' autoresizing masks so that they are repositioned when the contentView size gets changed.
Please look at this answer.
I have a UICollectionView that contains some large UICollectionViewCells. These cells are so large that they completely fill the UICollectionView bounds and extend off screen.
The problem is that the large cells are removed from the UICollectionView and queued for reuse while they are still displayed, resulting in a blank view. If I continue scrolling on the blank UICollectionView area, eventually the final portion of the cell appears and the start of the next cell appears in exactly the right place.
I've effectively disabled cell reuse and the problem still occurs apparently because the UICollectionView thinks that the cell is no longer displayed since no corner is within the bounds of the collection view.
To demonstrate make a collection view that is a single column and have a cell that is 10000px tall, when scrolling over the very tall cell it will disappear after about 1000px of content is scrolled off the screen and will reappear to display the final 1000px of content for the cell.
You can download a simple prototype app that displays this problem at: http://jack.cox.s3.amazonaws.com/collectionviewprototype.zip
This issue happened for me along with this warning in the debug log:
the behavior of the UICollectionViewFlowLayout is not defined because:
the item height must be less that the height of the UICollectionView
minus the section insets top and bottom values.
It seems that the out of the box UICollectionViewFlowLayout does not support cells larger than the screen.
This issue is being tracked as radar #12889164
Try this fix if you are still looking for a quick solution:
When a cell is detected to have a bigger height than the collectionview holding it, the collection view simply needs to be larger than the cell. So set the collecitonView frame to be bigger and correct the content and indicator insets.
- (void)updateCollectionViewForLargeCellHeight:(CGFloat)largeCellHeight {
CGFloat currentCollectionViewHeight = CGRectGetHeight(self.collectionView.frame);
if (largeCellHeight > currentCollectionViewHeight) {
self.collectionViewBottomConstraint = -largeCellHeight;
//This is the bottom constraint of the collectionView to its superview.
//If you are not using constraints, simply set the frame for the collectionView
UIEdgeInsets edgeInset = UIEdgeInsetsMake(0, 0, largeCellHeight, 0);
self.collectionView.contentInset = edgeInset;
self.collectionView.scrollIndicatorInsets = edgeInset;
[self.collectionView needsUpdateConstraints];
}
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGSize size;
[self updateCollectionViewForLargeCellHeight:size.height];
return size;
}
I have used UICollectionView with cells of the size of the screen. For iPad the cell size was 768x1024. And haven't found any issues. Please make sure that you have set the Min Spacing For Cells and For Lines are 0 Also return correct CGSize and UIEdgeInsets as follows
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGSize retval;
retval.width=maxWidth; //768 in case of iPad
retval.height=maxHeight; //1024 in case of iPad
return retval;
}
- (UIEdgeInsets)collectionView:
(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
return UIEdgeInsetsMake(0 , 0, 0, 0);
}
I have a plain (not grouped) tableView with custom cells, and when I hit the Edit button, the cells indent. I don't want that, I want the deletion sign to lay right on top of my cell.
I tried shouldIndentWhileEditingRowAtIndexPath and also cell.shouldIndentWhileEditin = NO; as well as cell.indentionLevel = -3, but both won't have any effect. Any idea why?
Could this be due to my setup? I followed this tutorial, and I also tried a setup like Davyd suggested here, but the last did not only still indent my cells, it made it even worse, as the cells were indented, when I press Done.. and I can't get the background image to cover the whole cell...
So, anyone knows how to stop custom cells in a plain tableview from intending, while still showing the delete and move sign?
//EDIT:
btw, I build the custom cell in IB. I can take away the checkmark saying "Indent while Editing", it doesn't care. I can change the values for indention level and width, no effect. If i change the editing accessory, it happily displays it.
Hope that helps..
Thanks!
After a lot of research and trying pixel by pixel, it turned out, I needed to use -(void)layoutSubviews to "transit" from the original state to the original size.. If someone else ever needs to do that, here's my code, placed in my CustomCell.m:
- (void)willTransitionToState:(UITableViewCellStateMask)aState
{
[super willTransitionToState:aState];
self.state = aState;
}
- (void)layoutSubviews
{
[super layoutSubviews];
// no indent in edit mode
self.contentView.frame = CGRectMake(0,
self.contentView.frame.origin.y,
self.contentView.frame.size.width,
self.contentView.frame.size.height);
if (self.editing )
{
NSLog(#"subview");
float indentPoints = self.indentationLevel * self.indentationWidth;
switch (state) {
case 3:
self.contentView.frame = CGRectMake(indentPoints,
self.contentView.frame.origin.y,
self.contentView.frame.size.width +124,// - indentPoints,
self.contentView.frame.size.height);
break;
case 2:
// swipe action
self.contentView.frame = CGRectMake(indentPoints,
self.contentView.frame.origin.y,
self.contentView.frame.size.width +75,// - indentPoints,
self.contentView.frame.size.height);
break;
default:
// state == 1, hit edit button
self.contentView.frame = CGRectMake(indentPoints,
self.contentView.frame.origin.y,
self.contentView.frame.size.width +80,// - indentPoints,
self.contentView.frame.size.height);
break;
}
}
}
Hope that helps :)
None of the above works for me, but this did:
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleNone;
}
This way your saying to the UITableView that you dont want any native styling when in edit mode, and instead you can take care of it yourself.
Have you checked that the delegate method tableView:shouldIndentWhileEditingRowAtIndexPath: is being called when you edit the cell?
The only time I used the tableView:shouldIndentWhileEditingRowAtIndexPath: delegate method
it worked fine.
// Override to prevent indentation of cells in editing mode (in theory)
- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath {
return NO;
}
Try changing the autoresizing mask of your content view or the elements inside your cell. The indent is because when your cell enters editing mode the content view is resized to show the accessories and the content moves with it.
It's hard to give specific advice without knowing what's in your cell, but you want to look at the fixed left or right margins.
I had the same problem. The reason is: They're not indented but auto-resized. The remove button is shown an the cell's view (and its subviews) are resized.
Solution is: Set the autosizing behavior of the custom table cell's subviews (the labels or whatever you placed on it) in InterfaceBuilder/Xcode as you need it.
I just realized that if you connect a UIView to the backgroundView outlet, it doesn't move at all. That combined with autoresizing flags is really all you need, I think.
Just tried this on iOS 6.
The shouldIndentWhileEditingRowAtIndexPath delegate method now works on plain table view as well.
----Edited-----
Well, as it turned out, it doesn't indent only if allowsMultipleSelection = YES