My collectionView displays images of characters that have been added as a favorite. This is persisted in Core Data.
On a detail view controller, the user clicks the favorite button which adds the character to favorites. When it is clicked again, the figure is removed.
The problem is the images in the collection view are not updated without closing the app and re-running. I tried using:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.favoriteCollectionView.reloadData()
}
But this was unsuccessful. How can I get the collection view to update?
UPDATE: Here is all that is in the cellForItemAt function.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ownedCell", for: indexPath) as! OwnedCollectionViewCell
cell.ownedFigImage.sd_setImage(with: URL(string: favFigArray[indexPath.row]))
return cell
}
Related
I have a collectionView within a tableview and I am trying to access the collection view's indexPath in order to get its contents upon selecting it.
Here is my code:
TableView
//This grabs the indexPath of the collectionView Selected with its contents
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedDeal = dealArray[indexPath.row]
}
ViewController
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let cell = tableView.dequeueReusableCell(withIdentifier: "SpecialPicTableViewCell", for: indexPath) as! SpecialPicTableViewCell
//Defines the selected
let deal = cell.selectedDeal
//Grabs selected information here
dealSelected = deal.title ?? ""
//Then segues
performSegue(withIdentifier: "goToDeal", sender: nil)
tableView.deselectRow(at: indexPath, animated: true)
}
However, the selection does not register with the TableView. Nothing happens as a result.
The only information in the collection view is information you put there. You shouldn't need to get any information out of the collection view cell because you already have it.
The code you posted dequeues (possibly creates) a cell, cleans it up and gets it ready for use (possibly reuse.) That cell that you essentially created will know nothing about the data that is in the cell that was selected.
What you should be doing instead, is referring to the array where you store the deals to display and figuring out what deal is at the selected index path.
I have a UICollectionViewController, which scrolls vertically(like a tableview). I created a custom UICollectionViewCell. Inside of a custom cell, there are checkmarks. I need some kind of event to click when the user clicks on a checkmark.
What I tried, was overriding:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("clicked")
}
but that only executes when the use clicks on a cell. But, asI stated above, the cell contains checkmarks...I need to find out when each individual check mark is clicked.
Is this possible?
You can add an IBAction to the UICollectionViewCell class and handle the tap from that IBAction, if you need it to change something on the Parent view controller you have a couple of options, you can use a delegate or pass the controller to the cell.
On the parent view controller :
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! CustomCollectionViewCell
cell.controller = self
return cell
}
On the UICollectionViewCell class:
var controller: ParentViewController?
#IBAction func checkmarkPressed(sender: UIButton) {
print("checkmarkPressed")
controller.someFunction()
}
I am trying to recreate the iOS Gallery App, where the user has two collection view controllers, the first one showing all the available images and the second one is displayed when the user taps in an image (a cell) and that same image is shown in another collection view controller with the following function:
DispatchQueue.main.async {
self.collectionView?.scrollToItem(at: self.customIndexPath, at: UICollectionView.ScrollPosition.centeredHorizontally, animated: false)
}
}
where the custom indexPath is set when the user selects an item in the previous collection view controller.
The problem is, in the second collection view controller (Where the image is displayed individually), I don't know how to make it so when the user swipes right or left, the next/previous image is shown and centred, just as if the above function has been called on that image.
I've tried to call the above func in the following code
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let newIndexPath = IndexPath(item: indexPath.item, section: 0)
print("Item at \(indexPath.item)")
self.collectionView?.scrollToItem(at: newIndexPath, at: UICollectionView.ScrollPosition.centeredHorizontally, animated: true)
}
But its called too soon, the image is replaced by the next image as soon as the user starts going forward or backwards the collection view, I would prefer if the next image is displayed and centred when the user swiped or at least moved horizontally and more than half of the previous image isn't shown and more than half of the next image is shown, the user releases and automatically the next image is centred
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
var customIndexPath = IndexPath()
let images:[UIImage] = [
UIImage(named: "image_1")!,
UIImage(named: "image_2")!,
UIImage(named: "image_3")!,
UIImage(named: "image_4")!,
UIImage(named: "image_5")!,
UIImage(named: "image_6")!,
UIImage(named: "image_7")!,
UIImage(named: "image_8")!,
]
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print(customIndexPath)
DispatchQueue.main.async {
self.collectionView?.scrollToItem(at: self.customIndexPath, at: UICollectionView.ScrollPosition.centeredHorizontally, animated: false)
}
}
}
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate {
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.userImageView.image = images[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let newIndexPath = IndexPath(item: indexPath.item, section: 0)
print("Item at \(indexPath.item)")
self.collectionView?.scrollToItem(at: newIndexPath, at: UICollectionView.ScrollPosition.centeredHorizontally, animated: true)
}
}
The result should be very similar to the iOS Gallery App in a very basic level, when the user swipes, the next image must be shown and centred, not floating around like it is now or showed partially, also if the user swipes slowly and the next cell (next image) is not shown by at least half of its content, then the collection view must display and centre the previous image, just like the iOS Gallery App
My project until now can be found in the following link:
https://github.com/francisc112/Gallery-Test
Thanks in Advance, if there is any tutorial where I can learn to do this, please point it out.
i just tried solving this problem using a bit different approach. Im only using the scrollViewWillEndDragging and scrollViewDidEndDragging methods. Also im using a previousCollectionViewContentOffsetX property which is used to tell if we should scroll to the next or prior cell when the dragging end in case the user make a fast scroll (which is a PhotosApp behaviour).
Finally i've just set a fast deceleration rate to emulate the pagination feature:
collectionView.decelerationRate = .fast
You can see this solution in the pull request i've just made in your repo.
Another simpler solution is to set collectionView's min spacing for cells and lines to zero and enable the collectionView's pagination. We lose the margins between photos shown by the PhotosApp but avoid touching the scrollview delegates. I can make a pull request with this solution if you want.
Having UICollectionView as a child of UITableView row. UICollectionView contains images, but whenever I scroll tableview down and up the collection view images got vanished randomly. I am attaching images for my problem reference. Please suggest me how to stop this.
I want my tableview to be like this. And its items should not change on scrolling.
----------------------------------------------------------------------------------------------------------------------------------------------
The collectionview images got vanish on scrolling tableview. It looks like this after scrolling up.
Code Is as follow:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell:PartOfLookTableViewCell = self.looksListTable.dequeueReusableCell(withIdentifier: "cell") as! PartOfLookTableViewCell
let oneRecord = looksArray[indexPath.row]
cell.myCollectionView.loadInitial(_dataArray: oneRecord.imagesArray, isLooks: 1)
return cell
}
Code for loading data to CollectionView:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: looksReuseIdentifier, for: indexPath) as! CustomCollectionViewCell
let oneRecord = inputArray[indexPath.row]
cell.productImage.sd_setImage(with: URL.init(string: oneRecord.thumb_url)){ (image, error, cacheType, url) in
DispatchQueue.main.async {
cell.productImage.image = image
}
}
}
}
}
#Sourabh Bissa :
UITableView reuses the cell using method CellForRowAtIndexPath whenever your new cell gets visible your this method reuse the data source.
The very important thing here is to maintain the data source:
In your case cell for the row at index path giving the updated value to the collection view method but you are not reloading in main Queue. Try to do it immediately after you get the data source.
Your Cell for the row at index path will look like this :
guard let cell = self.tableview.dequeueReusableCell(withIdentifier: "cell") as! PartOfLookTableViewCell else {
return UITableViewCell()
}
let oneRecord = looksArray[indexPath.row]
cell.configureCell(for record : oneRecord with looks : 1)
return cell
and Now in the cell, you will have collection view outlet, where you will implement a collection view data source method and there you download your images asynchronously.
Cell Class will look like this :
class PartOfLookTableViewCell: UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
collectionView.delegate = self
collectionView.dataSource = self
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func configureCell(for record : Record , with looks : Int) {
// Here reload your collection view
// This collection view will be specific to the cell.
collectionView.reloadData()
}
}
extension PartOfLookTableViewCell : UICollectionViewDelegate , UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//return array
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// Asyncronously download images
}
}
This is how you can achieve your requirements without using any tags. Please let me know if you have any Queries in it.
new to swift. I have a nested CollectionView from one viewcontroller. The main viewcontroller has 7 collectionviewcell ("Level1Cell" in the code below). Each time I click a button or trigger an event, I want the collectionView to reload with the new data.
func eventHandler() {
// updates data
myCollectionView.reloadData()
}
Then, after it calls reload, it will call the reload again on each of the the nested CollectionViewCell.
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Level1Cell", for: indexPath) as! Level1Cell
cell.appsCollectionView.reloadData()
return cell
}
The problem is, let say I want to, for the first cell, set a particular row some text.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if(self.index == 0 && indexPath.row == 30){
rightCell.textLabel.text = "asdasd"
}
The fourth "Level1Cell" cell somehow has its label set also at the 30th row, but not the second and third. After stepping through the debugger, I realize that the cells, after reloading, the fourth cell "Level1Cell" is set to have the the same memory address as the first cell ( why does reload do this - shouldn't it allocate a new memory for each "Level1Cell"? - how can I get around this). Also, should I not use reload to update the data in the view and nested view of those from the view controller?
Thanks!
UIcollectionview will reuse cells. You must provide needed data for row in method
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
Or just clear previous data at cell.