Animating a CollectionViewCell Size To Outside CollectionView Boundary - Swift - ios

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.

Related

Call collectionView(_ :layout:sizeForItemAt:) in Swift 4

I made the gallery using UICollectionView.
If you lower the screen, the image should continue to come out and stop at 20.
The code was constantly updated.
However, the function collectionView (_: layout: sizeForItemAt :) that sets the size of the cell did not work.
Therefore, only 20 images are displayed continuously.
On viewDidLoad()
if let layoutt = artCollectionView?.collectionViewLayout as? PinterestLayout
{
layoutt.delegate = self
}
cell setting
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HomeCollectionViewCell", for: indexPath) as? HomeCollectionViewCell,
let arts = self.artList else { return HomeCollectionViewCell() }
if arts.count > indexPath.row
{
let model = arts[indexPath.row]
cell.imgView.sd_setImage(with: URLHelper.createEncodedURL(url: model.thumburl), completed: nil)
}
return cell
}
return cell size
extension HomeViewController : PinterestLayoutDelegate
{
func collectionView(_ collectionView: UICollectionView, heightForPhotoAtIndexPath indexPath:IndexPath) -> CGFloat
{
return CGFloat(300.0)
}
}
Currently, the function only executes once when the app starts. Do you know how to run that function every time I want?
You need to implement UICollectionViewDelegateFlowLayout.
You can read more about the protocol here:
UICollectionViewDelegateFlowLayout protocol
Example:
extension viewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 50, height: 300)
}
}
If you look at the prepare() method of PinterestLayout class, you can see the below code
guard
cache.isEmpty == true,
let collectionView = collectionView
else {
return
}
When you reload the colectionView the cache is no longer empty, so it returns back without altering the layout.
So just remove the cache value when prepare is called.
Add removeAll() before guard
cache.removeAll()
Please follow the steps to install PinterestLayout in UICollectionView below.
Set PinterestLayout to your collection view.
let layout = PinterestLayout()
collectionView.collectionViewLayout = layout
Setup layout
layout.delegate = self
layout.cellPadding = 5
layout.numberOfColumns = 2
Implement methods of PinterestLayoutDelegate
extension CustomCollectionVC: PinterestLayoutDelegate {
func collectionView(collectionView: UICollectionView,
heightForImageAtIndexPath indexPath: IndexPath,
withWidth: CGFloat) -> CGFloat {
let image = images[indexPath.item]
return image.height(forWidth: withWidth)
}
func collectionView(collectionView: UICollectionView,
heightForAnnotationAtIndexPath indexPath: IndexPath,
withWidth: CGFloat) -> CGFloat {
let textFont = UIFont(name: "Arial-ItalicMT", size: 11)!
return "Some text".heightForWidth(width: withWidth, font: textFont)
}
}
When data is loaded invalidate layout as well as reload data on collection view.
collectionView.collectionViewLayout.invalidateLayout()
collectionView.reloadData()
Observation:
func collectionView(_ collectionView: UICollectionView, heightForPhotoAtIndexPath indexPath:IndexPath) -> CGFloat
try this instead of above:
func collectionView(collectionView: UICollectionView,
heightForImageAtIndexPath indexPath: IndexPath,
withWidth: CGFloat) -> CGFloat

Show/hide content in expandable collection view cell

I have a collection view with two cells. I made cells expand/collapse by select(didSelectItemAtIndexPath). Cell contains additional views which should became visible in expanded mode and hidden in collapsed. My code work well if I play with expanded cell, but if I have expanded cell and tap on collapsed the expanded cell change their size but additional view stay visible. Maybe it will be easy to understand looking on the gif.
Good scenario:
click first cell = expand it
click first cell again = collapse it and hide image view
Bag scenario:
click first cell = expand it
click second cell = expand second cell & collapse first cell but additional image view on first cell still visible
click second cell again = collapse second cell and hide their content. additional image view on first cell still visible
Here is cell code:
class ExerciseSetCell: BaseCell {
// timer
let timerButton: UIButton = {
let button = UIButton()
button.setImage(UIImage(named: "timer"), forState: .Normal)
button.frame = CGRectMake(0, 0, 25, 25)
button.alpha = 0
return button
}()
override func setupViews() {
super.setupViews()
backgroundColor = UIColor.whiteColor()
addSubview(timerButton)
timerButton.snp_makeConstraints { (make) in
make.width.equalTo(25)
make.height.equalTo(25)
make.bottomMargin.equalTo(-7)
make.centerX.equalTo(self)
}
}
}
Here is my collectionViewController code:
class ExerciseDetailVC: UICollectionViewController, UICollectionViewDelegateFlowLayout {
var exercise: Exercise?
let exerciseSetCell = ExerciseSetCell()
private let setCell = "ExerciseSetCell"
private var expandedCellIndex = -1
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.registerClass(ExerciseSetCell.self, forCellWithReuseIdentifier: setCell)
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(setCell, forIndexPath: indexPath) as! ExerciseSetCell
return cell
}
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let cell = collectionView.cellForItemAtIndexPath(indexPath) as! ExerciseSetCell
if expandedCellIndex == indexPath.item {
UIView.animateWithDuration(0.1, animations: {
cell.weightView.alpha = 0
cell.timerButton.alpha = 0
})
expandedCellIndex = -1
} else {
UIView.animateWithDuration(0.25, animations: {
cell.weightView.alpha = 1
cell.timerButton.alpha = 1
})
expandedCellIndex = indexPath.item
}
collectionView.reloadData()
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
if expandedCellIndex == indexPath.item {
return CGSizeMake(view.frame.width, 200)
}
return CGSizeMake(view.frame.width, 60)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
return 2
}

rearrange collectionViewCell once the scrolling to next item ends

I have a collectionView and my collectionViewCell contains an imageView, so when I'm loading my view for the first time it seems fine, but when I scroll through it, it gets misplaced , see the images
i have 3 images in my collectionView cell the first image is at place but not the second and third images
before scrolling (image1)
after scrolling (image2) (when am horizontally switching to next image)
my collectinView supports Horizontal Scroll and (pink background) is my cell , (yellow background) is CollectionView as you can see once I scroll to next image my cell gets a little out of screens, any idea how to fix this ???
my codes :
in tableView:
extension TableViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return contentImages[collectionView.tag].count
}
func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
return true
}
func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell",
forIndexPath: indexPath) as! CollectionViewCell
//cell.imgView.frame = CGRectMake(0, 0, collectionView.frame.width, collectionView.frame.height)
cell.imgViewOfCell.contentMode = .ScaleAspectFit
cell.imgViewOfCell.image = ResizeImage(UIImage(named: contentImages[collectionView.tag][indexPath.item])!, targetSize: CGSizeMake( cell.imgViewOfCell.frame.width , cell.imgViewOfCell.frame.height))
//imageView.image = UIImage(named: imageModel[collectionView.tag][indexPath.item])
return cell
}
}
collectionViewCell :
class CollectionViewCell: UICollectionViewCell {
#IBOutlet var imgViewOfCell: UIImageView!
override func awakeFromNib() {
imgViewOfCell.frame = self.bounds
self.contentView.setNeedsLayout()
}
}
in tableViewCell:
func setCollectionViewDataSourceDelegate
<D: protocol<UICollectionViewDataSource, UICollectionViewDelegate>>
(dataSourceDelegate: D, forRow row: Int) {
collectionView.delegate = dataSourceDelegate
collectionView.dataSource = dataSourceDelegate
collectionView.tag = row
collectionView.reloadData()
}
var collectionViewOffset: CGFloat {
get {
return collectionView.contentOffset.x
}
set {
collectionView.contentOffset.x = newValue
}
}
Any idea of how to fix this? Please let me know what am doing wrong or what I'm missing.
ended up with this ,after debugging i got to know that my Cell's x origin was not accurate , so am calculating this manually on my cellForItemAtIndexPath like this :
func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell",
forIndexPath: indexPath) as! CollectionViewCell
cell.imgViewOfCell.contentMode = .ScaleAspectFit
if indexPath.row == 0 {
cell.frame.origin.x = 0
}else if indexPath.row == 1 {
cell.frame.origin.x = self.view.bounds.width
}else if indexPath.row == 2 {
cell.frame.origin.x = (self.view.bounds.width*2)
}else if indexPath.row == 3 {
cell.frame.origin.x = (self.view.bounds.width*3)
}
....... }

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.

How to make UICollectionView auto layout

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.

Resources