Size of UICollectionView is sometimes wrong - ios

Here is my problem: I'm developing a Kodi Remote on iOS (http://forum.kodi.tv/showthread.php?tid=235973) and everything run pretty well (a classic development) but I'm having a behavior that I can't explain.
I have multiple view controllers; in one of them (a UIViewController containing a UICollectionView), I implemented this delegate method :
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
CGSize originalSize = [(UICollectionViewFlowLayout *)collectionViewLayout itemSize];
CGSize collectionViewSize = [collectionView frame].size;
CGFloat interItemSpacing = [(UICollectionViewFlowLayout *)collectionViewLayout minimumInteritemSpacing];
UICollectionViewFlowLayout *collectionViewFlowLayout = (UICollectionViewFlowLayout *)[[self collectionView] collectionViewLayout];
UIEdgeInsets sectionInset = [collectionViewFlowLayout sectionInset];
CGFloat usableWidth = (collectionViewSize.width - ((kColumns - 1) * interItemSpacing) - sectionInset.left - sectionInset.right);
CGFloat width = usableWidth / kColumns;
CGFloat height = width * originalSize.height / originalSize.width;
return CGSizeMake(width, height);
}
I get the collection view frame (bounds is the same, of course, I don't need origin) and it works great. I get on an iPhone 5 simulator 288 width for a no matter height.
BUT, in a copy-paste of the view controller in the storyboard, I get a 304 width. The weirdest behavior is that with FLEX, and after the render, the collection view measures 288 pixels.
The "only" difference is that the first view controller is just pushed, the second is contained in a UITabBarController.
PS : the first screen I'm talking about are the 3rd & 4th screenshots.
If someone has an explication to this, I'll take it ;)
Update 1 :
If I invalidate the collection view layout in - (void)viewDidLayoutSubviews, items are correctly drawn BUT there is like a flash because the whole collection view is redrawn (the header & items below).

- (CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout*)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return CGSizeMake(self.collectionView.frame.size.width,
self.collectionView.frame.size.height);
}
and uncheck "Constrain to margins" and click on all dotted red constrain
so -8 padding will be gone in iPhone 6+

Storyboard
It makes a lot of sense when you approach the problem with a Storyboard. First, you will get a clear view of what you are designing. Second, you get to build your application with exactly 0 lines of code, leading to 0 bugs.
The parent UITabBarController and a set of UINavigationController is a common design pattern, and only when you implement it as follow do you get a proper UI behavior.
Code
You do not have to use a Storyboard to create the hierarchy above. You can embed each UIViewController in a UINavigationController programatically.
► Find this solution on GitHub and additional details on Swift Recipes.

Related

In my app I used uicollectionview then how to make difference between two cell same in all iPhone models?

In my app, I used UIcollectionview then how to make difference between two cell same in all iPhone models. and auto layout is turned on I tried many things but the issue is not resolved my app looks perfect on iPhone 5s but It shows more difference between two cells in iPhone 6s and 6s plus.enter image description here
You need to calculate the cell dynamically according to the screen size. I have faced such situation and fixed it. You can try this, might be help you...
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(width, height);
}
get width and height
-(void)getDynamicCellSize {
width = (SCREEN_SIZE.width - 50)/3;
height = (SCREEN_SIZE.height - (135+self.tabBarController.tabBar.frame.size.height)-29)/3;
} // 135 means the navigation bar height + segment control (I have in my design)
CGFloat width, height;// Declare globally
Call getDynamicCellSize method in viewWillAppear or didLoad
OR
Use this code in sizeForItemAtIndexPath method in 'SWIFT'
let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
let cellWidth = (CGRectGetWidth(collectionView.frame) - (flowLayout.sectionInset.left + flowLayout.sectionInset.right) - flowLayout.minimumInteritemSpacing) / 2
return CGSize(width: cellWidth, height: cellWidth)
Happy coding...

Ipad UX creating news Board using UIcollectionView

I'm new on IOS development and I'm working on Ipad app for News Blog and I want to create Home screen look like this :
So my question is what's the best way to do that?
I mean, Should I create reusable view ( two views : one for the big size
post and the second for the small size post), then I add those view to my UIViewController in storyboard.
Or, create one reusable view with the big size and when I added it to storyboard I resize it (I'm not sure if this solution can be achieved and create UIview with dynamic size )?
Or, I create those view directly in my storyBoard and no need for using reusable component ?
Update
Thank's To Larme solution I've used UIcollectionView. But the problem I can't push the two first small row, in red circle, to top of screen and add under them two other small Cell
I want to have 4 small cells on the right big cell
This is my code:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGSize imgSize = CGSizeMake(230, 160);
if (indexPath.row == 0) {
//double the size for the first cell
imgSize = CGSizeMake(490, 360);
}
return imgSize;
}
-(UIEdgeInsets)collectionView:
(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
return UIEdgeInsetsMake(5, 5, 5, 5);
}

How to get Storyboard to support dynamically sized cells in UICollectionView?

I have the following layout in my dynamically-sized Storyboard:
However, when running the application (depending on the orientation), cells look like the following:
This is to be expected, because the previous cells had static heights and widths.
However, I'd like the cells to resize dynamically based on the device width (height can remain static for this purpose). The CollectionView itself resizes properly, because it's pinned to its superview, so how can cells be overridden with auto-layout constraints (vs. the static cell sizes dictated from the UICollectionView).
The simplest way seems to be over-riding the cell size via the delegate within my main View Controller:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
float cellWidth = (self.view.frame.size.width / 2) - 35;
float cellHeight = cellWidth * 190.0f / 270.0f;
return CGSizeMake(cellWidth, cellHeight);
}
I'd prefer to have as much display logic in the storyboard, but I suppose this is room for future improvement for Apple.
The cell sizes aren't determined by auto layout, they're set by the itemSize property of the layout object. If you only have one cell type, then you only need one cell in the collection view in IB. When your collection view is loaded, you can set that size in code, based on the width of the collection view. I've done it like this,
-(void)viewWillLayoutSubviews {
self.layout.itemSize = CGSizeMake(self.collectionView.bounds.size.width/2.5, 100);
}

UICollectionView, simply fit cell to width?

Here's a simple UICollectionView in yellow
The red arrow sets the width of the cell. (TBC: clicking on the pink cell: for 'size' select 'Default', then what you set at the red arrow becomes the size of the cell.)
Example, for an upright iPhone set width to 320.
But this seems crazy ... surely I can set the cell width based on the width of the UICollectionView?
There's no problem autosizing the view itself ..
That works fine.
But it seems crazy that I can't set the width of the CELL to be "same as the view". It seems hard to believe one has to set it manually, in code?
TBC In other words, as Nikita points out,
-(CGSize) collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return self.view.frame.size;
}
(indeed you also typically need this ...)
-(UIEdgeInsets)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout*)collectionViewLayout
insetForSectionAtIndex:(NSInteger)section
{
return UIEdgeInsetsMake(0,0,0,0); //t,l,b,r
}
In fact - you have to do that in code?! There's no way to do that in Storyboard, with autolayout, or anything else?!
I think you need to take a look at
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
of UICollectionViewLayout where you can set size based on orientation and current frame.
It would indeed seem that you simply have to do this in code.
There is a simple way to do this. Here is the swift code. Assumes you want one item wide and in the example my cells are 64 pts high, and I want 8 pts for side margins, and finally collectionView is the name of your UICollectionView.
Insert this into viewDidLoad :
let layout = collectionView?.collectionViewLayout as! UICollectionViewFlowLayout
layout.itemSize = CGSize.init(width: (UIScreen.main.bounds.width - 16), height: 64.0)
This will set all the cells to default to that size. You can still override individual cells if you subclass UICollectionViewLayout or UICollectionViewFlowLayout, as mentioned in other answers. But if you simply want to have more table view like behavior this should work.
I my case cell inset making extra shapes

UICollectionView Layout Issue

I am using UICollectionView using the flow layout.
I have made a custom UICollectionViewCell for the same.
But on running the project the console keeps on throwing this error-
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.
I have made sure that the size of the cell is correct
Has anyone been able to resolve this issue.
I found that, using storyboards, you have to go into the storyboard and click on the overall View Controller (the view should be highlighted in blue) and go to the Attributes Inspector (the fourth tab on the right side of the screen) and unchecking the "Under Top Bars", "Under Bottom Bars", and "Under Opaque Bars." This cleared up the issue for me, and it cleared it for my coworkers as well.
I've been having some issues when the using a collection view to present full screen view controllers.
Basically at run time, it'll ask for the size of the item and simply return the size of the collection view:
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return self.collectionView.frame.size;
}
I know this is a very late reply, but I have also experienced this.
Inside my view controller, let's call it MyViewController I have a UICollectionView that is using custom UICollectionViewCells. I'm implementing the method collectionView:layout:sizeForItemAtIndexPath where I am returning a item size that is dependent on the height of the UICollectionView.
MyViewController is made in a storyboard using autolayout to control the height of the UIViewController.
The cells look fine when in portrait mode, but did not when in landscape mode.
I solved my issues by invalidating the UICollectionViewLayout of my UICollectionView inside the viewWillLayoutSubviews method.
- (void)viewWillLayoutSubviews {
[self.myCollectionView.collectionViewLayout invalidateLayout];
}
Earlier I had tried to invalidate the layout inside willAnimateRotationToInterfaceOrientation:duration, but it didn't work. The reason for this is probably that the sizes for all the views are not calculated yet, so we have to wait until autolayout has finished its magic. I refer to this SO thread.
Update for Swift 4.0:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.myCollectionView.collectionViewLayout.invalidateLayout()
}
Had this issue myself a few times when trying to create collection views with fullscreen cells. My problem was caused by laying out for 4" screen in IB/Storyboard and specifying 320*568 for item size, but running in the simulator using 3.5" screen, which has a height of 480. The solution is to specify your item size in code with something like:
UICollectionViewFlowLayout *layout = (id) self.collectionView.collectionViewLayout;
layout.itemSize = self.collectionView.frame.size;
This ensures that the cell size is set correctly at runtime.
If you are using storyboards and auto layout, debugging this kind of problems is really hard...
I had similar problem when trying to display UICollectionViewCell fullscreen on iPhone.
Most of the time issue is with the size of the cell set in
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)
or directly on flowLayout.itemSize.
But... try:
Select the ViewController:
Then uncheck Extend Edges options:
And now try setting your auto layout constraints.
Good luck.
That fixed my problem:
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(self.collectionView.frame.size.width, self.collectionView.frame.size.height - 70);
}
U can see padding value from top and bottom on this screen:
Be sure to set the right autoresizing mask for UICollectionView and UICollectionviewcell.
This fixed my problem for iPhone 4 Simulator
For me I am using AKPickerView inside of a custom UIView which was giving me a slew of warnings like the ones mentioned here. All I did to eliminate the warnings was to Un-Check the box called 'AutoResize Subviews' in the Attributes Inspector for my custom UIView. That silenced the warnings so hard, I thought my logger stopped working.
What caused this for me was that I was trying to set my cells to the size of the superview rather than the size of the UICollectionView. Simply replaced the superview frame with the UICollectionView frame.
I had this problem when I was need full screen cells in collectionView. So I resolved it with this decision.
Cell size is equal collection view size
2. Change in storyboard "Inset from:" from Safe Area to Content Inset
Change "Content Insets" to Never
I just ran into the same error message, but for me the issue was that when I received new data from the api and tried to refresh my collectionView, the method that called [collectionView reloadData] wasn't calling it on the main thread.
Hope this helps someone...
I have same issue
the behavior of the UICollectionViewFlowLayout is not defined because:
the item height must be less than the height of the UICollectionView minus the section insets top and bottom values.
I solved this issue by checking the values in section Insets.And for fix cell size I have used below code.
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
CGSize result = [[UIScreen mainScreen] bounds].size;
CGFloat scale = [UIScreen mainScreen].scale;
result = CGSizeMake(result.width * scale, result.height * scale);
CGFloat cellWidth = [[UIScreen mainScreen] bounds].size.width - 20;
CGFloat cellHeight = [[UIScreen mainScreen] bounds].size.height - 120;
return CGSizeMake(cellWidth, cellHeight);
}
If you're loading a collectionView via a container view + UICollectionViewController, you may have set a height constraint on the container view which is constraining the height of the collection view itself. In my case, I found the collectionView.contentSize was taller than the constraint I had set on my container view.
Obviously this is a bit of an edge case but just in case you have a similar setup, I figured I'd add this comment.
For me the solution was to set Estimate Size (in the storyboard size attributes pane) for the collection view to None.
It's complaining that the item height is bigger than the height of the collectionView. So it's sticking out.
So you have to make the height the same or less.
Like this:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.height, height: collectionView.frame.height)
}
Using the height for the width makes it a square. This is assuming inset is 0 which it should be if you haven't done anything.
None of the above fixes did it for me.
I fixed it with this
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{CGFloat currentWidth = collectionView.frame.size.width;
UIEdgeInsets sectionInset = [(UICollectionViewFlowLayout *)collectionView.collectionViewLayout sectionInset]; //here the sectionInsets are always = 0 because of a timing issue so you need to force set width of an item a few pixels less than the width of the collectionView.
CGFloat width = currentWidth - 10;
}

Resources