How do we use the invisible cells in uitableview swift? - ios

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

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().

Get CollectionView cells that are invisible

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

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

UITableView doesn't scroll automatically if the datasource changes

I have a UITableView with two arrays as datasource. I also have one UISegmentedControl to switch between these arrays. I use the segmentedControlChanged method to call the reloadData method.
This works fine.
But there is one problem.
For example arrayOne has 1 item and arrayTwo has 10 items.
So if I switch to the arrayTwo and scroll to the bottom of the tableview and after that switch back to arrayOne the tableview stays scrolled down and i don't see any item. But if I start scrolling, the tableview "jumps" automatically to the first item.
So my question is, how can i trigger this behaviour automatically when the tabledata source changes?
You can use any one of following two lines after reloading table
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
self.tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Top, animated: true)
or
mainTableView.setContentOffset(CGPoint.zero, animated: true)
Firstly i don't know how you change data source of tableview but i use this method for my data source switches.
I create 3 arrays;
var mainArr = [String]()
var sortedArr = [String]()
var arrToReturn : [String] {
if segmentedController.selectedIndex == 0 {
return mainArr
}
else {
return sortedArr
}
}
I always return arrToReturn for tableView data source.
When it comes to your scrolling issue, it sounds like you are reloading tableView before changing its dataSource.
After that when you start scrolling, tableView reloads cells and can't find old items and automatically sets its scrolling position to start of screen.
Also do you have any code about setting tableviews scrolling position even if it is irrelevant? If you have you should check it.

How do you edit Views within a dynamic UITableViewCell in Swift?

So basically I have a progress view, among others, within a UITableViewCell that is dynamic. How would I access the progress view (or any view) of a specific cell? I want to change the progress based on a value stored for that specific cell.
Thank you in advance.
You should have a view controller (class) level property for progress and eventually implement a property observer so that you can refresh the view as soon as progress gets updated.
var progress: Double = 0.0 {
didSet{
updateProgressCell()
}
}
Supposing that you know where (what's the row number - progressCellRowNumber) in your table is the progress view this is how you can implement the cell refreshing:
fund updateProgressCell() {
var indexPath = NSIndexPath(forRow: progressCellRowNumber, inSection: 0)
self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.None)
}

Resources