UICollectionViewCell's subview absolute frame - swift

Have been having this issue that I considered to be an easy solve, but spent a lot of time cracking it with no result.
So, I have a UIViewController with UICollectionView in it. This collection is padded 75 px from the top and has cells with pretty simple setup - just 1 UIImageView set to fit the size of the container. Everything is made with AutoLayout.
My task is to make UIImageView go full screen on tap. What I am doing is making a temporary UIImageView and add to the hierarchy of UIViewController view.
The problem I am facing is I can't get the absolute frame of UIImageView in the cell I tap.
I tried various ways to calculate the frame, my most recent try is
var rect = cell.imageView.superview!.convertRect(cell.imageView.frame, toView: self.view)
However, it returns result that seems to be ignoring the collection view top constraint.
Update:
It turns out my problem was in the wrong way I was acquiring the cell from collection view. Instead of:
let cell = collectionView.cellForItemAtIndexPath(indexPath) as! UIImageCollectionViewCell
I was getting cell with
let cell = self.collectionView(collectionView, cellForItemAtIndexPath: indexPath) as! UIImageCollectionViewCell
Which obviously resulted in creating a new cell with the wrong frame. My bad, late night coding is not the best thing sometimes. Thanks to Arun Ammannaya for the quick example

This what i tried, works as expected.
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
return collectionView.dequeueReusableCellWithReuseIdentifier("aaa", forIndexPath: indexPath)
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 10
}
#IBAction func frame(sender: AnyObject) {
print(String(sender.frame))
let rect = sender.superview??.convertRect(sender.frame, toView: self.view)
print(String(rect))
let addedView = UIView()
view.addSubview(addedView)
addedView.frame = rect!
addedView.backgroundColor = UIColor.yellowColor()
}
}
Story board looks like:
Output is looks like:

Related

SWIFT: How to fix cell highlighter and make the whole collection view scroll instead of the highlighter?

I am building a TVOS app where I have this collectionView:
The current cell (The cell which has scrolled to), gets highlighted in Orange.
For example, here the user scrolls to the third cell:
WHAT I AM TRYING TO ACHIEVE:
When the user scrolls to another cell, I want the Orange square, to remain in the first cell, and the whole collection to scroll to the left(or to the right if the user is scrolling in the opposite direction.
And I honestly have no idea how to achieve this, or what should I use exactly.
Should I embed the whole collection view inside of a scrollView?
Anyway, here's how implemented that collection view:
extension MoviesViewController2: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print("first function collectionView detected")
return items2.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier:
cellIdentifier, for: indexPath) as! movieCardCell
cell.movieImageView.sd_setImage(with: URL(string: items2[indexPath.item].imageURL))
return cell
}
// Handle collectionViewItem selection
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("didSelectItem\(indexPath)")
}
// Highlight the current cell
func collectionView(_ collectionView: UICollectionView, didUpdateFocusIn context: UICollectionViewFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
if let pindex = context.previouslyFocusedIndexPath, let cell = collectionView.cellForItem(at: pindex) {
cell.contentView.layer.borderWidth = 0.0
cell.contentView.layer.shadowRadius = 0.0
cell.contentView.layer.shadowOpacity = 0.0
}
if let index = context.nextFocusedIndexPath, let cell = collectionView.cellForItem(at: index) {
cell.contentView.layer.borderWidth = 8.0
cell.contentView.layer.borderColor = UIColor.orange.cgColor
cell.contentView.layer.shadowColor = UIColor.orange.cgColor
cell.contentView.layer.shadowRadius = 10.0
cell.contentView.layer.shadowOpacity = 0.9
cell.contentView.layer.shadowOffset = CGSize(width: 0, height: 0)
collectionView.scrollToItem(at: index, at: [.centeredHorizontally, .centeredVertically], animated: true)
}
}
}
If anyone can answer the question above, it'd be great. Also any tip or what I should use is most welcome.
As I understand, you want the orange rectangle to remain always on the same place. When scrolling you can make it disappear and re-appear once the scrolling has stopped maybe but that's a detail.
So you could put a UIView with the same dimensions as a cell (since all seem to have the same height and width) and make it appear once the user has stopped scrolling.
Anyway I'm looking again at your question and I think I didn't understand your issue correctly, specifically this part The current cell (The cell which has scrolled to), gets highlighted in Orange. in combination with this When the user scrolls to another cell, I want the Orange square, to remain in the first cell
The process might be as follows:
Set your collectionView's size to single cell size. And align it to the left
Make your collectionView's clipsToBounds property to false
Make UICollectionView paging enabled.
Place a rectangle view on top of your collectionView

UICollectionView Cells overlapping with each other when i use a button to increase cell height

I have a CollectionView setup and i have a button inside that increases the cell height by a fixed amount. However when it does this is overlapps with the cell below meaning the cell below doesnt move down to make space.
Ive been trying to figure this out for days and i cant seem to get it working.
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
var colors = [UIColor.red,UIColor.blue]
#IBOutlet weak var col: UICollectionView!
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! cellCollectionViewCell
cell.frame.size.height = CGFloat(317 + cell.button.tag*45)
cell.backgroundColor = colors[indexPath.row]
return cell
}
#IBAction func increaseHeight(_ sender: Any) {
col.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
class cellCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var butt: UIButton!
#IBAction func incHeight(_ sender: UIButton) {
sender.tag = sender.tag + 1
}
}
Editing the frame of the cell directly is probably not the best approach. Instead override collectionView sizeForItemAtIndexPath, and specify the height change there. This should allow the collection view to re-layout correctly with the new cell height.
You can see other questions like this one for more details

how to size cell in gridview

I have a gridlist that looks like this
class ListViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
private var collection: ACollection?
private var collectionViewLayout:UICollectionViewFlowLayout?
override func loadView() {
super.loadView()
collectionViewLayout = UICollectionViewFlowLayout()
collectionViewLayout?.itemSize.width = 128
collectionViewLayout?.itemSize.height = 227
view = UICollectionView(frame: CGRect.zero, collectionViewLayout: collectionViewLayout!)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let someObj: SomeObj = collection!.getObject(index: indexPath.item)
let cell: UICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: NSStringFromClass(UICollectionViewCell.self), for: indexPath as IndexPath)
let cellView: UIImageView = UIImageView(image: object.image)
cellView.frame = cell.bounds
cell.contentView.addSubview(cellView)
return cell
}
I would like my cells to have different aspect ratios though depending on a value in someObj. How can I resize the cells without them overlapping? I have tried to make
cellView.frame = CGRect(x:0,y:0,width:100,height:300)
cellView.frame =cell.bounds
but the cells overlap.
Maybe I'll elaborate a little more about the problem.
First, if you implement collectionView(_:layout:sizeForItemAt:) method, you don't need to specify the following:
collectionViewLayout?.itemSize.width = 128
collectionViewLayout?.itemSize.height = 227
which is a default size for every cell.
Second, overlaps you see on top of every default cell are probably the cellViews (UIImageViews) that you are adding on reused cells. The problem is, you're trying to add UIImageView on the current cell every single time delegate asks for the cell. You'll need to check if the UIImageView is already there, since the entire cell may have been reused still having the image view which added previously.

UICollectionView cells randomly disappear on scroll up or reload functions - iOS 9/Swift

After going through the solutions here on STO and other blogs, none seem to truly fix the UICollectionView issues others definitely seem to still have to this day!
Avoiding the reload functions initially loads the CollectionVC with no issues and scrolling down to the bottom no issues as well, but the moment I scroll up, random cells go missing. Also, noticed that the delegate method below gets called multiple times as I am scrolling, is this normal?
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
Using any of the reload functions results in random cells .backgroundView disappearing:
self.collectionView?.reloadSections(NSIndexSet.init(index: 0))
self.collectionView?.reloadData()
self.collectionView?.reloadItemsAtIndexPaths(cellIndexPathArray)
self.collectionView?.reloadInputViews()
Subclassing UICollectionViewFlowLayout with different override options seems to have no effect.
class CustomCollectionFlowLayout: UICollectionViewFlowLayout {
override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
//self.scrollDirection = UICollectionViewScrollDirection.Vertical
return true
//invalidateLayoutWithContext(invalidationContextForBoundsChange(newBounds))
//return super.shouldInvalidateLayoutForBoundsChange(newBounds)
}
override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
/*
let attributes = super.layoutAttributesForElementsInRect(rect)
print(attributes)
let contentSize = collectionViewContentSize()
return attributes?.filter { $0.frame.maxX <= contentSize.width && $0.frame.maxY < contentSize.height }
*/
let attributes = super.layoutAttributesForElementsInRect(rect)
return attributes
}
}
CollectionVC.swift
private let reuseIdentifier = "cell"
class CollectionVC: UICollectionViewController {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
override func viewDidLoad() {
super.viewDidLoad()
//Register cell class
self.collectionView!.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
}
////////////////////////////////
//UICollectionViewDataSource
////////////////////////////////
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return appDelegate.chosenImageArray.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
// Configure the cell
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath)
cell.backgroundColor = UIColor.clearColor()
cell.layer.borderColor = UIColor.whiteColor().CGColor
cell.layer.borderWidth = 1
cell.layer.cornerRadius = 8
let bgView = appDelegate.chosenImageArray.objectAtIndex(indexPath.row) as? UIView
if bgView != nil {
cell.backgroundView = bgView
cell.hidden = false
cell.backgroundView?.hidden = false
}
return cell
}
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return appDelegate.deviceSize
//Tried with different sizes CGSize(100,100) - (200,200) and 0 on insets, no difference
}
////////////////////////////////
//UICollectionViewDelegate
////////////////////////////////
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath)
//do various stuff with selected cell and segue to next view, no issues here
}
}
Hope someone can tell me it's just a few settings I need to input, as this issue seems random and maybe just a bug with UICollectionView? Anyway, thank you very much in advance for any help.
Yes this is normal every time you scroll the collection view cell and it becomes visible on screen again the delegates get called.
It is getting re used.
In cellForItemAtIndexPath you have
let bgView = appDelegate.chosenImageArray.objectAtIndex(indexPath.row) as? UIView
this above line.
I guess in appDelegate.chosenImageArray you have all the UIViews.
Possible fix
Do not store the UIView directly on array create a NSObject model class and create required properties in it.
-Try to create a NSObject model class which should have all the relevant properties like UIImage , name etc... as per your requirement.
Store that NSObject class in array. Here NSObject class is your model class which you are going to store in your array.
Now in delegate cellForItemAtIndexPath -
let bgViewModel = appDelegate.yourModelClassArray.objectAtIndex(indexPath.row) as? yourModelClass
cell.backgroundView = bgViewModel.bgView
remove cell.hidden = false & cell.backgroundView?.hidden = false it is not required.

UICollectionView appears black when running the App

I am using swift 2.0 and xcode 7. I would like to have a vertical collection view with an array of image on my app. However when I run my project the UICollectionView area appears black. I have set the script on my ViewController but I don't what I'm missing. I already have set an identifier and all other stuff. Could somebody help me please. Thanks.
ViewController Script
ViewController error message
I guess you forgot about delegates, it's typical error.
Also, try use this example of code
class ExampleCollectionView: UIViewController, UICollectionViewDelegate
{
#IBOutlet weak var collectionView: UICollectionView!
let tshirtsArray = ["tshirt_1.png",
"tshirt_2.png",
"tshirt_3.png",
"tshirt_4.png"]
override func viewDidLoad()
{
super.viewDidLoad()
collectionView.delegate = self
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return self.tshirtsArray.count
}
func collectionView(cv: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let cell: UICollectionViewCell = cv.dequeueReusableCellWithReuseIdentifier("CollectionCell", forIndexPath: indexPath)
let collectionImageView: UIImageView = cell.viewWithTag(100) as! UIImageView
collectionImageView.image = UIImage(named: self. tshirtsArray[indexPath.row])
return cell
}
}
I've made the UICollectionView display the images although i still need to update the sizes. I've followed #pdobsk advice to look through the error message and it says that i should consider the cell size together with the margin and other stuffs because the cell size is the same size with the collection view.
error fixed!