UICollectionViewCells aren't displaying content correctly - ios

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.

Related

How to replace an image in a cell CollectionView

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)")
}

iOS swift: UICollectionview horizontal scroll single cell not reloading

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
}
}

uibutton in collectionview cell action duplicating

So basically my problem is that when I click on a button which is present in collection view cell it should change the colour of button background colour. but the problem is it is changing the colour of another button. eg if I click on button 1 it changes the colour of button 6 automatically.
class hello: UICollectionViewCell {
#IBOutlet weak var btn: UIButton!
#IBAction func click(_ sender: Any) {
if btn.isSelected == true
{
btn.backgroundColor = UIColor.red
btn.isSelected = false
}
else{ btn.backgroundColor = UIColor.purple
btn.isSelected = true
}
}
override func prepareForReuse() {
super.prepareForReuse()
}
}
view controller file
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "happy", for: indexPath) as! hello
if cell.btn.isSelected
{
cell.btn.backgroundColor = UIColor.red
}
else{ cell.btn.backgroundColor = UIColor.purple
}
cell.btn.tag = indexPath.item
print(cell.btn.isSelected ,indexPath.row)
return cell
}
The problem is that the UICollectionView re-uses cell for optimized scroll performance. Hence it re-uses the cell at index 1 when displaying cell at index 6 for e.g. Therefore you need to set the state of the cell when ever it is updated/reused.
The following function is called everytime. So you need to set cell.btn. backgroundColor over here.
func collectionView(_ collectionView: UICollectionView, cellForItemAt
indexPath: IndexPath) -> UICollectionViewCell {
...
...
if dataSource[indexPath.row].selected {
btn.backgroundColor = UIColor.red
}else {
btn.backgroundColor = UIColor.purple
}
...
return cell
}
Now, it is upto your individual implementation, how you want to update the model when selection is changed. One option is you can define a protocol and implement it in your ViewController to update the values.

Get index of clicked UICollectionViewCell in UICollectionView Swift

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)
}

How to transition a UICollectionViewCell via a button to another ViewController?

Question:
How do I transition a UICollectionViewCell I've assigned a button for a UICollection to another View Controller?
Summary:
I've created a UICollection, and UICollectionCells for each image I have.
I create a CollectionCell object for each image I find.
I'm able to create images and buttons for each object, but once I set the target of the button for the cell, I cannot redirect to another ViewController.
Attempts:
I read some other attempts, but they all required that the function be called from the original view controller.
Starting the view controller from within the current view controller:
let secondViewController:SecondViewController = SecondViewController()
self.presentViewController(secondViewController, animated: true, completion: nil)
}
Calling a function from the view controller:
button.addTarget(self,action:#selector(YourControllerName.buttonClicked(_:)),
forControlEvents:.TouchUpInside)
Current Code:
class CollectionViewCell: UICollectionViewCell {
var text: String!
var image_view: UIImageView!
var button: UIButton!
override func awakeFromNib() {
print("Number:" + card_text)
image_view = UIImageView(frame: contentView.frame)
image_view.contentMode = .scaleAspectFill
mage_view.clipsToBounds = true
contentView.addSubview(image_view)
let x_value = contentView.frame.width - contentView.frame.width
let y_value = (contentView.frame.height)/3
let rectangle = CGRect(x: x_value, y: y_value, width: contentView.frame.width, height: 40)
button = UIButton(frame: rectangle)
button.backgroundColor = UIColor.darkGray
button.setTitle(card_text, for: .normal)
button.layer.cornerRadius = 3
button.clipsToBounds = true
button.addTarget(self,action: #selector(ObjectViewCell.buttonPressed), for: .touchUpInside)
contentView.addSubview(button)
}
func buttonPressed(sender:UIButton!) {
print("Practicing: " + self.card_text)
}
}
class Selector: UIViewController
{
var collectionView: UICollectionView!
let base_image = UIImage(named: "blank")
var images = [UIImage(named: "blank"), UIImage(named: "blank"), UIImage(named: "blank"), UIImage(named: "blank"), UIImage(named: "blank"), UIImage(named: "blank"), UIImage(named: "blank"), UIImage(named: "blank"), UIImage(named: "blank"), UIImage(named: "blank"), UIImage(named: "blank"), UIImage(named: "blank")]
#IBOutlet weak var main_view: UIView!
override func viewDidLoad() {
super.viewDidLoad()
print("Loading view in Selector")
setupCollectionView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func setupCollectionView() {
let layout = UICollectionViewFlowLayout()
// Add spacing around each cell.
layout.minimumLineSpacing = 10
layout.minimumInteritemSpacing = 10
// Set each cell size to the size of the image.
layout.estimatedItemSize = CGSize(width: (base_image?.size.width)!, height: (base_image?.size.height)!)
// Set the collection view to the size of the view frame.
collectionView = UICollectionView(frame: main_view.frame, collectionViewLayout: layout)
// Register all images with the CollectionViewCell object.
collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "cell")
// Set the background color.
collectionView.backgroundColor = UIColor.white
collectionView.delegate = self
collectionView.dataSource = self
// Add the collections view to the main view.
view.addSubview(collectionView)
}
}
extension Selector: UICollectionViewDelegate, UICollectionViewDataSource
{
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
cell.card_text = String(indexPath.item + 1)
cell.awakeFromNib()
return cell
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let cardCell = cell as! ObjectViewCell
cardCell.image_view.image = images[indexPath.row]
}
Update:
Here is what I ended up doing. I read over everybody's comments and answers, and I resolved to make all necessary changes within the view controller who originates the UICollection cells. Therefore, I moved my logic out of the CollectionViewCell object, and instead perform all logic/subview additions in the initialization of the cells.
Now I just need to figure out how to transition a view controller. Thank you everyone.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
let contentView = cell.contentView
let image_view = UIImageView(image: base_image)
image_view.contentMode = .scaleAspectFill
image_view.clipsToBounds = true
contentView.addSubview(image_view)
let x_value = contentView.frame.width - contentView.frame.width
let y_value = (contentView.frame.height)/3
let rectangle = CGRect(x: x_value, y: y_value, width: contentView.frame.width, height: 40)
let button = UIButton(frame: rectangle)
let text = String(indexPath.item + 1)
button.setTitle(text, for: .normal)
button.accessibilityHint = text
button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
contentView.addSubview(button)
return cell
}
func buttonPressed(sender:UIButton!) {
print("Button pressed")
print(sender.accessibilityHint)
//let navController = UINavigationController(rootViewController: self)
// 2. Present the navigation controller
//self.present(navController, animated: true, completion: nil)
}
The problem is that your button's target is the cell, and so the buttonPressed action function is located in the cell. That's a pretty silly thing to do, because (as you rightly say) you cannot call present without a view controller to send it to.
What I would have done is set the button's target/selector in cellForItemAt:. That way, self is the view controller and we can set the target to self. But you didn't do that!
Thus, you need to get from the cell to the view controller that controls it.
However, there is a way. It's called walking the responder chain. Set a UIResponder variable to the button:
var responder : UIResponder = self
Now loop, calling next on responder to walk one step up the chain:
responder = responder.next
Each time, look to see if this responder is a UIViewController. When it is, stop looping and send it present! Thus:
var responder : UIResponder = self
repeat { responder = responder .next } while !(responder is UIViewController)
let vc = responder as! UIViewController
vc.present( // ...
(Still, even though the problem can be solved in this way, I think it was silly to get yourself into this mess in the first place. Making the view controller the target in the first place would have been a much better idea, in my opinion.)

Resources