I am using UICollectionView in a viewcontroller and passed an array of images. I have enabled paging of collection view . But when i scroll to next image , the previous cell does not disappear completely. It remains at the edge. So, how I can make only specific image/cell visible while scrolling left/Right.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == collection_LargeView
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Large Cell", for: indexPath)as! LargeImageCell
cell.imageView_LargePic.sd_setImage(with: NSURL(string: array[indexPath.row]) as URL! )
cellIndex = indexPath
return cell
}
else
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Picture Cell", for: indexPath)as! VendorPictureCell
cell.imageView_VendorPics.sd_setImage(with: NSURL(string: array[indexPath.row])as URL!)
return cell
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return array.count
}
//when item at bottom collection view is selected the other collection view scroll to that indexPath
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if collectionView == collectionView_picturesCollection
{
Index = indexPath.row
collection_LargeView.scrollToItem(at: indexPath, at: .right, animated: true)
}
}
Please solve this.
Thank You.
After scrolling from one cell to next Screenshot : https://i.stack.imgur.com/msmnV.jpg
You fist change in your story board's collectionview's in min spacing for cell and for lines to 0 like in below images:
To
I hope it may help you
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if collectionView == collection_LargeView
{
return CGSize(width: self.view.frame.size.width, height: 524.0)
}
else{
return CGSize(width: 86.0, height: 74.0)
}
}
Related
I have a collectionview cell, and need to filtering cell.
I'm trying to make cell height to 0. but not work.
this is my code.
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.numberOfCell
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? data else {
return UICollectionViewCell()
}
let set1 = CoreDataManager.shared.getSetting(idx: indexPath.row).set1
if(set1){
return cell
}
else{
let none = collectionView.dequeueReusableCell(withReuseIdentifier: "none", for: indexPath)
none.frame.size.width = 100
none.frame.size.height = CGSize(width: 0, height: 100).width
return none
}
}
}
If you need to set a size of cell you need to conform to UICollectionViewDelegateFlowLayout and implement sizeForItemAt and give it the size.
extension viewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 100, height: 0)}
}
another solution to make height constrain for a view inside the your cell with identifier "cell" and give it to 0 before returning a cell in cellForItemAt.
I'm making a CollectionView which has 2 sections, everything is fine but the size. I'd like to give different size of cell in different section, but when I call sizeForItemAt, Xcode shows a yellow warning "Will never be executed". Here's how I set my CollectionView functions:
extension ArtistProfileViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
switch mySections[section] {
case .profile:
return 1
case .relatedArtists:
return 10
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
switch mySections[indexPath.section] {
case .profile:
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ArtistProfileCell.identifier, for: indexPath) as? ArtistProfileCell else { return UICollectionViewCell()}
return cell
case .relatedArtists:
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: RelatedArtistCell.identifier, for: indexPath) as? RelatedArtistCell else { return UICollectionViewCell()}
cell.myImageView.image = UIImage(systemName: "person")
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 100, height: 100)
}
}
And here is how I set my CollectionView:
var myCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
layout.estimatedItemSize = .zero
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.register(ArtistProfileCell.self, forCellWithReuseIdentifier: ArtistProfileCell.identifier)
cv.register(RelatedArtistCell.self, forCellWithReuseIdentifier: RelatedArtistCell.identifier)
return cv
}()
I did set the CollectionView.delegate/datasource to self in viewDidLoad, and I can see the cell appear on the screen. However, the size of cell does not change at all. Plus, I know one of the solution is to set the estimated size to none in storyboard, but I don't know how to set it programmatically. So I don't know if it works.
There's one thing odd, when I call sizeForItemAt, it does not show the complete function automatically. I have to type it by myself like this.
Does anyone can help? Thanks in advance!
You put sizeForItemAt inside cellForItemAt. They need to be separate.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
switch mySections[indexPath.section] {
case .profile:
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ArtistProfileCell.identifier, for: indexPath) as? ArtistProfileCell else { return UICollectionViewCell()}
return cell
case .relatedArtists:
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: RelatedArtistCell.identifier, for: indexPath) as? RelatedArtistCell else { return UICollectionViewCell()}
cell.myImageView.image = UIImage(systemName: "person")
return cell
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 100, height: 100)
}
Try selecting your code, then pressing Ctrl + i to re-format your code (fixes indenting too!)
I have a collection view which is nested inside of a table view cell. The first cell in the collection view is a button which allows users to create a new list. Its width is smaller than that of the main lists cells. I recently changed the code around so that the collectionView delegate and dataSource methods are in the tableViewController instead of the table view cell, however now the first cells width is not changing as it did before moving the code. This picture shows:
If I press into the blank space it is registering that I clicked the plus cell.
The code for all the collectionView delegate and dataSource methods:
extension ProfileTableViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print("the media: \(usersLists.count)")
return usersLists.count + 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "addNewListCell", for: indexPath) as! addNewListCell
cell.currentUser = currentUser
return cell
}else{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as! userListsCell
cell.layer.applySketchShadow()
cell.layer.cornerRadius = 20
cell.layer.masksToBounds = false
cell.currentUser = currentUser
cell.media = usersLists[indexPath.item-1]
return cell
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize{
if indexPath.item == 0 {
let size = CGSize(width: 80, height: 158)
return size
}else{
let size = CGSize(width: 158, height: 158)
return size
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.item == 0 {
//button
self.addListDelegate?.addNewItem()
}
}
I tested if changing any values in the size in sizeForItemAt would change the cell size and nothing changes in any cells
the table view methods which are in the ProfileTableViewController class and not the extension:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "profileCell", for: indexPath) as! profileCell
cell.selectionStyle = .none
return cell
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
guard let tableViewCell = cell as? profileCell else { return }
tableViewCell.setCollectionViewDataSourceDelegate(dataSourceDelegate: self, forRow: indexPath.row) //this line sets the collectionView delegate and datasouce so that I can have all of the collectionView methods in this class
}
and this is the code inside of the tableViewCell
protocol addListCollectionDelegate: class {
func addNewItem()
}
class profileCell: UITableViewCell, UICollectionViewDelegate {
#IBOutlet weak var collectionView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
collectionView.register(UINib(nibName: "usersLists", bundle: nil), forCellWithReuseIdentifier: "listCell")
collectionView.register(UINib(nibName: "newListCell", bundle: nil), forCellWithReuseIdentifier: "addNewListCell")
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func setCollectionViewDataSourceDelegate
<D: UICollectionViewDataSource & UICollectionViewDelegate>
(dataSourceDelegate: D, forRow row: Int) {
collectionView.delegate = dataSourceDelegate
collectionView.dataSource = dataSourceDelegate
collectionView.reloadData()
}
}
Does anyone know how to change the width of the first cell since sizeForItemAt is not changing the size? Thanks
You forgot to add this protocol: UICollectionViewDelegateFlowLayout
It is required when you want to return custom size for an item.
Also improve your code like below:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "addNewListCell", for: indexPath) as! addNewListCell
cell.currentUser = currentUser
return cell
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as! userListsCell
cell.layer.applySketchShadow() // Write this line in cell class
cell.layer.cornerRadius = 20 // Write this line in cell class
cell.layer.masksToBounds = false // Write this line in cell class
cell.currentUser = currentUser
cell.media = usersLists[indexPath.item - 1]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize{
return indexPath.item == 0 ? CGSize(width: 80, height: 158) : CGSize(width: 158, height: 158)
}
Try to use UICollectionViewDelegateFlowLayout method. In Xcode 11 or later, you need to set Estimate Size to none from Xib.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout:
UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let padding: CGFloat = 170
let collectionViewSize = advertCollectionView.frame.size.width - padding
return CGSize(width: collectionViewSize/2, height: collectionViewSize/2)
}
I am a new iOS programming. and now i am developing an app which display similar to appstore. But i have some problems which when user open the app so, i want cell of data should be display the second cell. for example, i have three cell then when user open the app the second cell should be automatically scrolled to that position.
Here is how i created header section: (programmatically)
/// banner section
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerCellId, for: indexPath) as! HeaderBanner
header.sectionCategory = dashbordCell?[indexPath.item]
return header
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
let size = CGSize(width: view.frame.width, height: 370)
return size
}
Here is I created HeaderBanner
let dataBannerCollectionView: UICollectionView = {
var scrollInterval: Int = 3
let flowLayout = UICollectionViewFlowLayout()
flowLayout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: .zero,collectionViewLayout: flowLayout)
collectionView.backgroundColor = UIColor.clear
collectionView.translatesAutoresizingMaskIntoConstraints = false
return collectionView
}()
func setupViewsCell(){
dataBannerCollectionView.showsHorizontalScrollIndicator = false
dataBannerCollectionView.dataSource = self
dataBannerCollectionView.delegate = self
dataBannerCollectionView.register(HeaderBannerDataCell.self, forCellWithReuseIdentifier: headerCellId)
addSubview(dataBannerCollectionView)
setupConstraintDataBannerCollectionView()
}
Here is behavior of HeaderBanner file.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let count = sectionCategory?.dataBanner?.count {
return count
}
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: headerCellId, for: indexPath) as! HeaderBannerDataCell
cell.dataBannerCell = sectionCategory?.dataBanner![indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
print(frame.width)
let size = CGSize(width: frame.width - 100 , height: 350)
return size
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Banner selected")
}
As you can see the header section display the first cell of image. I just my app look like this when user opens the app .
Here is output of my sample app:
Here is my expectation which i want to display the middle cell of all cells.
You can use the scrollToItem function of UICollectionView:
if you have three cells for example:
dataBannerCollectionView.scrollToItem(at: IndexPath(item: 2, section: 0), at: UICollectionViewScrollPosition.centeredHorizontally, animated: false)
Add that code in setupViewsCell() function
I am using Swift4 and Xcode9.
I create my collectionView cells by using the delegate method below;
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "mediaItemCell", for: indexPath) as! MediaItemCollectionViewCell
cell.mediaItemCellImageView.image = mediaPlayer?.item(at: indexPath.row)?.image
return cell
}
Then, I want to replace the image of selected cell with a "stop" image. To achieve this, I have tried to use didSelectItemAt method;
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if (mediaPlayer?.isPlaying == true) {
// stop the player
mediaPlayer?.stopPlayingMedia()
} else {
// change the image and start playing
let cell = collectionView.cellForItem(at: indexPath) as! MediaItemCollectionViewCell // CRASH
cell.mediaItemCellImageView.image = UIImage(named: "stop.png")
...
...
}
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
// restore the original image
let cell = collectionView.cellForItem(at: indexPath) as! MediaItemCollectionViewCell
cell.mediaItemCellImageView.image = mediaPlayer?.item(at: indexPath.row)?.image
// stop playing
mediaPlayer?.stopPlayingMedia()
}
Now, when I select the cell which is already selected (hence the original image has already been replaced with "stop.png") the code above works; i.e. the original image is restored, which is done by stopPlayingMedia() method.
When I select a cell different than the currently selected one, the App crashes. I tried to move the didSelectItemAt call out of the delegate method, but it did not work.
Surprisingly, the logical check if collectionView.cellForItem(at: indexPath) is MediaItemCollectionViewCell succeeds when I select the currently selected cell, and fails when I select another cell.
I want to change the image of the cell, hence need to cast the variable type but it fails. Why does to cast fail when I select a different cell than the currently selected one?
Thanks
Edit:
I use collectionView.reloadData() to restore all the images to originals (I mentioned this in the OP - when stopPlayingMedia() is invoked). It appears that, if I add a private var index:IndexItem? to my ViewController class and update its value within didSelectItemAt (to save the last selected cell) and use the code below, the code does not crash
extension ViewController:MediaPlayerControllerDelegate {
// this delegate method is invoked when stopPlayingMedia() is called
func mediaPlayerControllerDidFinishPlaying() {
// restore the UI
// collectionView.reloadData() -> this causes the crash, instead, I only restore one cell
let cell = collectionView.cellForItem(at: index!) as! MediaItemCollectionViewCell
cell.mediaItemCellImageView.image = mediaPlayer?.item(at: (index?.row)!)?.image
timeLabel.text = "00:00"
progressBar.progress = 0.0
}
}
I tried the following to create cell and changing the image inside cell on selection. Not crashing at all. Please check the implementation, It might help you.
import UIKit
class ViewController: UIViewController {
private var isPlayingModel = [false, false, false, false, false] // initially all are stop, not playing
#IBOutlet weak var collectionView: UICollectionView! {
didSet{
collectionView.delegate = self
collectionView.dataSource = self
}
}
}
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return isPlayingModel.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "mediaItemCell", for: indexPath) as! MediaItemCollectionViewCell
cell.imageVw.image = isPlayingModel[indexPath.row] ? #imageLiteral(resourceName: "start") : #imageLiteral(resourceName: "stop")
return cell
}
}
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
isPlayingModel[indexPath.row] = !isPlayingModel[indexPath.row]
let cell = collectionView.cellForItem(at: indexPath) as! MediaItemCollectionViewCell
cell.imageVw.image = isPlayingModel[indexPath.row] ? #imageLiteral(resourceName: "start") : #imageLiteral(resourceName: "stop")
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.size.width, height: 60)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 4
}
}
class MediaItemCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var imageVw: UIImageView!
}
Check the implementation here.
You are not passing indexPath correctly to load a cell, that's why it is crashing.Edit your didSelectItemAt: method as follows:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedCell = IndexPath(item: indexPath.item, section: indexPath.section)
let cell = collectionView.cellForItem(at: selectedCell) as! ACollectionViewCell
cell.imgView.image = UIImage(named: "3.jpg")
}
Collection view indexPath has element item, and not row
Try this and see: (let me know what does it print)
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
// restore the original image
if let cell = collectionView.cellForItem(at: indexPath) as? MediaItemCollectionViewCell {
if let cellImage = mediaPlayer?.item(at: indexPath.item)?.image { // Collection view indexPath has element `item`, and not `row`
cell.mediaItemCellImageView.image = cellImage
} else {
print("cellImage not found at indexPath - \(indexPath)")
}
} else {
print("collection view cell `MediaItemCollectionViewCell` not found at indexPath -\(indexPath) ")
}
// stop playing
mediaPlayer?.stopPlayingMedia()
}