I created the collection programmatically. Everything works perfectly. But now I need to replace a picture in a certain cell. How to identify the cell and get to the UIImage?
var imagesOfPaletes = [UIImage]()
override func viewDidLoad() {
super.viewDidLoad()
if imagesOfPaletes == [] {
for i in 0...11 {
let palete = UIImage(named: "\(i)-0")!
imagesOfPaletes.append(palete)
}
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let myCell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell", for: indexPath as IndexPath)
let img2 = imagesOfPaletes[indexPath.item]
imageView2.image = img2
myCell.contentView.addSubview(imageView2)
return myCell
}
if you want to access a certain cell use "collection.cellForItem(at: inedexPath)" and then use the returned cell object to access the image.
So to access the image you can loop through cells subviews using "collectionCell.subViews" and check the type of the subView. check the following example:-
let collectionCell = UICollectionViewCell()
collectionCell.addSubview(UIImageView())
for subView in collectionCell.subviews {
if subView is UIImageView {
debugPrint("Ok")
}
debugPrint("subview is \(subView)")
}
Related
In my application UICollection scroll horizontally. collection-view cell two button and one UIView was designed.
Each cell loaded two button. when user click one button the particular cell should be reload and the UIView will be displayed in that cell only other cell not displayed.
Here i attached the collectionview image:
Here my code:
#IBOutlet weak var dataCollectionView: UICollectionView!
var addIndexArray:[Int] = []
var arraySelectedFilterIndex = [IndexPath]()
self.listArr = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t"]
override func viewDidLoad() {
super.viewDidLoad()
self.dataCollectionView.reloadData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return (lvsnListArr.count/2)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = dataCollectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier_CollectionView, for: indexPath as IndexPath) as! DataCell
if self.arraySelectedFilterIndex.contains(indexPath) {
cell.buttonView.isHidden = false
}else{
cell.buttonView.isHidden = true
}
cell.btn1.setTitle(lvsnListArr[2 * indexPath.row], for: .normal)
cell.btn1.tag = indexPath.row
cell.btn1.addTarget(self, action: #selector(btnPressed(sender:)), for: .touchUpInside)
cell.btn2.setTitle(lvsnListArr[2 * indexPath.row + 1], for: .normal)
cell.btn2.tag = indexPath.row
cell.btn2.addTarget(self, action: #selector(btn2Pressed(sender:)), for: .touchUpInside)
return cell
}
#objc func btnPressed(sender: UIButton) {
self.addIndexArray.append(sender.tag)
let hitPoint = sender.convert(CGPoint.zero, to: dataCollectionView)
if let indexPath = dataCollectionView.indexPathForItem(at: hitPoint) {
print("indexPath--->",indexPath)
self.arraySelectedFilterIndex.append(getIndexPath)
self.dataCollectionView.reloadItems(at: [getIndexPath])
}
}
#objc func btn2Pressed(sender: UIButton) {
self.addIndexArray.append(sender.tag)
let hitPoint = sender.convert(CGPoint.zero, to: dataCollectionView)
if let indexPath = dataCollectionView.indexPathForItem(at: hitPoint) {
print("indexPath--->",indexPath)
self.arraySelectedFilterIndex.append(getIndexPath)
self.dataCollectionView.reloadItems(at: [getIndexPath])
}
}
My error:
I clicked cell btn1 one action btnPressed single cell over load and display the next cell image both images are overlap in collectionview.
Here i attached my issue cell image.
Here i got cell indexpath and indexpath.row, how can i validate and fix this issue in cell for row. struggling this point.
Kinldy help to fix this issues. Thanks advance.
You can reload single cell like you are doing in button action
self.dataCollectionView.reloadItems(at: [getIndexPath])
OR
You can get cell in button action like as below
if let cell = self.dataCollectionView.cellForItem(at: indexPath) as? DataCell
{
//Here you can write your view hide show code
if self.arraySelectedFilterIndex.contains(indexPath)
{
cell.buttonView.isHidden = false
}
else
{
cell.buttonView.isHidden = true
}
}
I have a two ViewControllers, and each one of them has a collectionView (let's name it CV).
The first CV Cell (named MainPageCell) is the main structure of the app, and among it's other components, it displays an UIView named imageViewGrid.
The purpose of the second controller is only to add it's CV to itself as a subview, and then, it can be added as a child of the first controller and then insert it's view on the imageViewGrid.
In a nutshell, I'm trying to display a CV inside of another CV component. But the cells of the second CV doesn't load, or if they do, they disappear when scrolling. Just one cell can display the content until I scroll.
This GIF describes my situation: Click here
First CV cellForItemAt:
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: mainReuseIdentifier, for: indexPath) as! MainPageCell
displayController(contentController: photoGridController, on: cell.imageViewGrid)
return cell
}
imageViewGrid declaration inside MainPageCell:
var imageViewGrid: UIView = {
let ivg = UIView()
ivg.backgroundColor = Colors.lightBlue
ivg.translatesAutoresizingMaskIntoConstraints = false
return ivg
}()
DisplayController func:
func displayController(contentController content: UIViewController, on view: UIView) {
addChild(content)
view.addSubview(content.view)
content.view.frame = view.bounds
content.didMove(toParent: self)
}
Second CV cellForItemAt:
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: photoReuseIdentifier, for: indexPath) as! PhotoGridCell
cell.backgroundColor = Colors.lightBlue
cell.item = names[indexPath.item]
return cell
}
Second CV cell components:
var item: String? {
didSet {
guard let myItem = item,
let image = UIImage(named: myItem) else { return }
if image == UIImage(named: myItem) {
gridImageView.image = image
} else {
gridImageView.image = UIImage(named: "fbicon")
}
}
}
let gridImageView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFill
iv.translatesAutoresizingMaskIntoConstraints = false
return iv
}()
I don't know if I'm supposed to reload something, or if I forget a step, but couldn't been able to make this work, yet. I'd appreciate some help.
I have a collectionview cell which has an image on it and a button below it. Now when I click on this button, I want to load a tableviewCell which has on it the image from the collectionview. To achieve this, I did this initially..
func SellBtnTapped(_ sender: UIButton) {
let indexPath = collectionView?.indexPath(for: ((sender.superview?.superview) as! RecipeCollectionViewCell))
self.photoThumbnail.image = self.arrayOfURLImages[(indexPath?.row)!]
and photoThumbnail is defined like so...var photoThumbnail: UIImageView! But doing this gives a crash telling 'Unexpectedly found nil while unwrapping an optional value' So I tried this..
let point = sender.convert(CGPoint.zero, to: self.collectionView)
let myIndexPath = self.collectionView.indexPathForItem(at: point)
self.photoThumbnail.image = self.arrayOfURLImages[(myIndexPath?.row)!]
But again, the same crash of Unexpectedly found nil.... is happening. Any idea as to what could be the issue..?
EDIT:
This is the code for cellForItemAtIndex...
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath as IndexPath) as! RecipeCollectionViewCell
cell.sellButton.tag = indexPath.item
cell.sellButton.addTarget(self,action: #selector(SellBtnTapped(_:)),for: .touchUpInside)
return cell
}
It's because you alway get nil your indexPath.
Another approach is
in collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath method
set tag of your cell's button like
cell.myButton.tag = indexPath.item
And in SellBtnTapped method use below code for get indexPath
let indexPath = NSIndexPath(item: sender.tag, section: 0) // set section as you want
let cell = collectionView.cellForItem(at: indexPath as NSIndexPath) as! RecipeCollectionViewCell
Now by use of cell you can get image object that is on it or use self.arrayOfURLImages to get right image. and do your further stuff.
I prefer avoiding tags altogether. I wrote this a while ago and still find it useful.
extension UIView {
var superCollectionViewCell: UICollectionViewCell? {
if let cell = self as? UICollectionViewCell {
return cell
} else {
return superview?.superCollectionViewCell
}
}
var superCollectionView: UICollectionView? {
if let collectionView = self as? UICollectionView {
return collectionView
} else {
return superview?.superCollectionView
}
}
var indexPathOfSuperCollectionViewCell: IndexPath? {
guard let cell = superCollectionViewCell, let collectionView = superCollectionView else { return nil }
return collectionView.indexPath(for: cell)
}
}
This turns your action into
func SellBtnTapped(_ sender: UIButton) {
guard let indexPath = sender.indexPathOfSuperCollectionViewCell else {
print("button has no index path")
return
}
self.photoThumbnail.image = self.arrayOfURLImages[indexPath.row]
}
How do I get the index of the "Sheep" I clicked on in a CollectionView made in Xcode with Swift for iOS?
class SheepsOverviewVC:
UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "class", for: indexPath) as! ClassesCollectionCell
if(sheeps.count > 0) {
cell.ClassImageView.image = UIImage(named: sheeps[indexPath.row] as! String)
cell.SheepName.text = names[indexPath.row] as? String
}
return cell
}
I created a Sent Event on the TouchDown via the Gui:
#IBAction func clickingSheep(_ sender: UIButton) {
print("This will show info about the Sheep")
print(sender)
}
But the response I get is from the second print:
<UIButton: 0x7f9a63021d20; frame = (50 50; 136 169); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x60800003d260>>
Probably there is some way to figure out which Sheep was clicked, but how do I get that information?
This is how it looks like (other namings then provided in the post):
One solution is to get the index path of the cell based on the button's location.
#IBAction func clickingSheep(_ sender: UIButton) {
let hitPoint = sender.convert(CGPoint.zero, to: collectionView)
if let indexPath = collectionView.indexPathForItem(at: hitPoint) {
// use indexPath to get needed data
}
}
You can set and check the button property "tag" (if you have the outlet set to the controller)
Here is another easy solution:
Have a new property for the callback.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "class", for: indexPath) as! ClassesCollectionCell
if(sheeps.count > 0) {
cell.ClassImageView.image = UIImage(named: sheeps[indexPath.row] as! String)
cell.SheepName.text = names[indexPath.row] as? String
}
cell.callBack = { [weak self] collectionViewCell in
let indexPath = collectionView.indexPath(for: collectionViewCell)
self?.doStuffFor(indexPath)
}
return cell
}
and on the cell you can have the ibaction
cell class
//...
var callBack : ((UICollectionViewCell?)->Void)?
//...
#IBAction func action(_ sender: UIButton) {
self.callBack?(self)
}
In my CollectionView I want the user to select cells. If a cell is selected, an image should appear and the label should disappear. The User should also be able do deselect the cell again and then selecting it again (the selecting after deselecting doesn't work at the moment).
The text for the labels comes from an array and is shown properly.
But when selecting a cell, and then scrolling other cells are selected to. When scrolling up back again randomly other cells a selected that have never been touched. I think it has to do something with the reuse of the cell so I already added a prepareForReuse Method in the Class of the cell.
I also want to store which cells were selected when the user closes the app and show them as selected when he opens it up again.
A further problem is, that I want it only to be possible to select a cell, when a certain other cell is also selected. What is the best way to do this?
class CollectionViewController: UICollectionViewController {
var array:[String] = []
var selectedIndexes:NSMutableDictionary = NSMutableDictionary()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
doors = ["1","2","3"]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return array.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell:MyCollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! MyCollectionViewCell
var Label = cell.viewWithTag(1) as! UILabel
Label.text = array[indexPath.row]
let keystring = String(format:"%i", indexPath.row)
if (self.selectedIndexes[keystring] != nil) {
cell.myLabel.hidden = true
cell.myImageView.image = UIImage(named:"1")!
}
else {
}
return cell
}
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let keystring = String(format:"%i", indexPath.row)
if (self.selectedIndexes[keystring] != nil) {
let cell = collectionView.cellForItemAtIndexPath(indexPath) as! MyCollectionViewCell
cell.myLabel.hidden = false
cell.myImageView.image = nil
}
else {
selectedIndexes.setObject(keystring, forKey: keystring)
let cell = collectionView.cellForItemAtIndexPath(indexPath) as! MyCollectionViewCell
cell.myLabel.hidden = true
cell.myImageView.image = UIImage(named:"1")!
}
The class of the CollectionViewCell:
class MyCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var myLabel: UILabel!
#IBOutlet weak var myImageView: UIImageView!
override func prepareForReuse() {
super.prepareForReuse()
self.myImageView.image = nil
self.myLabel.hidden = false
The cells are reused (which greatly optimises table scrolling speed, etc), and as you describe that is your "problem".
All you have to do is to "reinitialise" the text and labels, etc on the cells overtime they are loaded. The best thing is to have your cell content in an object and to have those object in an array. When the cell i loaded, you then set the values according to the values in the object in the array using cellForRowAtIndexPath (usually indexPath.row in the default implementation)