How to make UICollectionView auto layout - ios

How to make UICollectionView auto layout such that the number of items appearing on the screen at some moment is decided according to total number of items. I actually want to make such layout that if i have 9 items than all nine items should be displayed at the same time on the collection view. if items are 8 or something else then the size of the items should increase in such a manner that they still consume complete area of the collection view.
the condition i have plotted is as under:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
var cell = collectionView.cellForItemAtIndexPath(indexPath) as CollectionViewCell
if((cell.selected) && (indexPath.row%2 == 0))
{
cell.backgroundColor = UIColor.whiteColor()
imageview.image = UIImage( named: a[indexPath.row])
}
else if((cell.selected) && (indexPath.row%2 != 0))
{
cell.backgroundColor = UIColor.whiteColor()
imageview.image = UIImage( named: a[indexPath.row])
}
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
var abc : CGSize!
if(a.count == 1){
abc = CGSize(width: self.view.frame.width, height: self.view.frame.height/2)
}
else if(a.count == 2) {
abc = CGSize(width: self.view.frame.width/2, height: self.view.frame.height/2)
}
else if (a.count > 2) && (a.count<6){
abc = CGSize(width: self.view.frame.width/3, height: self.view.frame.height/2)
}
else {
abc = CGSize(width: self.view.frame.width/3, height: self.view.frame.height/5)
}
return abc
}
func collectionView(collectionView: UICollectionView, shouldHighlightItemAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool
{
return true
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return a.count
}
func collectionView(collectionView: UICollectionView, shouldShowMenuForItemAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}

From your question I understand that you want to very the UICollectionView's cell sizes so they cover the whole screen.
There are various ways you could achieve what you want, but the key will be to work with the UICollectionViewLayout.
For example, the UICollectionViewFlowLayout has an itemSize property.
Based on your content (for the cells), you could try calculating how much space they will occupy and then set the itemSize property of the FlowLayout. If all the cells are supposed to be the same size, this should be good enough.
You could also use the delegate method collectionView:layout:sizeForItemAtIndexPath: to set the size of each cell.
If that's not sufficient, because you want to change the arrangement of the cells as well for example, you will need to subclass the FlowLayout in order to calculate the size for each item. Have a look at this:
https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/UsingtheFlowLayout/UsingtheFlowLayout.html
Ultimately, you need to start by calculating how many cells you'll need based on your content first.

Related

UICollectionView overlapping labels

I am using a collectionview and the first data load is working fine. The problem occurs when the data is load a second time the labels overlaps because the old labels appear to still exist.
Below is my collectionview code, thanks in advance.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let customCell = collectionView.dequeueReusableCell(withReuseIdentifier: "customCellIdentifier", for: indexPath)
customCell.layer.borderColor = UIColor.black.cgColor
customCell.layer.borderWidth = 1
// changed these lines here
if cellsLabels[indexPath.row] != nil {
customCell.willRemoveSubview(cellsLabels[indexPath.row]!)
}
//to these lines here and the problem was solved
let maybe = customCell.subviews
for i in 0 ..< maybe.count {
maybe[i].removeFromSuperview()
}
let c
ommentLabel = UILabel()
commentLabel.text = commentsArray[indexPath.row]
commentLabel.frame = CGRect(x: 0, y: 50, width: 200, height: 30)
customCell.addSubview(commentLabel)
self.cellsLabels[indexPath.row] = commentLabel
if indexPath.row == commentLoadTracker*10 - 1 {
print("working doomfist")
}
return customCell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return commentsArray.count
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
var cellSize = CGSize()
cellSize.width = self.commentView.frame.width
cellSize.height = 100
return cellSize
}
customCell.willRemoveSubview
You are calling willRemoveSubiew instead of removeFromSuperview
if cellsLabels[indexPath.row] != nil {
cellsLabels[indexPath.row]!.removeFromSuperview()
}
There is no need to call willRemoveSubview, UIKit calls it for you, it only exists so that it can be
Overridden by subclasses to perform additional actions before subviews are removed from the view.

UICollectionView on UIScrollView freezes

I have an UIViewController, which contains UIScrollView, which contains a few elements, including UICollectionView. UICollectionView is used to display N buttons in fixed frame. UICollectionView scrolling is disabled. Still, my UIScrollView scrolls not as smoothly as it scrolls without UICollectionView. What my be the problem? Does it have something to do with reusable cells?
code:
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 9
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("tagsCell", forIndexPath: indexPath) as! TagsCollectionViewCell
cell.backgroundColor = UIColor.clearColor()
cell.tagsButton.setTitle("Test", forState: .Normal)
cell.tagsButton.sizeToFit()
return cell
}
func collectionView(collectionView: UICollectionView!, layout collectionViewLayout: UICollectionViewLayout!,
sizeForItemAtIndexPath indexPath: NSIndexPath!) -> CGSize
{
let element = "Test" as NSString
let stringSize = element.sizeWithAttributes([NSFontAttributeName: UIFont.systemFontOfSize(17.0)])
return CGSize(width: stringSize.width + 25, height: 29)
}
If the frame of the scrollView and collectionView is same then it must be a problem to differentiate, which one to call. May be that is why it's creating an issue with the collectionView.

Sections with different cell size with UICollectionView

I have to create a view in a Swift project with two differentiate sections.
The first one has seven cells with square shape. The second one has three cells with rectangular shape. Data inside cells are static.
In all cases, the sections should fit the entire screen width (mostly in landscape mode).
I don't have much experience using UICollectionViewController so I decided to accomplish this using one UICollectionViewController class and two reusable cells, but when I set the first section cell width the second one is also affected and then cells width is cut.
Is my point correct?
Shall I use different UICollectionViewControllers (one per section)?
Update: My Controller code
import UIKit
class InverterController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let moduleCell = "moduleCell"
let parameterCell = "parameterCell"
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
if section > 0 {
return CGSizeZero
} else {
return CGSize(width: collectionView.frame.width, height: CGFloat(195))
}
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtPath indexPath: NSIndexPath) -> CGSize {
var newSize = CGSizeZero
if indexPath.section == 0 {
newSize = CGSizeMake(CGFloat(105), CGFloat(105))
} else {
newSize = CGSizeMake(CGFloat(313), CGFloat(200))
}
return newSize
}
override func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionElementKindSectionHeader:
let headerView = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "inverterHeader", forIndexPath: indexPath)
return headerView
default:
assert(false, "Unexpected element kind")
}
}
/**
Two sections in this UICollectionView: First one for clock items, second one for other parameters.
*/
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 2
}
/**
First section contains one cell
Second section contains three cells
*/
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
var items = 0
if section == 0 {
items = 7
} else {
items = 3
}
return items
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cellIdentifier = ""
if indexPath.section == 0 {
cellIdentifier = moduleCell
} else {
cellIdentifier = parameterCell
}
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellIdentifier, forIndexPath: indexPath)
cell.layer.cornerRadius = 6
cell.layer.masksToBounds = true
return cell
}
}
Interface Builder configuration for CollectionView
Simulator screenshot (non desired result; all cells have the same width)
You can use collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize delegate method to adjust cell size.
In your case you could write something like this:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let width : CGFloat
let height : CGFloat
if indexPath.section == 0 {
// First section
width = collectionView.frame.width/7
height = 50
return CGSizeMake(width, height)
} else {
// Second section
width = collectionView.frame.width/3
height = 50
return CGSizeMake(width, height)
}
}
Put this code inside your collectionViewController class.
Set height to what your cell height should be. You might want to adjust width if you're using spacing between cells or insets in your collectionView.
for Swift 4
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width : CGFloat
let height : CGFloat
if indexPath.section == 0 {
// First section
width = collectionView.frame.width/7
height = 50
return CGSize(width: width, height: height)
} else {
// Second section
width = collectionView.frame.width/3
height = 50
return CGSize(width: width, height: height)
}
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 1.0
}
func collectionView(_ collectionView: UICollectionView, layout
collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 1.0
}

How to implement UICollectionViewDelegateFlowlayout's sizeForItemAtIndexPath with Async images?

I'm trying to implement UICollectionViewDelegateFlowlayout's sizeForItemAtIndexPath similar to this tutorial: http://www.raywenderlich.com/78550/beginning-ios-collection-views-swift-part-1
The problem I have is that my photos are downloaded via an async call on the web, so when sizeForItemAtItemPath is called, my images haven't loaded yet. In other words, the code below crashes on imageArray. How can I delay the method until my images are loaded? I've also tried using a hardcoded with and height value if no images are available, but then I only return those hardcoded values, i.e. sizeForItemAtIdexPath doesn't seem to get called again after the images are loaded.
func pinImageForIndexPath(indexPath: NSIndexPath) -> UIImage {
return imageArray[indexPath.row]
}
}
extension PinCollectionViewController : UICollectionViewDelegateFlowLayout {
//1
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let pinImage = pinImageForIndexPath(indexPath)
//2
if var size = pinImage.size as? CGSize {
size.width += 10
size.height += 10
return size
}
return CGSize(width: 100, height: 100)
}
//3
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
insetForSectionAtIndex section: Int) -> UIEdgeInsets {
return sectionInsets
}
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! PinCollectionViewCell
cell.pinImage?.pin_updateWithProgress = true
cell.pinImage?.image = nil
var actInd: UIActivityIndicatorView = UIActivityIndicatorView()
actInd.frame = cell.pinImage.bounds
actInd.hidesWhenStopped = true
actInd.activityIndicatorViewStyle =
UIActivityIndicatorViewStyle.Gray
cell.addSubview(actInd)
actInd.startAnimating()
if let pinImageURL = self.pins[indexPath.row].largestImage().url {
cell.pinImage?.pin_setImageFromURL(pinImageURL, completion: ({ (result : PINRemoteImageManagerResult) -> Void in
actInd.stopAnimating()
if let image = result.image {
self.imageArray.append(image)
}
self.collectionView?.reloadItemsAtIndexPaths([indexPath])
}))
}
return cell
}
Indeed, you should return some hardcoded values until you haven't loaded image. Strictly saying, you should display some predefined placeholder for not yet loaded image.
When your image get loaded (when async download operation successfully completes), you should call -reloadItemsAtIndexPaths: on collection view with index path's of those cells, images for which are available.

Animating a CollectionViewCell Size To Outside CollectionView Boundary - Swift

I have been working on animating the size of some collectionViewCells. It is working great in my collectionViewController, but in another UIViewController where I have a small collectionView alongside other objects, the cell will only grow to the size of the collectionView. I would like it to fill the whole screen. What is the best way about doing this?
Here is my current code:
var largePhotoIndexPath : NSIndexPath? {
didSet{
var indexPaths = [NSIndexPath]()
if largePhotoIndexPath != nil {
indexPaths.append(largePhotoIndexPath!)
}
if oldValue != nil {
indexPaths.append(oldValue!)
}
collectionView?.performBatchUpdates({ () -> Void in
self.collectionView?.reloadItemsAtIndexPaths(indexPaths)
return
}){
completed in
if self.largePhotoIndexPath != nil {
self.collectionView?.scrollToItemAtIndexPath(self.largePhotoIndexPath!, atScrollPosition: .CenteredVertically, animated: true)
}
}
}
}
func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool {
if largePhotoIndexPath == indexPath {
largePhotoIndexPath = nil
}
else {
largePhotoIndexPath = indexPath
}
return false
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let dealImage: UIImage = UIImage(data: self.imageDataArray[indexPath.row])!
let dealPhoto: UIImageView = UIImageView(image: dealImage)
if indexPath == largePhotoIndexPath {
var size = UIScreen.mainScreen().bounds
dealPhoto.frame == size
return CGSize(width: 300, height: 525)
}
return CGSize(width: 100, height: 175)
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.imageDataArray.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! TrailInfoCollectionViewCell
cell.imageView.image = UIImage(data: self.imageDataArray[indexPath.row])
return cell
}
And some images before and after I select the cell:
As you can see, the image has grown to the size I want it, but it is constrained by the collectionView. Is it possible for a collectionViewCell to exceed the boundaries of a collectionView, or will I have to do it some other way? I would just set up a hidden imageView but I like the animated transition.
Thanks for your help. Please let me know if you need any more details.
clipsToBounds properties is what prevents a subview from growing past it's parent. Set it to false and it should work.

Resources