ios-Prevent reloading of cell in UITableView on scrolling - ios

I have implemented a UIWebview inside UITableView. When I scroll the table view the data reloads and there is some flickering of the UIWebview. This is because cells are reloaded whenever i scroll table view. I can save the state of cell or save the data of UIWebview in NSMutableArray for faster loading but i cannot save the cells. So how can i save the cell or prevent the reloading of cells again and again. I want those cells to be reloaded only once.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("videoCell") as! UITableViewCell
// var height = UIScreen.mainScreen().applicationFrame.size.height
let linksCell = links[indexPath.row].videoLink
let winksCell = links[indexPath.row]
mutArray.insertObject(linksCell!, atIndex: indexPath.row)
println(mutArray)
if let webView = cell.viewWithTag(21) as? UIWebView {
webView.scrollView.scrollEnabled = false
webView.scrollView.bounces = false
var html = "<html><body><iframe src=\"http://www.youtube.com/embed/\(mutArray[indexPath.row])\" width=\"\(videoTable.frame.width - 16)\" height=\"200\" frameborder=\"0\" allowfullscreen></iframe></body></html>"
webView.loadHTMLString(html, baseURL: nil)
// println(linksCell.videoLink)
// println(html)
}
if let videoName = cell.viewWithTag(22) as? UILabel {
videoName.text = winksCell.title
videoName.adjustsFontSizeToFitWidth = true
}
return cell
}

Try this: I did not test it. but give it a try:
replace:
let cell = tableView.dequeueReusableCellWithIdentifier("videoCell") as! UITableViewCell
With
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "videoCell")

If you are using storyboard then create a custom cell class
and get the cell with identifier inside
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
YourCustomeCell_Class *cell = (YourCustomeCell_Class *)[tableView dequeueReusableCellWithIdentifier:#"MyCellIndentifier"];
return cell;
}
Try it. Hope it solves your problem.

Related

CollectionView inside TableViewCell

I used CollectionView inside TableViewCell. All works fine and shown all as expected. But if I scrolled the TableView very fast, items (i used images in collectionView) from one collection replaced with items (images) from another collection and override it on View (on Debug mode in code al works fine, its just displaying of them).
UITableView GetCell():
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
var item = _view.Items[indexPath.Row];
var cell = (MyTableCell)tableView.DequeueReusableCell(“cell”);
cell.TextLabelView.Text = item.Title;
cell.YesButtonView.Hidden = item.IsCategory;
cell.NoButtonView.Hidden = item.IsCategory;
if (item.IsImagePoint)
{
cell.ImagesCollectionView.DataSource = new ItemsDataSource(item.Images, cell.ImagesCollectionView);
cell.ImagesCollectionView.Delegate = new ItemsDelegate(item, _view);
}
return cell;
}
UICollectionView GetCell():
public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
{
var cell = (ImageViewCell)_collectionView.DequeueReusableCell(new NSString(“ImageViewCell”), indexPath);
var image = _images[indexPath.Row];
var imagePath = image.ThumbnailPath;
if (!string.IsNullOrEmpty(imagePath))
{
cell.ImagePath = imagePath;
}
return cell;
}
Swift 5
Add this in your custom UITableViewCell class.
override func prepareForReuse() {
collectionView.dataSource = nil
collectionView.delegate = nil
collectionView.reloadData()
collectionView.dataSource = self
collectionView.delegate = self
}
It's probably because of the reuse system of cells in UITableView. Do you set up properly your data when you configure the cell? Do you call CollectionView's reloadData()?
EDIT: You should call it in the tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell where you configure your cell. This way, each time a cell is reused, you update its content.
EDIT 2: Just like I said try to add the collection view reloadData() when you set up your tableview cell. You also have to clean your datasource and delegate, because it's a reused cell so it may already have been used with another value.
if (item.IsImagePoint)
{
cell.ImagesCollectionView.DataSource = new ItemsDataSource(item.Images, cell.ImagesCollectionView);
cell.ImagesCollectionView.Delegate = new ItemsDelegate(item, _view);
}
else
{
cell.ImagesCollectionView.DataSource = null;
cell.ImagesCollectionView.Delegate = null;
}
cell.ImagesCollectionView.ReloadData()
return cell;

Reusable UITableViewCell with UICollectionViewCell

Code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
registerCells()
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifierForItem(at: indexPath.row), for: indexPath) as? HorizontalCollectionTableViewCell else {
return UITableViewCell()
}
if cellViewModels.count > indexPath.row {
let viewModel = cellViewModels[indexPath.row]
cell.viewModel = viewModel
}
return cell
}
Passing viewModel to Cell:
var viewModel: TitleAccessoryButtonCollectionViewModel? {
didSet {
guard let viewModel = viewModel else {
return
}
titleLabel.text = viewModel.title
if let buttonTitle = viewModel.accessoryButtonModel?.title {
setAccessoryButtonTitle(buttonTitle)
}else{
accessoryButton.hideTitleLabel()
}
if let buttonImage = viewModel.accessoryButtonModel?.image {
accessoryButton.buttonImageView.image = buttonImage
}
else {
accessoryButton.hideImageView()
}
sectionContentImage.image = viewModel.sectionContentImage
titleLabelLeadingConstraint.constant = viewModel.titleLabelLeadingSpacing
accessoryButton.isHidden = viewModel.hideAccessoryButton
sectionContentView.isHidden = viewModel.hidePremiumContentView
let collectionViewModel = viewModel.collectionViewModel
collectionViewHeight.constant = CGFloat(collectionViewModel.height)
collectionViewModel.setup(collectionView: collectionView)
collectionView.delegate = collectionViewModel.delegate
collectionView.dataSource = collectionViewModel.dataSource
collectionView.reloadData()
}
}
Description:
I have six UITableViewCell mostly, and they are reusable.
In every UITableViewCell is UICollectionView.
Five UICollectionView's use normal UICollectionViewFlowLayout's, but one needs a custom subclass.
The problem is that when UITableViewCell with custom UICollectionViewFlowLayout is hiding and new UITableViewCell is showing and cell with this custom flow layout is reused and UICollectionView already have UICollectionViewFlowLayout but is bad.
Is any nice way to clear this layout or prevent this situation?
Maybe something with prepareForReuse()?
I add that UICollectionView is outlet in UITableViewCell.
This excellent article helped me a lot to get UICollectionViews in UITableviewCells up and running: https://ashfurrow.com/blog/putting-a-uicollectionview-in-a-uitableviewcell-in-swift/
To update the layout you can call
collectionView.collectionViewLayout.invalidateLayout()
See also:
Swift: How to refresh UICollectionView layout after rotation of the device

Duplicate tableview cells added above previously loaded cells

I have a collection view. On one of the collection cells, I have a tableview with 5 different custom cells. I am reusing each cell of the table view.
When the table is reloaded, I see new rows added just above the previously loaded cells. This appears like a new table has been added just above the previous table. I got this visual when I see the 3D view of the collection view:
Blue color is one of the 5 cells. Each time the table loads, it has added 5 sets of rows above previously loaded cells. What is happening here and how could this be possible?
override func viewDidLoad() {
super.viewDidLoad()
self.cabinTableView.registerNib(UINib(nibName:"CabinNetNib", bundle:
nil), forCellReuseIdentifier: cabinNetNib)
}
internal func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellDetail = tableViewDataSourceArray[indexPath.row] as? cabinDetailsMO
let cellType = cellDetails?.cellTypeEnum
if cellType == CellType.Net {
let netLabelCell = self.questionsTableView
.dequeueReusableCellWithIdentifier(questionLabelCellIdentifier) as?
NetLabelTableViewCell
netLabelCell!.selectionStyle = UITableViewCellSelectionStyle.None
netLabelCell!.netLabel.attributedText = cellDetails?.attributedQuestionString
return netLabelCell!
}
else if cellType == CellType.Video {
let videoCell = self.cabinTableView.dequeueReusableCellWithIdentifier(cabinVideoCellIdentifier)
as! cabinVideoTableViewCell
videoCell.videoName = cellDetails?.questionVideo
videoCell.loadVideoItem(cellDetails?.questionVideo, videoFields: nil)
videoCell.selectionStyle = UITableViewCellSelectionStyle.None
shouldReloadAllCells = true
image_videoCellIndex = indexPath.row
return videoCell
}
else if (cellType == CellType.captian) {
let captianCell = self.cabinTableView .dequeueReusableCellWithIdentifier(captianCellIdentifier) as!
CaptianTableViewCell
captianCell .selectionStyle = UITableViewCellSelectionStyle.None
captianCell.question_idLabel.text = cellDetails?.contentidName
return aptianCell
}
}
First cell is alone a nib.

UITableView self sizing cell scrolling issues with big difference of cell height

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let json = JSON(data: activityTableView.onlineFeedsData)[indexPath.row] // new
if(json["topic"]["reply_count"].int > 0){
if let cell: FeedQuestionAnswerTableViewCell = tableView.dequeueReusableCellWithIdentifier(kCellIdentifier_reply) as? FeedQuestionAnswerTableViewCell{
cell.selectionStyle = .None
cell.tag = indexPath.row
cell.relatedTableView = self.activityTableView
cell.configureFeedWithReplyCell(cell, indexPath: indexPath, rect: view.frame,key: "activityTableView")
// Make sure the constraints have been added to this cell, since it may have just been created from scratch
cell.layer.shouldRasterize = true
cell.layer.rasterizationScale = UIScreen.mainScreen().scale
return cell
}
}else{
if let cell: FeedQuestionTableViewCell = tableView.dequeueReusableCellWithIdentifier(kCellIdentifier) as? FeedQuestionTableViewCell{
cell.selectionStyle = .None
cell.tag = indexPath.row
cell.relatedTableView = self.activityTableView
cell.configureFeedCell(cell, indexPath: indexPath, rect: view.frame)
// Make sure the constraints have been added to this cell, since it may have just been created from scratch
cell.layer.shouldRasterize = true
cell.layer.rasterizationScale = UIScreen.mainScreen().scale
return cell
}
}
return UITableViewCell()
}
There is difference of 200-400 in height in each cell,So tried implementing the height calculation in estimatedHeightForRowAtIndexPath .I cant the exact estimate height in this method as bcz of calculation
and caching the height in willDisplayCell method but still the scroll jumps like is it taking time to rendering .
The cell is like the Facebook card type layout with lots of dynamic data with variable height text and images.Can someone help me in this .Thanks
If you're able to calculate each cells height, just use the tableView(_:heightForRowAtIndexPath) instead of estimatedRowHeightAtIndexPath property. estimatedRowHeightAtIndexPath is mostly used in smthg like selfsizing cells etc.
Try this one. I hope this could help you. It's worked for me. Actually this calculates the cell height before it displayed.
Thanks.
NSMutableDictionary *cellHeightsDictionary;
// Put this in viewDidLoad
cellHeightsDictionary = #{}.mutableCopy;
// UITableview delegate Methods
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
NS_AVAILABLE_IOS(7_0)
{
NSNumber *height = [cellHeightsDictionary objectForKey:indexPath];
if (height) return height.doubleValue;
return UITableViewAutomaticDimension;
}
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
[cellHeightsDictionary setObject:#(cell.frame.size.height) forKey:indexPath];
}

How to let user to modify the text in UITableView cells

I have a question regarding uitable view.
I am implementing an app which is similar to the address book app.I am able to present the table view in editing mode. I want to let the user to edit the text in the cells in editing mode. I know that in order to edit the text in the cells, I need a textfield. I have created a textfield.
My question is:
what should I do in order to present that textfield in the cells.
what are the methods I need to implement in order to present that text field in the table view in editing mode.
Once I am done with editing ,How can I update the data which is in my contacts view controller(contains all the contacts).The saving should persist in the address book. For this question I know that I need to implement some delegate method,But I am not sure how to do that.
Please have a look at the following code,so that you will have an idea about my problem.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
[tableView setSeparatorColor:[UIColor clearColor]];
//[self.tableView setEditing: YES animated: YES];
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell...
if(isEditingOn) {
if(cell == nil)
cell = [self getCellContentView:CellIdentifier];
UILabel *lblTemp1 = (UILabel *)[cell viewWithTag:1];
UITextField *textfield1=(UITextField*)[cell viewWithTag:2];
if(indexPath.row == 0) {
lblTemp1.text = #"Name";
textfield1.text = myContact.name;
}
else if(indexPath.row == 1) {
lblTemp1.text = #"Phone";
textfield1.text = myContact.phone;
}
else if(indexPath.row == 2) {
lblTemp1.text = #"Email";
textfield1.text = myContact.email;
}
}
else {
if(indexPath.row == 0) {
cell.textLabel.text = myContact.name;
}
else if(indexPath.row == 1) {
cell.textLabel.text = myContact.phone;
}
else if(indexPath.row == 2) {
cell.textLabel.text = myContact.email;
}
}
return cell;
}
- (UITableViewCell *) getCellContentView:(NSString *)cellIdentifier {
CGRect CellFrame = CGRectMake(0, 0, 60, 20);
CGRect Label1Frame = CGRectMake(10, 10, 180, 25);
UILabel *lblTemp;
UITableViewCell *cell = [[[UITableViewCell alloc] initWithFrame:CellFrame reuseIdentifier:cellIdentifier] autorelease];
lblTemp = [[UILabel alloc] initWithFrame:Label1Frame];
lblTemp.tag = 1;
[cell.contentView addSubview:lblTemp];
[lblTemp release];
CGRect TextFieldFrame=CGRectMake(240, 10, 60, 25);
UITextField *textfield;
textfield=[[UITextField alloc]initWithFrame:TextFieldFrame];
textfield.tag=2;
textfield.placeholder = #"";
[cell.contentView addSubview:textfield];
}
This is a really complex question to answer this fully and in-depth with code examples, but I'll try to point you in the right direction.
1) Add a UITextField as a subview of your table cell when you create the cell in the tableView:cellForRowAtIndexPath: method (I assume that's what your getCellContentView: method is for). Set a tag on your UITextField that matches the row index of the cell and make your tableviewcontroller the delegate for the cell. Set the textfield to hidden. (remember to set the tag each time the cell is requested, not just the first time you create it).
2) In the tableView:didSelectRowAtIndexPath: method, grab the cell using tableViewCellForRowAtIndexPath and then show the textfield inside it (you may have to do some view traversal to get it) and call becomeFirstResponder on the textfield.
3) When the user has typed something, your textfielddelegate methods will be fired. You can look at the tag on the textfield to work out which row the field belongs to and then update the dat source with the new text. Then just reload the table to hide the textfield and update the cell content.
If you know how to use custom table cell subclasses then you can make your life a bit easier by creating a custom cell that already contains a textfield and has an property for accessing it, but otherwise the technique will be mostly the same.
Also, tableView:didSelectRowAtIndexPath: won't normally fire when a tableview is in edit mode unless you set tableView.allowsSelectionDuringEditing = YES;
It's better to use 2 UITableViewCells, The first one for view and the last for edit mode.
Also we will depend on the variable rowToEdit which refers to the current editing row. (in my case one cell is allowed to be edited at the same time)
let us begin:
First I depend on accessoryButtonTap action to edit the row:
var rowToEdit: IndexPath? = nil
override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
// End edit mode if one cell being in edit mode other than this one
if let row = self.rowToEdit {
// return current edit cell to view mode
self.rowToEdit = nil
self.tableView.reloadRows(at: [row], with: .automatic)
}
self.rowToEdit = indexPath
self.tableView.reloadRows(at: [self.rowToEdit!], with: .automatic)
}
Differentiate between the 2 modes when you will load the cell:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath == self.rowToEdit {
let cellId = "ContactEditTableViewCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath as IndexPath) as! ContactEditTableViewCell
cell.accessoryType = .none
self.configEditCell(cell: cell, indexPath: indexPath)
return cell
} else {
let cellId = "ContactTableViewCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath as IndexPath) as! ContactTableViewCell
self.configCell(cell: cell, indexPath: indexPath)
return cell
}
}
Additional option if you want to change the height based on mode:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath == self.rowToEdit {
return 120
} else {
return 70
}
}
Last option to add Save and Cancel buttons:
I added them to each cell, So I pass a reference to the ContactTable to each cell.
#IBAction func btnSave_click(_ sender: UIButton) {
// save the record
btnCancel_click(sender)
}
#IBAction func btnCancel_click(_ sender: UIButton) {
let tmp = self.tbl.rowToEdit
self.tbl.rowToEdit = nil
self.tbl.tableView.reloadRows(at: [tmp!], with: .automatic)
}

Resources