Get CollectionView cells that are invisible - ios

Intro
I know that this topic has been discussed a lot on StackOverflow and
am aware that a Collection View recycles its cells.
However, I have not found a suitable explanation for the problem I
describe below.
Problem
In my app, I have a collection view that has multiple cell types in it - each cell only exists once in the collection view.
The collection view acts like a form in which the user can enter different things - split up on a few cells.
If I now scroll down, the first cell is not visible anymore. However, it is also not removed from the collection view as the entered data is still correctly displayed if I scroll back to the top.
If a user clicks a button, I am fetching every collection view cell
from the collection view and run a validation method on it.
Just like the following:
let firstCell = myCollectionView.cellForItem(at: IndexPath(row: 0, section: 0)) as! DefaultElementCell // get the cell
firstCell.validateEntries() // validates entries and highlights incorrect fields
If I now scroll to the bottom of the collection view (the last cell), I get an error "unexpectedly found nil while unwrapping an optional value". The optional value in this case is the Cell that should be returned by cellForItemAt().
So, I think that the cell has already been removed but on the other
hand the text entered into the fields and everything are still there
if I scroll back to the first cell.
What I tried:
if let firstCell = myCollectionView.dataSource?.collectionView(self.myCollectionView, cellForItemAt: IndexPath(row: 0, section: 0)) {
print((firstCell as! DefaultElementCell).validateEntries()) // does nothing
}
Now, I don't get the error "unexpectedly found nil" anymore but validateEntries() also does nothing anymore.
Is there any way I can correctly get the invisible collection view cells or prevent the collection view from re-using cells? As already mentioned, every cell type only exists once in the collection view.
I would appreciate any help!

You need to create an array of same size as you number of cells then
var arr = [String]()
// inside cellForRowAt
cell.textfield.addTarget(self, action: #selector(self.textFieldDidChange(_:)), for: UIControl.Event.editingChanged
cell.textfield.tag = indexPath.item
and
#objc func textFieldDidChange(_ textField: UITextField) {
arr[textfiled.tag] = textfield.text!
}
then validate you entry from arr values

Related

UiCollectionView jump on next cell when previous cell removed

I have custom layout with fullscreen cells. When removing cell from the left (it's not visible at the time), UICollectionView jumps to the next cell.
It's like current cell was at index 4 and when cell on the left removed the next cell has index 4 now and immediately scroll to the next cell.
Describing in 3 steps (A is cell that need to be fullscreen, x will be removed, o other cells, large letter is fullscreen):
ooooAoo
oooxAoo
oooaOo
But must keep this oooAoo
Here is my solution if it can help to anybody, did not found how to achieve desired offset natively, so just scrolling contentOffset to the desired position right after reloadData():
var currentCell: MyCollectionViewCell? {
return (visibleCells.sorted { $0.frame.width > $1.frame.width }.first) as? MyCollectionViewCell
}
//-----------------------------------------
//some model manipulating code, removing desired items here...
let currentList = currentCell?.parentList
reloadData()
if let list = currentList, let index = self.lists.firstIndex(of: list) {
self.scrollToItem(at: IndexPath(row: index, section: 0), at: .centeredHorizontally, animated: false)
}
currentCell is computed property, returns optional middle cell.
Detect which cell is the largest, because of custom flowlayout logic.
parentList is the model item, I can compare cell by it to make life
easier. I check which list was attached to the cell before
reloadData().

How do we use the invisible cells in uitableview swift?

I want to change my progress bar one by one in tableview. but when my use invisible cell in code, then it produces nil.
I use the tableview.cellforRowAt(IndexPath) in our code , Please resolve my problem
UITableViewCell is reusable, you cannot update UI of cells that are not displayed (visible), they just do not exist or being reused for another cell.
Reusable means that cell's view (UITableViewCell) will be used for any other cell that might just got in to the tableView bounds, this happens when you delete a row, or when you scroll away and the cell gets out of tableView bounds etc...
So, its not a problem, but by design.
BTW, you are using the correct method tableView.cellForRow(at: IndexPath), it will return a cell only if one is displayed.
You should instead update your data source and keep the progress value, when the cell next time being displayed it will show the value from data source, like so:
struct CellData {
var progress: Float
}
var dataSource: [CellData]
Now when you need to update the progress of cells, you can just update dataSource, like so
dataSource[index].progress = value
Then you call either call UITableView.realoadData() to refresh the UI of all cells that are visible, or you can update only that particular index, like so:
if let cell = self.tableView.cellForRow(at: IndexPath(row: index, section: 0)) as? CustomCell {
cell.progressView.setProgress(value, animated: true)
}

how to hide the image/label in StoryBoard to show up when we run the app?

I am making a table view app which retrieves the data from the Firebase. when making the user interface in the storyboard, I am using dummy image and label to visualize my app.
but when i run the app which consists of dynamic table view, those dummy images and label also shows up before immediately replaced by the actual data that i download from the Firebase storage.
can I set those images and labels to not show up when i run the app but still available in the storyboard?
thanks in advance :)
If they're just dummies, you can get rid of them when your view loads, before it appears onscreen:
override func viewDidLoad() -> Void{
super.viewDidLoad()
dummy.removeFromSuperview()
}
Whenever you want to hide/show an UIView:
myView.isHidden = true // hide
myView.isHidden = false // show
I assume what you need is to hide the views in viewWillAppear and then show them when necessary.
In your custom cell class, define something to hide the unwanted views:
func hideDummyViews() {
// do some stuff to hide what you don't want, e.g.
myEnclosingStackView.isHidden = true
}
In your table view controller, in the cellForRowAt indexPath func:
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
// Configure the cell ..
if yourDataSource[indexPath.row].isCompletelyLoaded {
// do your fancy dynamic cell layout
} else {
// show the basic version (minus any dummy views)
cell.hideDummyViews()
}
return cell
You can choose your preferred method for hiding the items (isHidden for each view, removing, adjusting constraints). I prefer to embed any disappearing views in a stack view and then use isHidden = true on the enclosing stack. This keeps things organized in your storyboard/XIB file and neatly recalculates constraints for the hidden stacks.
It seems that you want to show some empty (or incomplete) cells until database content arrives and then you will reload each cell as you process new entries in the datasource. This answer will initially give you a set of cells appearing as per your storyboard/XIB, minus the hidden dummy elements. Then as items in your datasource are loaded fully, you can reload the cells.
By the way, it seems like a lot of work to carefully layout these dummy views for "visualization" and then never show them in the app. Why not have some user-friendly place holders or progress indicators showing and then animate in the real/dynamic views as the data arrives?
I assume you download the Image from FireBase and does't want the dummy Image to appear
(Why dont you declare an empty ImageView and an empty Label!). Try setting an Image array in the viewController. Or you can use a struct array if you want a Image and label text together.
var arrImage=[UIImage]()
var arrLblTxt=[String]()
In view did load append your Demo Image's if required.
arrImage.append(UIImage("Demo"))
arrLblTxt.append("Demo")
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tbl_AssignEmployees.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as YourCell
if arrImage[IndexPath.row] != UIImage("Demo")
{
cell.ImageView.Image = arrImage[indexPath.row]
cell.textLabel!.text = arrLblTxt[indexPath.row]
}
else
{
cell.ImageView.Image = nil
cell.textLabel!.text = arrLblTxt[indexPath.row]
}
cell.preservesSuperviewLayoutMargins = false
cell.separatorInset = UIEdgeInsetsZero
cell.layoutMargins = UIEdgeInsetsZero
return cell
}
Then After you download your Image From FireBase,
Reset your Image array with the new Images downloaded from fireBase
Then reload your tableView. tableView.reloadData

How can I identify a table row in iOS?

I made a prototype cell and I have data from a database to load into the cells in a table.
Each cell has a label and 3 buttons just like this:
If I click on a button for example "Meets Standard", how can I identify in which row I tapped the button?
So for example when I press the "Meets Standard" button at a given row I'd like to change the background color of that row to red. How can I do it?
I have a CustomCell.swift class where I configure the prototype cell and a TableView.swift class where I configure the table.
Try control dragging the button to your CustomCell.swift class and typing a name and selecting "Action" on the popup menu. Then, inside the generated method, you can call self.backgroundColor = UIColor.redColor() or perform any other operations that you'd like.
Edit:
Here's what I think you should do to change the height:
Make a boolean in your cell class called "expanded" or something. Then, go into your table view class and implement the heightForRowAtIndexPath method. In that method, retrieve the cell and check if it's expanded, and if so, return a larger height. Now, to make it reload, you will need to store a reference to the table view in each cell, as it says here: Reference from UITableViewCell to parent UITableView?
In that clicked method you already made, where the background is set to red, you will need to call:
tableView.beginUpdates()
tableView.endUpdates()
where tableview is the weak reference you already stored. Also, in that method, you need to add self.expanded = true of course.
Edit 2:
You know what, maybe it's easier to just do this:
weak var _tableView: UITableView!
...
func tableView() -> UITableView! {
if _tableView != nil {
return _tableView
}
var view = self.superview
while view != nil && !(view?.isKindOfClass(UITableView))! {
view = view?.superview
}
self._tableView = view as! UITableView
return _tableView
}

indexPathForRowAtPoint.row is always returning zero for every cell in a tableView

I have tableView cells with some custom views in them. When the custom views are touched I need to store the indexPath.row of the cell that the touched view is inside of. This is how I am currently getting the indexPath:
#IBAction func bulletGroupPressed(sender: AnyObject) {
let center = sender.center as CGPoint
let indexPath = self.exercisesTableView.indexPathForRowAtPoint(center)!
self.exercisesTableView.selectRowAtIndexPath(indexPath, animated: false, scrollPosition: UITableViewScrollPosition.None)
println(indexPath.row)
}
I suspect it has something to do with the ! on the indexPath line, but am not sure exactly what it is. Why does indexPath.row always give me zero on every cell?
I can't give you a coded answer in swift, but the point needs to be stated in the coordinate system of the table. As you have it, the center is stated in the coordinate system of the sender's parent, probably the table view cell. Use the method convertPoint:toView: to fix the coordinate system.
Alternatively, you might consider creating a method like the one shown in my answer to this question. From any view (in your case, the sender), it will walk up the view hierarchy to the first UITableViewCell and report that cell's index path.

Resources