I am working on an app since a while, it is going well. However, this weekend I updated to Xcode 6 and now my app is behaving differently as opposed to before the update to Xcode 6.
I have a UITableView in my app which I rotate in viewDidLoad:
//Rotate playerCardsTable.
CGAffineTransform rotateTable = CGAffineTransformMakeRotation(-M_PI_2);
_playerCardsTable.transform = rotateTable;
_playerCardsTable.frame = CGRectMake(0, 0, _playerCardsTable.frame.size.width, _playerCardsTable.frame.size.height);
In Xcode before the update (Xcode 5) I had this view:
But now, after updating to Xcode 6, I have this view:
The tableview is rotated, ergo I have horizontal scrolling, but it seems like the frame is not changed correctly after rotation. It is 320 pixels high and 80 pixels wide and it should be the other way round. I don't know why, but it seems I cannot change the frame afterwards in code, in other words, I don't see any change after changing the width and height.
The tableview is added via the interface builder and holds custom cells:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSString *cardName = [[[[[Game game] localPlayer] playerCards] objectAtIndex:indexPath.row] fileNameCard];
cell.cardImage.image = [UIImage imageNamed:cardName];
Card *card;
card = [[[[Game game] localPlayer] playerCards] objectAtIndex:indexPath.row];
if(card.playable == IsPlayable){
cell.backgroundColor = isPlayableColor;}
else if (card.playable == IsNotPlayable) {
cell.backgroundColor = isNotPlayableColor;}
else if (card.playable == IsReallyPlayable) {
cell.backgroundColor = isReallyPlayableColor;}
//Rotate image to align with rotated tableview.
CGAffineTransform rotateImage = CGAffineTransformMakeRotation(M_PI/2);
cell.cardImage.transform = rotateImage;
cell.playableImage.transform = rotateImage;
cell.cardImage.layer.borderWidth = 2;
cell.cardImage.layer.borderColor = [UIColor clearColor].CGColor;
cell.cardImage.layer.shouldRasterize = YES;
cell.cardImage.layer.rasterizationScale = [[UIScreen mainScreen] scale];
cell.cardImage.layer.shadowColor = [UIColor blackColor].CGColor;
cell.cardImage.layer.shadowOffset = CGSizeMake(0, 1);
cell.cardImage.layer.shadowOpacity = 0.7;
cell.cardImage.layer.shadowRadius = 2.0;
cell.cardImage.clipsToBounds = NO;
return cell;}
To be clear; I did not change any code after the update, so the different behavior is caused by the update.
Hope you guys could help.
Thanks!
I ran into a very similar situation after upgrading iPad to ios8 just recently - Horizontal tables not appearing correctly.
I don't have a complete solution, but can tell you from research and testing that in my case it was due to the transform action and AutoLayout constraints on the UITableView not coordinating/cooperating. Evidently the way these two concepts work together changed somewhat in ios8.
A top constraint set in a storyboard becomes a left constraint after the transform (or right, never did figure it out properly).
Bottom line for me was that after trying a number of combinations of constraints, including removing them before transform, then adding constraints back in after the transform, I ended up converting from a UITableView to UICollectionView.
Related
I'm building an app whereas I've got a UICollectionView with a custom layout. I have a problem whereas I cannot tap some rows. Here's an overview over what happens
1) App starts and presents a UICollectionView
2) 3 test items are displayed, using the visual debugger in Xcode, you can see that there is something not right with the hierarchy
The red row can't be tapped now, but the yellow and green rows can.
3) User taps the yellow item, and segues to another page
4) User pops the shown UIViewController and returns to the UICollectionView whereas the viewWillAppear method reloads the UICollectionView like so:
[self.collectionView reloadData];
5) Re-entering the visual debugger shows that the hierarchy seems shifted, and the yellow row is now untappable, but the red and green rows can be tapped.
What could be the reason for this? I'll post any relevant code, ask for what parts you'd like to see.
Update
The UIViewController displaying the UICollectionView
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
ShareViewCell *view = [collectionView dequeueReusableCellWithReuseIdentifier:#"share" forIndexPath:indexPath];
view.share = [self.sharesController shareAtIndex:indexPath.item];
return view;
}
Custom cell:
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
self.leftBorderView = [UIView new];
[self.contentView addSubview:self.leftBorderView];
self.label = [UILabel new];
self.label.font = [UIFont boldSystemFontOfSize:10];
self.label.numberOfLines = 0;
self.label.lineBreakMode = NSLineBreakByWordWrapping;
[self.contentView addSubview:self.label];
}
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
self.leftBorderView.frame = CGRectMake(0, 0, 1.5, self.bounds.size.height);
CGSize labelSize = [self.label sizeThatFits:CGSizeMake(self.bounds.size.width - 10.0f, MAXFLOAT)];
NSTimeInterval durationOfShare = [self.share.endSpan timeIntervalSinceDate:self.share.startSpan] / 3600;
CGFloat middleOfCell = self.bounds.size.height / 2;
CGFloat yPositionToUse = middleOfCell - (labelSize.height / 2);
if (durationOfShare < 0.25)
{
[self.label setHidden:YES];// to small to be shown
} else if (durationOfShare > 0.5)
{
yPositionToUse = 8; // show it at the top
}
self.label.frame = CGRectMake(8, yPositionToUse, labelSize.width, labelSize.height);
}
Update 2
Is the UICollectionReusableView blocking tap of the UICollectionViewCell?
Ok,
First, you should be more precise in your question : you're using some code found on some question on StackOverflow.
The code seems to be this Calendar UI made with UICollectionView.
This is a sample code, quickly built for the sake of a StackOverflow answer (with a bounty). It's not finished, has no contributors, and you should try to read it and improve over it !
From your debugger's captures, it seems you have some view which overlays on top of your collectionView's cells.
From reading this code, I see it uses some supplementary view to present 'hour' blocks. This supplementary view class is HourReusableView. And it's the view coming on top on your debugger's captures
CalendarViewLayout is responsible to compute these supplementary views frame, as it does for colored event blocks (see method - (void)prepareLayout)
I might bet that these supplementary views's Z-order isn't predictable - all views have a default zIndex of 0. One way to fix it ? After line 67 of this file, set a negative zIndex on the UICollectionViewLayoutAttributes of the hour block. This way, you make sure hour supplementary views are always behind your cells, and don't intercept the cell's touch
I am new to IOS.
I have used collection view. I am using Prototype cell in it. I have not used Auto-Layout in my app.
I need to change frame of buttons and view in cellForItemAtIndexPath. But when I first scroll to move to next cell it not reflecting first time. I can only see blank with out any buttons or view. I have checked all the frames are correctly updating. Still I can not see any thing.
Though if I again go to previous call and come back it will seen properly.
Please find below code of cellForItemAtIndexPath
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
Feed *feed;
NSMutableAttributedString * string;
feed = [arrTop objectAtIndex:indexPath.row];
ShuffleCell *myCell = [collectionView
dequeueReusableCellWithReuseIdentifier:#"ShuffleCell"
forIndexPath:indexPath];
myCell.index = indexPath;
myCell.delegate = self;
// Below frame changes are not working
myCell.vwBack.frame = collect.frame;
myCell.imgShare.frame = CGRectMake(myCell.imgShare.frame.origin.x, myCell.imgShare.frame.origin.y, self.view.frame.size.width,self.view.frame.size.width );
myCell.lblText.center = CGPointMake(myCell.imgShare.frame.size.width / 2, myCell.imgShare.frame.size.height / 2);
myCell.lblText.frame = CGRectMake(20
, myCell.imgShare.frame.origin.y + 20, self.view.frame.size.width - 20,self.view.frame.size.width -20);
int totHeight = (collect.frame.size.height - self.view.frame.size.width);
myCell.vwBtn.frame = CGRectMake(myCell.vwBtn.frame.origin.x, collect.frame.size.height -totHeight, myCell.vwBtn.frame.size.width, totHeight);
[myCell.vwBtn setFrame:CGRectMake(myCell.vwBtn.frame.origin.x, collect.frame.size.height -totHeight, myCell.vwBtn.frame.size.width, totHeight)];
myCell.btnFavoriteBack.frame = CGRectMake(myCell.btnFavoriteBack.frame.origin.x, 0, myCell.btnFavoriteBack.frame.size.width, totHeight/3);
[myCell.btnFavoriteBack setFrame:CGRectMake(myCell.btnFavoriteBack.frame.origin.x, 0, myCell.btnFavoriteBack.frame.size.width, totHeight/3)];
myCell.btnShare.frame = CGRectMake(myCell.btnShare.frame.origin.x, (totHeight/3), myCell.btnShare.frame.size.width, totHeight/3);
myCell.btnReport.frame = CGRectMake(myCell.btnReport.frame.origin.x, (totHeight/3)*2, myCell.btnReport.frame.size.width, totHeight/3);
myCell.vwLikeBtn.frame = CGRectMake(self.view.frame.size.width - 82, (totHeight/3 - 32)/2, 82,32);
NSLog(#"Y :- %f",myCell.vwBtn.frame.size.height);
return myCell;
I have also attached screen shot of same.
Thanks for Help.
Adding this line should fix the problem. Let the subviews of cell translate their frames to constraints.
cell.label.translatesAutoresizingMaskIntoConstraints = YES;
I can't seem to get AutoLayout working on my Table View Cells.
On some cells it seems to work, and on others it seems to not work. Even cells of the exact same kind.
For example, on some cells the Description will be more than 1 lines worth of text and it will work correctly...
...Yet on other cells the Description will be more than 1 lines worth of text but only show 1 line of it with a bunch of empty space.
Can you help me figure out what I'm missing or doing wrong? Thanks!
I'm using this StackOverflow question to guide my process as a first-timer doing this: Using Auto Layout in UITableView for dynamic cell layouts & variable row heights
1. Set Up & Add Constraints
These are working well for the most part I believe.
2. Determine Unique Table View Cell Reuse Identifiers
I'm not totally sure if I need to worry about this part since I will always have a Headline, Time, and Description.
For iOS 8 - Self-Sizing Cells
3. Enable Row Height Estimation
I added this to viewDidLoad:
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 180.0;
UPDATE: Adding more info per Acey request
To be clear, I put constraints:
Headline: 15 left, 85 top, 15 right
Vertical Spacing between Headline and Time, of 10
Vertical Spacing between Time and Description, of 10
I Cmd clicked all three labels and added Leading Edges and Trailing
Edges
I pinned 20 between Description and the bottom of the Table View Cell
UPDATE 2: Solved
Answer below worked really well, but also any extra spacing was due to height set for cell being too large, so Xcode was automatically adding extra space to fill out height of cell since text labels didn't fill out the full height of the Table View Cell.
Let me know if you have any questions or need any help on this if you come across this and have the same problem.
Thanks everyone!
I haven't tried using the new iOS 8 mechanisms yet. But I have faced similar issues when I was doing this with iOS 6 / 7. After updating the app to iOS 8 it still works fine, so maybe the old way is still the best way?
I have some examples of my code here:
AutoLayout multiline UILabel cutting off some text
And here:
AutoLayout uitableviewcell in landscape and on iPad calculating height based on portrait iPhone
Long story short the pre iOS 8 way involved keeping a copy of a cell just for calculating the height inside tableView:heightForRowAtIndexPath:. But this wasn't enough for dealing with multi line UILabel's. I had to subclass UILabel to update the preferredMaxLayoutWidth every time layoutSubviews was called.
The preferredMaxLayoutWidth "fix" seemed to be the magic secret I was missing. Once I did this most of my cells worked perfectly.
The second issue I had only required me to set the content compression resistance and content hugging properties correctly, so for example telling the label to hug the text will mean it won't expand to fill the whitespace which will cause the cell to shrink.
Once I did these 2 things my cells now handle any font size, or any amount of text without any messy layout code. It was a lot to learn but I do think it paid off in the end, as I have a lot of dynamic content in my app.
Edit
After coming across a few issues of my own with iOS 8, i'm adding some more details to solve these very odd autoLayout bugs.
With the code I mentioned, it doesn't seem to work when the cell "Row Height" is not set to custom. This setting is found in IB by selecting the cell and clicking the autoLayout tab (where all the content compression resistance settings etc are). Press the checkbox and it will fill with a temporary height.
Second is, in my code I keep a local copy of a cell, and then reuse it many times inside the heightForRowAtIndexPath: method. This seems to increase the cell height by a lot every time it is called. I had to re-init the local copy by calling:
localCopy = [self.tableView dequeueReusableCellWithIdentifier:#"mycell"];
It appears the new Xcode 6 / iOS 8 changes are very much so not backwards compatible with iOS 7 and it seems to be managed quite differently.
Hope this helps.
Edit 2
after reading this question: iOS AutoLayout multi-line UILabel
I've come across another issue with iOS 7 / iOS 8 autolayout support!!! I was overriding layoutSubviews for iOS 8 I also needed to override setBounds to update the preferredMaxLayoutWidth after calling super. WTF have apple changed!
Seems to be an issue with the setting in IB for preferredMaxLayoutWidth, because iOS 7 can't use the automatic feature, if you use the same UILabel on multiple devices, its only going to use the 1 width. So UITableViewCell's on an iOS 8 tablet will be bigger because the same cell needs to have 2 lines on an iOS 8 iPhone.
Here is my attempt.
You could create a method/function that get's you the cellview that you need. Like so:
- (UIView *) getCellView {
UIView *cellView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.view.frame.size.width, 0.0f)];
cellView.tag = 1;
cellView.backgroundColor = [UIColor clearColor];
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(15.0f, 10.0f, 15.0f, 15.0f)]; //I assumed that was the size of your imageView;
imgView.image = [UIImage imageNamed:#"whatever-your-image-is-called"];
[cellView addSubview:imgView];
CGFloat xPadding = 15.0f;
CGFloat yPadding = 15.0f;
UILabel *headlineLabel = [[UILabel alloc ]initWithFrame:CGRectMake(xPadding, 0.0f, self.view.frame.size.width - (xPadding*2), 0.0f)];
headlineLabel.numberOfLines = 0;
headlineLabel.text = #"Red Sox season fell apart after World Series title (The Associated Press)";
[headlineLabel sizeToFit];
CGRect hFrame = headlineLabel.frame;
if(hFrame.size.width > self.view.frame.size.width - 31.0f)
hFrame.size.width = self.view.frame.size.width - 30.0f;
hFrame.origin.y = imgView.frame.size.height + yPadding;
headlineLabel.frame = hFrame;
UILabel *timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(xPadding, 0.0f, self.view.frame.size.height-(xPadding*2), 0.0f)];
timeLabel.text = #"4h";
//timeLabel.numberOfLines = 0; //uncomment if it will wrap on multiple lines;
[timeLabel sizeToFit];
hFrame = timeLabel.frame;
if(hFrame.size.width > self.view.frame.size.width - 31.0f)
hFrame.size.width = self.view.frame.size.width - 30.0f;
hFrame.origin.y = headlineLabel.frame.size.height + yPadding;
timeLabel.frame = hFrame;
UILabel *descriptLabel = [[UILabel alloc] initWithFrame:CGRectMake(xPadding, 0.0f, self.view.frame.size.height - (xPadding*2), 0.0f)];
descriptLabel.text = #"Boston (AP) -- To Boston Red Sox manager John Farrel, it hardly seems possible that just 11 months ago his team was celebrating the World Series championship on the field at Fenway Park.";
descriptLabel.numberOfLines = 0; //I would suggest something like 4 or 5 if the description string vary from 1 line to more than 5 lines.
[descriptLabel sizeToFit];
hFrame = descriptLabel.frame;
if(hFrame.size.width > self.view.frame.size.width - 31.0f)
hFrame.size.width = self.view.frame.size.width - 30.0f;
hFrame.origin.y = timeLabel.frame.size.height + yPadding;
descriptLabel.frame = hFrame;
cellView.frame = CGRectMake(0.0f, 0.0f, self.view.frame.size.width, descriptLabel.frame.origin.y + descriptLabel.frame.size.height + 15.0f /*some padding*/);
return cellView;
}
If you are using indexPath.row, you could just change the method name to be - (UIView *)getCellView:(NSIndex) *indexPath and it should work the same.
Then in your heightForRowAtIndexPath you could do
return [[self getCellView] frame].size.height;
or
return [[self getCellView:indexPath] frame].size.height
And in your cellForRowAtIndexPath you could just do the following
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryNone;
cell.textLabel.text = #"";
cell.detailTextLabel.text = #"";
}
[[cell viewWithTag:1] removeFromSuperview];
[cell addSubview:[self getCellView]; //or [cell addSubview:[self getCellView:indexPath]];
return cell;
Hope this helps. Let me know if something was unclear or not quite working. There are some stuff you may need to tweak to fit your usage, especially in cellForRowAtIndexPath, but that should be more or less everything you need to get going. Happy coding.
I have used GMGridView inside my app. When I change the orientation all is fine, but if I change it again and enter edit mode all the cells are shacking except the visible one in the lower-right corner.
The GmGridView is added as a subview to a controller and it also doesn't occupy the whole screen. I've tried destroying it and recreating it when a rotation notification occurs ... same behaviour. Also I have made a custom view with multiple buttons and labels that I have set as the the GMGridViewCell's contentView.
here's the code for the cellForItemAtIndex
- (GMGridViewCell *)GMGridView:(GMGridView *)gridView cellForItemAtIndex:(NSInteger)index {
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
GMGridViewCell *cell = [gmGridView dequeueReusableCell];
if (!cell) {
CGSize cellSize = [self GMGridView:gmGridView sizeForItemsInInterfaceOrientation:orientation];
cell = [[GMGridViewCell alloc] initWithFrame:CGRectMake(0, 0, cellSize.width, cellSize.height)];
cell.deleteButtonIcon = [UIImage imageNamed:#"delete_button"];
cell.deleteButtonOffset = CGPointMake(0, 0);
}
CustomGridViewCell *gridViewContent = [CustomGridViewCell getNewGridViewCellForOrientation:orientation];
ContentData *data = [wishlistContentArray objectAtIndex:index];
[gridViewContent setuprWithContent:data];
cell.contentView = gridViewContent;
if (!gridViewContent.delegate)
gridViewContent.delegate = self;
return cell;
}
There is a bug on GMGridView when it computes the number of visible cells.
In loadRequiredItems there are these two lines of code
NSRange rangeOfPositions = [self.layoutStrategy rangeOfPositionsInBoundsFromOffset: self.contentOffset];
NSRange loadedPositionsRange = NSMakeRange(self.firstPositionLoaded, self.lastPositionLoaded - self.firstPositionLoaded);
You have to change the range so it will return the smallest number that is a multiple of the number of items on your cell.
For example if you have 2 cells on a row, right now the code will return a range of {0,5} but it should be {0,6}.
Ive got a chat system in my app, and im attempting to make dynamic cells to have dynamic height according to how much text is in the cell, pretty common thing people try to do, however i cant get to get mine working properly.
Also the messages align to the right, the sender is supposed to be on the left and the reciever should be on the right... heres what i have done with the storyboard.
created a TableView with 2 dynamic prototypes, inside a UIViewControllerhere is the viewController for that... each cell has a label, one left one right, the whole right and left thing work... heres my issue. Its only pulling to the right for all, so basically my if isnt happening and my else is overruling. Heres a SS.
So i have two issues... Text wont have multiple lines... along with wont do dynamic height, also... if someone can point me i the right dirrection for getting sender and reciever to show on different sides.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSArray *myWords = [[getMessage objectAtIndex:indexPath.row] componentsSeparatedByString:#":oyr4:"];
if (myWords[1] == [MyClass str]){
static NSString *sender = #"sender";
UITableViewCell* cellSender = [_tableView dequeueReusableCellWithIdentifier:sender];
messageContentTo = (UILabel *)[cellSender viewWithTag:83];
self->messageContentTo.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.8];
self->messageContentTo.lineBreakMode = NSLineBreakByWordWrapping;
[self->messageContentTo sizeToFit];
messageContentTo.text = myWords[4];
return cellSender;
} else {
static NSString *reciever = #"reciever";
UITableViewCell* cellReciever = [_tableView dequeueReusableCellWithIdentifier:reciever];
messageContentFrom = (UILabel *)[cellReciever viewWithTag:84];
messageContentFrom.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.8];
messageContentFrom.lineBreakMode = NSLineBreakByWordWrapping;
messageContentFrom.font = [UIFont systemFontOfSize:22];
messageContentFrom.numberOfLines = 0;
messageContentFrom.text = myWords[4];
return cellReciever;
}
}
#pragma mark - UITableViewDelegate methods
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGSize size = [[getMessage objectAtIndex:indexPath.row]
sizeWithFont:[UIFont systemFontOfSize:22]
constrainedToSize:CGSizeMake(1000, CGFLOAT_MAX)];
return size.height + 15;
}
The left-right problem might be due to this:
if (myWords[1] == [MyClass str])
If myWords[1] is a string, you need to use isEqualToString: not "==" to compare it.
if ([myWords[1] isEqualToString:[MyClass str]])
As far as the label height not adjusting properly, it's hard to tell what's going on without knowing how your labels are set up. I usually do it by making constraints between the label and the top and bottom of the cell in IB. That way, when you change the height of the cell, the label will follow (and of course, set numberOfLines to 0). Also, in your sizeWithFont:constrainedToSize: method, the width you pass into CGSizeMake() should be the width of the label, not 1000.