I have a collectionView and want to make the center cell is bigger than other cells and when move to previous or next cell make it bigger in center
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == self.collectionView {
return slidData.count
} else {
return categoryData.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.collectionView {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "imageCell", for: indexPath) as! SliderImgCell
cell.categoryName.text = slidData[indexPath.row].imageTitle
cell.detail.text = slidData[indexPath.row].imageContent
cell.pics = slidData[indexPath.item]
return cell
} else{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "categoryCell", for: indexPath) as! CategoryCell
cell.categoryName.text = categoryData[indexPath.row].depName
cell.pics = categoryData[indexPath.item]
return cell
}
}
You need to save the selected indexPath like this:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedIndexPath = indexPath
}
Your collection view must confirm UICollectionViewDelegateFlowLayout. Then in sizeForItemAt method resize your cell:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath == selectedIndexPath {
return CGSize(width: 200, height: 90)
} else {
return CGSize(width: 180, height: 80)
}
}
Basically you want a Carousel animation.
Take a look into this library and either use full of it or take help from the code.
You can achieve this by using ScrollViewDidScroll(_:) method and find the center cell and enlarge it.
I wrote clear comments on the code below, if you want to go into details check-out my article about this:
https://medium.com/#sh.soheytizadeh/zoom-uicollectionview-centered-cell-swift-5-e63cad9bcd49
func setFocusedCellStyle(_ scrollView: UIScrollView) {
guard scrollView is UICollectionView else { return }
// get the centerPoint
let centerPoint = CGPoint(x: self.collectionView.frame.size.width / 2 + scrollView.contentOffset.x, y: self.collectionView.frame.size.height / 2 + scrollView.contentOffset.y)
// get the indexPath for the cell at center
if let indexPath = self.collectionView.indexPathForItem(at: centerPoint), self.centerCell == nil {
// centerCell is instance variable of type: YourCell
self.centerCell = (self.colorPickerCollectionView.cellForItem(at: indexPath) as! CustomCollectionViewCell)
UIView.animate(withDuration: 0.2) {
// Choose desired scale value
self.transform = CGAffineTransform(scaleX: 1.58, y: 1.58)
}
}
if let cell = self.centerCell { // center cell is global variable
let offsetX = centerPoint.x - cell.center.x // get to current position of the cell
// when cell is 15 pixels away from the center to the sides
// reset the cell size.
if offsetX < -15 || offsetX > 15 {
UIView.animate(withDuration: 0.2) {
self.transform = CGAffineTransform.identity
}
self.centerCell = nil // set this to nil so next cell in the center will enter the above scope
}
}
}
Related
I create a CollectionView with PinterestLayout, so this is a collection view with different cells hight. It's work, but when I put my code to the project with Firestore (when image in cells I take from URL) I have a problem, because in this line "let photo = post.floverImg" I think I have to put something like in cell "cell.imageView.sd_setImage(with: URL(string: flovers[indexPath.row].floverImg!))" to show Image and not String.
extension HomeViewController {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return flovers.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
cell.imageView.sd_setImage(with: URL(string: flovers[indexPath.row].floverImg!))
cell.label.text = flovers[indexPath.row].floverName
// cell.descriptionLabel.text = flovers[indexPath.row].floverName
cell.layer.cornerRadius = 15
return cell
}
}
func collectionView(collectionView: UICollectionView, heightForPhotoAt indexPath: IndexPath, with width: CGFloat) -> CGFloat
{
if let post = posts?[indexPath.item], let photo = post.floverImg {
let boundingRect = CGRect(x: 0, y: 0, width: width, height: CGFloat(MAXFLOAT))
let rect = AVMakeRect(aspectRatio: photo.size(), insideRect: boundingRect)
return rect.size.height
}
return 200
}
https://ibb.co/J3KdDtG
https://ibb.co/JzXZv3V
Within a UICollectionView, I would like to show/hide more content when an item is tapped.
Currently, I am doing this by designing a larger cell on a Storyboard, with just the UILabel I want to always show at the top. When an item is tapped, the didSelectItemAt() and sizeForItemAt() calls are coded as:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedIndex = indexPath.item
print("didSelectItemAt: \(selectedIndex)")
collectionView.reloadItems(at: [indexPath])
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
var size = CGSize()
size.width = collectionView.frame.width
size.height = 50
if let index = selectedIndex {
print("SizeForItemAt item:\(indexPath.item), \(index)")
if index == indexPath.item {
size.height = 150
} else {
size.height = 50
}
}
return size
}
Which has this output (taken from an iPhone Simulator screen capture, converted to GIF). Note, when hiding/reducing the height on the higher item, the blue box animates behind the lower item.
Is there a better way to implement this?
You can achieve this by adding UILabel as collectionview Section and expandad/collapsed view as row. When user select label , you can change rows height
Declare section number :
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
Declare row count in section:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if indexPath.section == 0{
return 1
}
else indexPath.section == 1{
return 1
}
return 0
}
Then fill your section and rows in cellForRowAt
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellID", for: indexPath) as! CellViewController
if indexPath.section == 0{
cell.yourLabel.text = "bla bla"
if indexPath.row == 0{
// You can declare another cell in here
let blueViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "blueViewCellID", for: indexPath) as! BlueViewController
}
}
}
And in sizeForItemAt function
func collectionView(_ collectionView : UICollectionView,layout collectionViewLayout : UICollectionViewLayout,sizeForItemAt indexPath : IndexPath) -> CGSize{
if indexPath.section == 0{
if indexPath.row == 0{
if firstSectionOpened == true {
return 50
}else{ return 0 }
}
let width = collectionView.frame.width / CGFloat(4)
return CGSize(width: width, height: width)
}
}
Then in didSelect
var firstSectionOpened : Bool = false
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.section == 0 {
if firstSectionOpened == true{
firstSectionOpened = false
}
else{
firstSectionOpened = true
}
self.myCollection.reloadData()
}
}
I have two UICollectionView collectionViewA and collectionViewB in my ViewController, collectionViewB is presented as a subView when a button is tapped, the issue i have now is that when i scroll on collectionViewB, collectionViewA scrolls too, is there a way to only scroll for the active collectionView without affecting the second.?
extension TrendListVC: UICollectionViewDelegate,
UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == self.collectionViewForSubView{
return count
}
return 60
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.collectionViewForSubView{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! ModalViewCell
//let setting = settings[indexPath.item]
//cell.setting = setting
return cell
}else{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell2", for: indexPath) as! CollectionCell
cell.itemNameLabel.text = "Name".uppercased()
return cell
}
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
var reusableView : CollectionHeader? = nil
if collectionView == self.collectionView{
if (kind == UICollectionElementKindSectionHeader) {
let head = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "headerCell", for: indexPath) as! CollectionHeader
head.trendListVc = self
head.headerHeightConstraint = headerHeightConstraint
reusableView = head
}
}
return reusableView!
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if collectionView == self.collectionViewForSubView{
if count % 2 != 0 && indexPath.item == count - 1{
let paddingSpace = sectionInsets.left * (itemsPerRowForSubView + 1)
let availableWidth = collectionViewForSubView.frame.width - paddingSpace
return CGSize(width: availableWidth + 23, height: cellHeight)
}else{
let paddingSpace = sectionInsets.left * (itemsPerRowForSubView + 1)
let availableWidth = collectionViewForSubView.frame.width - paddingSpace
let widthPerItem = availableWidth / itemsPerRowForSubView
return CGSize(width: widthPerItem, height: cellHeight)
}
}else{
let paddingSpace = sectionInsets.left * (2 + 1)
let availableWidth = view.frame.width - paddingSpace
let widthPerItem = availableWidth / itemsPerRow
return CGSize(width: widthPerItem + 4, height: widthPerItem)
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
if collectionView == self.collectionView{
return CGSize(width: view.frame.width, height: 50)
}else{
return CGSize(width: 0, height: 0)
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if collectionView == self.collectionView{
showControllerForSetting(setting: "Name")
}else if collectionView == self.collectionViewForSubView{
print("Some thing")
}
}
}
This is most likely due to using your ViewController as delegate and datasource for both of your collection views. You need to create separate delegate classes for each collection view.
I have an array of photos that I currently display in a UICollectionView. The only thing I still want to add is an extra static cell that should give the user the possibility to open the camera. I used an if-else statement to detect the index. Unfortunately, the console gives me an out of index error.
To be precise: I want this static cell to be in the top left corner, followed by my array of images. Do I have to add two sections, or should I register another custom cell to accomplish this? As of now I can see my extra cell, but it's not working when tapped (out of index).
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageArray.count + 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: photoId, for: indexPath) as! PhotosCollectionViewCell
if indexPath.row == imageArray.count {
cell.backgroundColor = UIColor.lightGray
cell.addGestureRecognizer(UIGestureRecognizer(target: self, action: #selector(tappedCamera)))
} else {
cell.imageView.image = imageArray[indexPath.item]
cell.imageView.addGestureRecognizer(UIGestureRecognizer(target: self, action: #selector(tappedPhoto)))
}
return cell
}
Updated code (solution)
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageArray.count + 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.row == 0 {
let cameraCell = collectionView.dequeueReusableCell(withReuseIdentifier: cameraId, for: indexPath) as! CameraCollectionViewCell
return cameraCell
}
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tappedPhoto))
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: photoId, for: indexPath) as! PhotoCollectionViewCell
cell.imageView.image = imageArray[indexPath.row - 1]
cell.imageView.addGestureRecognizer(tapGesture)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.row == 0 {
print("Camera")
}
}
var startingFrame: CGRect?
var blackBackGroundView: UIView?
var selectedImageFromPicker: UIImage?
var selectedImageCompressed: UIImage?
func tappedPhoto(sender: UIGestureRecognizer) {
if let indexPath = self.collectionView?.indexPathForItem(at: sender.location(in: self.collectionView)) {
let imageView = self.collectionView?.cellForItem(at: indexPath)
startingFrame = imageView?.superview?.convert((imageView?.frame)!, to: nil)
let zoomingImageView = UIImageView(frame: startingFrame!)
zoomingImageView.image = imageArray[indexPath.row - 1]
zoomingImageView.isUserInteractionEnabled = true
zoomingImageView.contentMode = .scaleAspectFill
zoomingImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleZoomOut)))
if let keyWindow = UIApplication.shared.keyWindow {
blackBackGroundView = UIView(frame: keyWindow.frame)
blackBackGroundView?.backgroundColor = UIColor.black
blackBackGroundView?.alpha = 0
keyWindow.addSubview(blackBackGroundView!)
keyWindow.addSubview(chooseLabel)
keyWindow.addSubview(zoomingImageView)
// Set selected image and compress
selectedImageFromPicker = imageArray[indexPath.row - 1]
selectedImageCompressed = selectedImageFromPicker?.resized(withPercentage: 0.1)
chooseLabel.rightAnchor.constraint(equalTo: keyWindow.rightAnchor, constant: -25).isActive = true
chooseLabel.bottomAnchor.constraint(equalTo: keyWindow.bottomAnchor, constant: -25).isActive = true
UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.blackBackGroundView?.alpha = 1
self.chooseLabel.alpha = 1
let height = self.startingFrame!.height / self.startingFrame!.width * keyWindow.frame.width
zoomingImageView.frame = CGRect(x: 0, y: 0, width: keyWindow.frame.width, height: height)
zoomingImageView.center = keyWindow.center
}, completion: {(completed) in
// Do nothing
})
}
}
}
Do I have to add two sections, or should I register another custom
cell to accomplish this?
In your case, just adding one cell at the beginning of the collection should be fair enough, there is no need to multi-section it.
Your methods should be implemented as follows:
1- numberOfItemsInSection method: should be as is:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageArray.count + 1
}
2- cellForItemAt method: depends on the first cell, if it should be a different cell:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// first row
if indexPath.row == 0 {
let cameraCell = collectionView.dequeueReusableCell(withReuseIdentifier: "cameraCell-ID", for: indexPath)
// setup the cell...
return cameraCell
}
let defaultCell = collectionView.dequeueReusableCell(withReuseIdentifier: "defaultCell-ID", for: indexPath)
// setup default cell...
return defaultCell
}
Or, if you want it to be the same cell, but with some editions:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell-ID", for: indexPath)
// first row
if indexPath.row == 0 {
// setup the cell as cemera cell...
} else {
// setup the cell as default cell...
}
return cell
}
Actually, there is no need to add UITapGestureRecognizer for each cell, all you have to do is to implement collectionView(_:didSelectItemAt:) delegate method:
Tells the delegate that the item at the specified index path was
selected.
3- didSelectItemAt method:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.row == 0 { // camera cell
// handle tapping the camera cell
} else { // default cells
// handle tapping the default cell
// don't forget that:
// getting the first element in 'imageArray' should be imageArray[indexPath.row - 1]
}
}
Hope this helped.
I have two cells within a single collection view. They both have different dimensions but for some reason on runtime both of the cells appear to have the same dimension.
Here's my setup:
internal func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if cellType == .books {
return books.count
} else if cellType == .spotlights {
return spotlights.count
} else {
return 0
}
}
internal func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if cellType == .books {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "booksCollectionCell", for: indexPath) as! BooksCollectionCell
let bookTitle = books[indexPath.item].title
let authors = books[indexPath.item].authors
cell.configureCell(title: bookTitle, authorNames: authors)
return cell
} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "spotlightsCollectionCell", for: indexPath) as! spotlightsCollectionViewCell
cell.configureCell(image: #imageLiteral(resourceName: "testImage"))
return cell
}
}
Here's my storyboard screenshots:
and this is setup for collectionView:
edit: So I managed to get somwhere (thanks to cpatmulloy) by putting this code in cellForItem:
cell.frame = CGRect(x: cell.frame.origin.x, y: cell.frame.origin.y, width: 180.0, height: 130.0)
However, here's the result (look at the last cell in tableview):
Try explicitly setting the cell's height and width in the cellForItemAtIndex Path function.
Be sure to set these example properties (i.e bookCellWidth) yourself
internal func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if cellType == .books {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "booksCollectionCell", for: indexPath) as! BooksCollectionCell
let bookTitle = books[indexPath.item].title
let authors = books[indexPath.item].authors
cell.configureCell(title: bookTitle, authorNames: authors)
cell.frame = CGRect(x: cell.frame.origin.x, y: cell.frame.origin.y, width: bookCellWidth, height: bookCellHeight)
return cell
} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "spotlightsCollectionCell", for: indexPath) as! spotlightsCollectionViewCell
cell.configureCell(image: #imageLiteral(resourceName: "testImage"))
cell.frame = CGRect(x: cell.frame.origin.x, y: cell.frame.origin.y, width: spotlightCellWidth, height: spotlightCellHeight)
return cell
}
}
Ok so I managed to fix it!
First I added UICollectionViewDelegateFlowLayout to the cell class, then:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if cellType == .spotlights {
return CGSize(width: 250.0, height: 180.0)
} else {
return (collectionViewLayout as! UICollectionViewFlowLayout).itemSize
}
}