Change UICollectionView cell size with custom Flow Layout - ios

I have five rows of cells each row containing four cells - 5x4. I am trying to accomplish almost an Apple watch like effect. The row in the center of the screen should have cell sizes of 100x100 and the rest return sizes of 80x80. When scrolled, the row moving away from the center should turn to 80x80 and the row moving into the center should turn 100x100.
I have implemented prepareLayout, layoutAttributesForElementsInRect, and layoutAttributesForItemAtIndexPath
So right now I have a center row of 100x100 cells and the rest 80x80.
To make it dynamic I implement shouldInvalidateLayoutForBoundsChange but nothing happens.
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
NSMutableArray *allAttributes = [NSMutableArray arrayWithCapacity:self.layoutInfo.count];
[self.layoutInfo enumerateKeysAndObjectsUsingBlock:^(NSString *elementIdentifier,
NSDictionary *elementsInfo,
BOOL *stop) {
[elementsInfo enumerateKeysAndObjectsUsingBlock:^(NSIndexPath *indexPath,
UICollectionViewLayoutAttributes *attributes,
BOOL *innerStop) {
// NSLog(#"%f, %f", newBounds.origin.x, newBounds.origin.y);
if ((newBounds.origin.y < [[UIScreen mainScreen]bounds].size.height/2-50) && (newBounds.origin.y > [[UIScreen mainScreen]bounds].size.height/2+50)) {
CGRect frame = attributes.frame;
frame.size.width = 100.0f;
frame.size.height = 100.0f;
attributes.frame = frame;
}else{
CGRect frame = attributes.frame;
frame.size.width = 80.0f;
frame.size.height = 80.0f;
attributes.frame = frame;
}
[allAttributes addObject:attributes];
}];
}];
return YES;
}

shouldInvalidateLayoutForBoundsChange: is called pretty frequently (whenever the view is resized, or scrolled...), so you probably don't need to be doing all that work there. If you know your cells will always have a fixed size and position then you can just return NO here.
As long as you have implemented prepareLayout, layoutAttributesForElementsInRect, and layoutAttributesForItemAtIndexPath, then make sure you also implement collectionViewContentSize to return the total size of the content.

Related

Implementing iPad General Setting Page

So far I have customized the tableview and implemented the iPad General Setting Page. Below is the code for tableview which will change frame accordingly. But the issue is when I insert/delete rows or section in the tableview. My tableviewcell backgroundview (not cell) width get shrinks. Any idea what wrong am I doing here?
- (void)setFrame:(CGRect)frame
{
if (UI_USER_INTERFACE_IDIOM()==UIUserInterfaceIdiomPad)
{
CGFloat inset =10;
frame.origin.x += inset;
frame.size.width -= 2 * inset;//The issue is in this line
}
[super setFrame:frame];
}
I found the simple solution to achieve this. No need of customizing UITableView subclass. Just take the outlet of tableview and set the frame and change the color of backgroundview. like that below:-
CGFloat tableBorderLeft = 10;
CGFloat tableBorderRight = 10;
CGRect tableRect = self.view.frame;
tableRect.origin.x += tableBorderLeft; // make the table begin a few pixels right from its origin
tableRect.size.width -= tableBorderLeft + tableBorderRight; // reduce the width of the table
yourTableView.frame = tableRect;
self.view.backgroundColor=[UIColor colorWithRed:(239/255.0f) green:(239/255.0f) blue:(244/255.0f) alpha:1.0];

Expand label that all text can fit in ( UITableViewCell )

I'm trying to create a tableviewCell with 1 label, to expand it when clicking on it and to collopase it again when again clicking on it. Now my animation on how to expand the cell is like this:
CGFloat targetHeightOfCell = [c.textLabel sizeOfMultiLineLabel].height;
_expandedState.height = #(targetHeightOfCell);
CGFloat difference = targetHeightOfCell - DEFAULT_CELL_HEIGHT;
CGFloat targetHeightOfContent = self.tableView.contentSize.height + difference;
[self.tableView beginUpdates];
[self.tableView endUpdates];
[UIView animateWithDuration:0.3f
animations:^{
CGRect frame = self.tableView.frame;
frame.size.height = targetHeightOfContent;
self.tableView.frame = frame;
}
completion:^(BOOL finished) {
}];
and in my heightForRowAtIndexPath I ofcourse return the right height. The cell expands but my calculation of sizeOfMultiLineLabel isn't correct. The text expands but still not all text is visible and so it's still appended by ...
This is my category on UILabel:
- (CGSize)sizeOfMultiLineLabel {
NSAssert(self, #"UILabel was nil");
//Label text
NSString *aLabelTextString = [self text];
//Label font
UIFont *aLabelFont = [self font];
//Width of the Label
CGFloat aLabelSizeWidth = self.frame.size.width;
//Return the calculated size of the Label
CGSize size = [aLabelTextString boundingRectWithSize:CGSizeMake(aLabelSizeWidth, MAXFLOAT)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{
NSFontAttributeName : aLabelFont
}
context:nil].size;
//Check if the height isn't smaller then the default one
if(size.height < DEFAULT_HEIGHT){
return CGSizeMake(aLabelSizeWidth, DEFAULT_HEIGHT);
} else {
return size;
}
}
What I want is that how long the text is, the label must expand so the user can see it.
I assume that your label has numberOfLines set correctly. If not, you probably want to set it to 0 to ensure that it can have as many lines as needed. You also need to ensure the wrapping mode is correct, e.g. UILineBreakModeWordWrap. Finally, depending on your cell layout there might be margins arount the label - so ensure you account for that when returning the height.

Dynamically size UITableViewCell's height

I have a UITableViewCell with multiple items inside. (Not just a textView so I cant follow this option.) I'm trying to dynamically size it's height based on the content it has inside.
The heights I will be changing, are a UITextView and a UIView. The textView will constantly be changing (at another method, if you'd like, I can post it). And the UIView will change if the user clicks a button:
Here is my code:
- (void)viewDidLoad
{
self.tableView.rowHeight = UITableViewAutomaticDimension;
}
- (IBAction)thisButton:(id)sender
{
CGRect frame = self.myView.frame;
frame.size.height = 50;
frame.size.width = self.myView.frame.size.width;
self.myView.frame = frame;
// update 'myView's constraint
self.viewHeight.constant = self.myView.frame.size.height;
self.tableView.rowHeight = UITableViewAutomaticDimension;
[myTableView reloadData];
}
Problem:
What happens is, when I press the button, the UIView's height gets
updated, but then everything else in the cell gets moved up, and the cell stays the same size.
When the UITextView's height changes, it doesn't pull everything else
down, and the cells height stays the same. Though the textView's
height does change and it just goes over everything else.
Constraints:
On the UITextView I have 3 constraints - 2 on each side, and 1 on top. The UIView has 3 constraints - 2 on each side, and 1 on the bottom.
I then have a constraint connecting the UIView to the textView.
You can dynamically manage UITableViewCell size by calculating a max height of your internal views:
NSArray *array = [[NSArray alloc] initWithArray:#[view1, view2, view3]];
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
__block CGFloat maxHeight;
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
UIView *view = obj;
if (view.bounds.size.height > maxHeight) {
maxHeight = view.bounds.size.height;
}
}];
return maxHeight;
}
Then in the end of your method that changes view's dimensions you must call [self.tableView reloadData]

UITableView bigger than screen inside UIPageView

Lately I'm pulling my hair out because of this. I'm still a rookie iOS Developer therefore a simple solution would be appreciated.
I have a UIPageViewController with three views, the middle view is a UITableView which i populate with data programmatically and try to make a layout like a grid. When the UITableView has too many data the information gets outside of the screen.
I've tried to disable auto layout and add the UITableView to a UIScrollView, enabling the UITableView to scroll horizontal. I've managed to do that but somehow the interaction is messy and most of the times when trying to horizontal scroll the table it catches the UIPageViewController interaction. Also the vertical scroll of the table does not respond well too.
Is there a known solution for this situation?
After testing a few things I ended up with a acceptable solution using autolayout.
So first I added a UIScrollView and added the UITableView inside the UIScrollView.
I've enabled the Paging and disabled the Bounce Horizontally in the UIScrollView.
In the UITableView I've basically disabled everything relative to bouncing paging and scrolling.
Since the data is being populated programmatically to be some sort of a grid I have to know the total width of the longest row for that I've created a few methods to calculate the maximum width and arrange the labels.
I also created constraints for the table Width and Height which I calculate and update after all the calculations are done.
-(void) ArrangeTable
{
for (int i= 0; i < [arrayTable count]; i++) {
for (int j=0; j< [[arrayTable objectAtIndex:i ] count]; j++) {
UILabel * nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 8.0, 95.0, 30.0)];
[nameLabel setText:[NSString stringWithFormat:#"%#",[[arrayTable objectAtIndex:i] objectAtIndex:j]]];
[nameLabel sizeToFit];
float widthIs = [nameLabel.text
boundingRectWithSize:nameLabel.frame.size
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{ NSFontAttributeName:nameLabel.font }
context:nil].size.width;
[self alignLabels:j currentWidth:[NSNumber numberWithFloat: widthIs]];
}
}
}
-(void) alignLabels:(int)colNumber currentWidth:(NSNumber*)labelWidth
{
if(colNumber >= [arrayLabelWidth count])
{
//Insert without position
[arrayLabelWidth addObject:labelWidth];
}else if ([labelWidth floatValue] > [[arrayLabelWidth objectAtIndex:colNumber] floatValue] )
{
[arrayLabelWidth replaceObjectAtIndex:colNumber withObject:labelWidth];
}
}
-(void) setMaxRowWidth
{
float widthTV =[[arrayLabelWidth valueForKeyPath:#"#sum.self"] floatValue] + ([arrayLabelWidth count]) * 10;
CGRect screenRect = [[UIScreen mainScreen] bounds];
float heightTV = [self tableViewHeight];
if(widthTV > screenRect.size.width)
{
tvWidth.constant = widthTV;
//svWidth.constant = valueTV;
}else{
if (UIDeviceOrientationIsPortrait(self.interfaceOrientation)){
//DO Portrait
tvWidth.constant = screenRect.size.width;
}else{
//DO Landscape
float calc = screenRect.size.width - self.view.frame.size.width;
tvWidth.constant = screenRect.size.width - calc;
}
}
tvHeight.constant = heightTV;
}
- (CGFloat)tableViewHeight
{
[tablePod layoutIfNeeded];
return [tablePod contentSize].height;
}

Adjust UICollectionView scrollOffset in intervals of the cell width

I have a collection view as such (scrolls sideways):
When the user scrolls, I want to adjust the table so it lines up perfectly around the static square.
I decided to do this using the scrollView when it ends dragging. However it is always just a little but off or at the very end completely off.
Where are my calculations going wrong?
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
if (![scrollView isKindOfClass:[IndexedCollectionView class]]) return;
IndexedCollectionView *collectionView = (IndexedCollectionView *)scrollView;
NSMutableArray *data = self.sectionsData[collectionView.index];
int cellsCount = data.count;
if (cellsCount==0) {
cellsCount++;
}
//Adjust cells so they line up correctly
CGPoint adjustedOffset = scrollView.contentOffset;
long interval = scrollView.contentSize.width/cellsCount;
long remainder = (long)adjustedOffset.x%interval;
long adjustment = interval-remainder;
adjustedOffset.x +=adjustment;
[UIView animateWithDuration:.3 animations:^{
[scrollView setContentOffset:adjustedOffset];
}];
NSInteger index = collectionView.index;
self.contentOffsetDictionary[[#(index) stringValue]] = #(adjustedOffset.x);
}

Resources