I have uitableview with 10 images in rows in my Xcode swift project. I use this code to show 10 images in rows in my uitableview:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: String(format: "Cell0", indexPath.row), for: indexPath)
var imageV = UIImageView()
imageV = cell.viewWithTag(5) as! UIImageView
imageV.image = UIImage(named:"image\(indexPath.row + 1).png")
But when I scroll my uitableview freezing very very very hard. Why is this happening? And How to fix it?
Create a custom cell
class MyCustomCell: UITableViewCell {
#IBOutlet weak var imgView: UIImageView!
}
In your storyboard, set the class of the cell to MyCustomCell and give it an identifier say cell.
Then, update your cellForRow method as below
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyCustomCell
let imageName = "image\(indexPath.row + 1).png"
cell.imgView.image = UIImage(named: imageName)
return cell
}
UPDATE
Create a new swift file called MyCustomCell.swift
In storyboard, set your cell's class to MyCustomCell
Add a UIImageView in your cell
Connect the #IBOutlet
Set your cell's identifier
Update your cellForRow method
The tableview is lagging and freezing on the scrolling because of how CellForRow works. Essentially, the OS does not save cells that are displayed. For example, rows 1-2-3 are displayed on the screen and have gone through cellForRow. Rows 4 is about to be displayed and row 1 will be removed off the screen. Row 4 will go through cellForRow for it's information to be displayed, and once row 1 is off the screen it will be removed from memory. So if you then scroll back to row 1, it will go through cellForRow and reload the image. So in this case your tableview is constantly loading images from the bundle as you scroll up and down.
Your solution might be to preload all your images and have them in an array so that the OS doesn't have to spend time loading the images repeatedly. Another way to go about might be to create a custom cell and simply have the cell handle loading it's own image by providing it the image name, rather than have the tableview manage the loading of every single's cell's image repeatedly. This would also clean up your cellForRow.
For example: make a new tableViewCell called imageCell which has an imageName property that could be an String. Once the cell is made, you simply pass it the string of it's image in cellForRow, and when the imageCell is created, have it load it's own image through this string in it's awakeFromNib.
let cell = tableView.dequeueReusableCell(withIdentifier: String(format: "Cell0", indexPath.row), for: indexPath)
to
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as CustomCell
cell.imageView.image = UIImage(named: "yourimagename")
Don't change your reusable identifier everytime. This is causing freezing in your case. Moreover create custom cell for adding UIImage.
Related
I have a table view. First, I load text data for all cells from database. Next, I load image for every cell from another database. But when I tried to set images, they are not displayed.
If I use
let cell = myTableView.dataSource?.tableView(myTableView,
cellForRowAt: indexPath) as? MyTableViewCell
then images loaded only in invisible cells. It means that those cells that I see the very first (before scrolling down) don't show their images. But all others do.
Another way, if I use
let cell = myTableView.cellForRow(at: indexPath) as? MyTableViewCell
then the opposite happens - only those cells that I see show their images, but all others don't.
After that I do
cell.setImage(image: image)
func setImage(image: UIImage) {
guard let myImageView = myImageView else { return }
myImageView.image = image
}
I set images not in func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {...} because as I said, images come after creating cells.
You need to refresh the Table View in the main thread after the load. You can do it with:
DispatchQueue.main.async {
self.yourTableView.reloadData()
}
I was trying to create a custom TableView Cell in Storyboards. The correct data shows when the ViewController is loaded. However, when I start scrolling back and forth, the data is showing up in random cells, not in the right place. I was also trying to make the text of a Right Detail cell bold programmatically like this:
if dayOfWeek!-1 == indexPath.row {
cell.textLabel?.font = UIFont.boldSystemFont(ofSize: (cell.textLabel?.font.pointSize)!)
cell.detailTextLabel?.font = UIFont.boldSystemFont(ofSize: (cell.detailTextLabel?.font.pointSize)!)
}
However, similar things happened: the bold seems right at first and the text are bolded in random cells. Any idea how to fix this? Thanks!
class CustomTableViewCell: UITableViewCell {
override func prepareForReuse() {
// your cleanup code
}
}
in cell for indexpath
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: CustomTableViewCell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) as! CustomTableViewCell
if dayOfWeek!-1 == indexPath.row {
cell.textLabel?.font = UIFont.boldSystemFont(ofSize: (cell.textLabel?.font.pointSize)!)
cell.detailTextLabel?.font = UIFont.boldSystemFont(ofSize: (cell.detailTextLabel?.font.pointSize)!)
}
return cell
}
You have to reload the tableview when you have called it.
The random shown data is due to the property undertaken by the various cell from the each other.
You have faced these type of problem when you are using the multiple tableview on the single view.
Hence the cell having the elements wrongly considered the properties, which can be solved by refreshing the table view which is "RELOAD".
If the Data coming is from the API , then it would be an advantage, put the Response data to your main array and Reload the table view .
I got NSData from Core Data, and when I transform from NSData to UIImage, it make my UITableView very slow to scroll. I already resize my Image before I store image to Core Data. Is there good way to do that. At first, I want to use Kingfisher, but it seems don't have this kind of method to achieve that.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "ListTableViewCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? ListTableViewCell else {
fatalError("The dequeued cell is not an instance of MealTableViewCell.")
}
//cell.imageContentPhoto.kf.setImage(with: url)
cell.imageContentPhoto.image = UIImage(data: photos[indexPath.row] as Data)//photo is NSData, I got it from Core Data
return cell
}
Your problem isn't that the transform is slow, but that it's done a lot of times when you scroll.
In order to optimize UITable, iOS does not create n cells for n rows. It creates x+2 cells (IIRC), where x is the number of visible cells.
When cellForRowAt is called, you call dequeueReusableCell, which takes one of the free cells and assigns it to the row. That way, when you scroll, there are no initializations of objects happening, and slowing the scrolling down.
The problem with your code (which is now evident), is that after the cell is allocated to the row, you again convert the image and initialize it.
What you should do is initialize the array of images in advance, for example in the viewDidLoad. Then your code would look like:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "ListTableViewCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? ListTableViewCell else {
fatalError("The dequeued cell is not an instance of MealTableViewCell.")
}
//cell.imageContentPhoto.kf.setImage(with: url)
cell.imageContentPhoto.image = imagesArray[indexPath.row] // or something similar
return cell
}
Of course, if you have a lot of different images, it might be worth doing some kind of lazy loading. Keep a place holder image while scrolling and when it stops, load the relevant images only. This is of course a lot more complicated and left as an exercise to the student.
I created a tableview with custom cell.
Storyboard looks like this:
As you can see, I put check symbol at the right side of the cell.
I assigned the custom cell class as follow:
class CustomCell: UITableViewCell{
#IBOutlet weak var checkImage: UIIMageView!
}
And in the tableview class, i have function that states:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! CustomCell
// Configure the cell...
cell.textLabel?.text = people[indexPath.row]
return cell
}
The weird thing is... when the cell is clicked the check mark shows up. And when other cell is clicked, the check mark disappears and only shows up at the selected cell.
I really have no clue what is going on here..
I tried answers from similar questions but none of them worked.
Any help will be appreciated. Thank you
I believe what you want is to be able to select multiple cells.
tableView.allowsMultipleSelection = true
Adding the line above should solve your problem.
However, why don't you use the accessory Checkmark? You can use even the Basic UITableViewCell and select the acc
You only need to set cell.accessoryType = .checkmark or cell.accessoryType = .none.
Just select multiply selection for tableview
I'm loading UIViews into my tableview cells content view but as I scroll through my tableview, only one cell loads its view. Its loading the correct view for each cell but it only loads one and then disappears as a new cell appears from the bottom. All of the data is loaded locally via a function makeTableViewRowView (an array of strings that populates the uiview).
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: expenseCell, for: indexPath)
tableViewRow = makeTableViewRowView(indexPath: indexPath)
tableViewRow.translatesAutoresizingMaskIntoConstraints = false
cell.contentView.addSubview(tableViewRow)
cell.selectionStyle = .none
let margins = cell.contentView.layoutMarginsGuide
tableViewRow.centerYAnchor.constraint(equalTo: margins.centerYAnchor).isActive = true
tableViewRow.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
tableViewRow.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true
tableViewRow.heightAnchor.constraint(equalToConstant: 40).isActive = true
return cell
}
make custom cell in interface builder and deque it. And make changes as per requirement for that cell in cellForRowAtIndexPath! Because cellForRowAtIndexPath will get called when your cell will be deque, I mean cell will be reused! So, creating view in cellforrow and add it as subview is not good solution, you can add directly from interface builder and can manipulate it in cellforrow as per requirement!