Reload UITableView has UITableViewCell visibility issue - iphone

I have a UITableView with two row. First row have the UITextField and second row have the UICollectionView. On reloading the UITableView, cell of UICollectionViewCell does not appear.But on scrolling the UITableView all cell of UICollectionView becomes visible. What wrong I might be doing.
if indexPath.row == 0 {
let cellIdentifier = "NewPostCell"
var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? NewPostCell
if (cell == nil) {
cell = Utils.getCellForGivenIdentifier(cellIdentifier, cellIdentifier: cellIdentifier, ownerT: self) as? NewPostCell
}
cell?.txtPost.delegate = self
cell?.txtPost.text = userThoughts
newPostCell = cell
return cell!
} else {
let cellIdentifier = "ASAttachmentCell"
var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? ASAttachmentCell
if (cell == nil) {
cell = Utils.getCellForGivenIdentifier(cellIdentifier, cellIdentifier: cellIdentifier, ownerT: self) as? ASAttachmentCell
}
if let height = cell?.collectionAttachment?.contentSize.height
{
cell?.cntCollectionHeight.constant = height
}
attachmentCell = cell
return cell!
}
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 1 {
attachmentCell = cell as? ASAttachmentCell
attachmentCell?.attachmentCollection(self)
}
}

Try to put in your code, exactly to the part where you set the collection view datasource also a :
collectionView.reloadData()

This is most likely because you are calling tableView.reloadData() on a background thread. Therefore, when you actually begin to scroll, the UI will get updated on the main thread and everything will look normal. Try calling the reloading on the main thread.
dispatch_async(dispatch_get_main_queue()){
self.tableView.reloadData()
}

Related

Tableview laggy when inserting dynamic cells and scrolling to bottom

I'm making a chatbot app, in which I'm adding different kind of cells in a tableview (with dynamic height), and after adding, I'm scrolling to the bottom.
When the content size of the tableview is smaller compared to the height of the screen, there's no problem with adding the cells. But when the tableview content size has to grow (outside the screen), adding of the cells is laggy, and on my old iPad (iOS 10), cells aren't even displayed right (parts are cut off). Could anyone help me?
I do a network call to get the new message, after I have clicked on a cell option. With the result of that call, I fill the tableview.
func ask(sessionId: String, question: String, retry : Bool, completion : #escaping CompletionBlock) {
Alamofire.request(url!, method: .get, parameters: params, encoding: URLEncoding.default, headers: self.defaultHeaders).responseJSON { response in
switch response.result {
case .success(let results):
completion(.success, results as? NSObject)
break
case .failure:
completion(.error, nil)
break
}
}
}
When the call was successful, and the dataobject was added to a array with the new message in it, I'm adding the new cell in this part:
func insertRowChatView(chatContent: [ChatMessage]) {
if chatContent.count > self.chatMessageArray.count {
self.chatMessageArray = chatContent
let indexPath: IndexPath = IndexPath(row: self.chatMessageArray.count - 1, section: 0)
self.tableView.insertRows(at: [indexPath], with: .none)
self.tableView.reloadRows(at: [indexPath], with: .none)
self.tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
}
}
And my cellforrowatindexpath code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (chatMessageArray.count) > indexPath.row {
if chatMessageArray[indexPath.row].dialogOptions != nil && chatMessageArray[indexPath.row].dialogOptions!.count > 0 {
// Dialogoptions cell
let cell = tableView.dequeueReusableCell(withIdentifier: "ChatDialogOptionCell", for: indexPath as IndexPath) as! ChatDialogOptionCell
cell.delegate = self
cell.setupCell(dialogOptions: chatMessageArray[indexPath.row].dialogOptions!, text: chatMessageArray[indexPath.row].text!, lastItem: chatMessageArray[indexPath.row].lastItem)
return cell
} else if chatMessageArray[indexPath.row].links != nil && chatMessageArray[indexPath.row].links!.count > 0 {
// Links cell
let cell = tableView.dequeueReusableCell(withIdentifier: "ChatLinkCell", for: indexPath as IndexPath) as! ChatLinkCell
cell.delegate = self
cell.setupCell(links: chatMessageArray[indexPath.row].links!, text: chatMessageArray[indexPath.row].text!, lastItem: chatMessageArray[indexPath.row].lastItem)
return cell
} else if chatMessageArray[indexPath.row].text != nil && chatMessageArray[indexPath.row].text != "" {
// Text only
let cell = tableView.dequeueReusableCell(withIdentifier: "ChatTextOnlyCell", for: indexPath as IndexPath) as! ChatTextOnlyCell
cell.setupCell(text: chatMessageArray[indexPath.row].text!, askedByUser: chatMessageArray[indexPath.row].askedByUser, lastItem: chatMessageArray[indexPath.row].lastItem)
return cell
} else {
// Typing indicator
let cell = tableView.dequeueReusableCell(withIdentifier: "ChatTypingIndicatorCell", for: indexPath as IndexPath) as! ChatTypingIndicatorCell
cell.setupCell()
return cell
}
}
let cell = UITableViewCell()
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
}
Is there a problem with my code, causing this performance problem?
Thanks for helping out!

Send different nibs in a function swift 3

I have a tableView who need to contain two different view, the name of the first one is CustomTableViewCell the second one is CustomDeliveryTableViewCell
I want my variable to take the two cell, I don't understand this error.
Here my function
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
var cell: UITableViewCell
cell = Bundle.main.loadNibNamed("CustomTableViewCell", owner: self, options: nil)?.first as! UITableViewCell
if (!self.appReady)
{
return cell
}
let arrayOfCard = self.selectedCard(section: indexPath.section, row: indexPath.row)
let json:JSON = JSON(arrayOfCard)
if (json[0]["cards"][indexPath.row]["category"] == "delivery")
{
cell = Bundle.main.loadNibNamed("CustomDeliveryTableViewCell", owner: self, options: nil)?.first as! CustomTableViewCell
}
cell = fillCell(cell: cell, json: json, index: indexPath.row)
return cell
}
My fonction fillCell is prototype like that
func fillCell(cell: CustomTableViewCell, json:JSON, index:Int) ->
CustomTableViewCell
Edit
Here the code of actual fillCell function
func fillCell(cell: UITableViewCell, json:JSON, index:Int) -> UITableViewCell {
if (json[0]["cards"][index]["category"] == "train")
{
if let type = json[0]["cards"][index]["category"] as JSON?
{
cell.labelType.text = type.string
}
if let departureStation = json[0]["cards"][index]["train"]["departure"]["station"] as JSON?
{
cell.labelDepartureStation.text = departureStation.string
}
// Do some code
}
else if (json[0]["cards"][index]["category"] == "delivery")
{
//Do some code
return cell
}
else{
//Do some code
return cell
}
}
Your initial assignment creates a cell of type CustomDeliveryTableViewCell.
Within the if block you're trying to assign a CustomTableViewCell to the same variable. This will only work if CustomTableViewCell is a subclass of CustomDeliveryTableViewCell
When you call fillCell( it's expecting CustomTableViewCell, but cell is a CustomDeliveryTableViewCell
If you declare var cell: UITableViewCell then you can assign either type to it.

Get indexPath in UITableViewCell subclass

I have a subclass of UITableViewCell that is shown in a TableView. Each cell has a text field. When the textFieldDidEndEditing func is called, I want to save the entered text as an attribute of an NSManagedObject in my Managed Object Context.
This function is implemented in my tableViewCell class:
func textFieldDidEndEditing(textField: UITextField) {
let viewController = ViewController()
let indexPath: NSIndexPath!
viewController.updateCommitsInMOC(self, atIndexPath: indexPath!)
}
And this is the function it calls. This function is implemented in my ViewController class, the one that controls the TableView which is made up of the tableViewCells:
func updateCommitsInMOC(cell: CommitTableViewCell, atIndexPath indexPath: NSIndexPath) {
// Fetch Commit
let commit = fetchedResultsController.objectAtIndexPath(indexPath) as! Commit
// Update Cell
commit.contents = cell.commitContents.text!
if cell.repeatStatus.selectedSegmentIndex == 1 { commit.repeatStatus = true }
saveManagedObjectContext()
}
I'm of course open to any suggestions as to other ways to implement the saving behavior every time the user is done editing the text field.
Is your question "How do I get the IndexPath"? Instead of the UITableviewCell trying to figure out what it's indexPath is in textFieldDidEndEditing, why don't you just figure it out within updateCommitsInMOC function?
Assuming you have a reference to your tableView you can just do this
func updateCommitsInMOC(cell: CommitTableViewCell) {
guard let indexPath = tableView.indexPathForCell(cell) else {
return
}
// Fetch Commit
let commit = fetchedResultsController.objectAtIndexPath(indexPath) as! Commit
// Update Cell
commit.contents = cell.commitContents.text!
if cell.repeatStatus.selectedSegmentIndex == 1 { commit.repeatStatus = true }
saveManagedObjectContext()
}
You can add a tag as row in cell textField.
like this:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("idCell")
cell.textField.tag = indexPath.row
return cell
}
and the textField delegate:
func textFieldDidEndEditing(textField: UITextField) {
let viewController = ViewController()
let indexPath = NSIndexPath(forRow: textField.tag, section: 0)
viewController.updateCommitsInMOC(self, atIndexPath: indexPath!)
}
or you can use the superview:
func textFieldDidEndEditing(textField: UITextField) {
let view = textField.superview!
let cell = view.superview as! UITableViewCell
let viewController = ViewController()
let indexPath = itemTable.indexPathForCell(cell)
viewController.updateCommitsInMOC(self, atIndexPath: indexPath!)
}
I suggest you to use in your tableview the
setEditing(editing, animated: animated) method.
Then inside of it you can manage the single object retrieving it from the fetchResultController.indexPathForObject(inputObject) or as you used fetchedResultsController.objectAtIndexPath(indexPath).
Finally you can use self.managedObjectContext.saveToPersistentStore() or self.managedObjectContext.save().

Swift: How to reload row height in UITableViewCell without reloading data

I have a case in which I have to reload only height of a UITableViewCell.
but if I call the function
tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: webView.tag, inSection: 0)], withRowAnimation: .Automatic)
it reloads the height as well as the data of the cell.
How can i just control the height of cell in Swift?
This is my cellForRow block :
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! CustomTableViewCell1
cell.heading.text = headerText
return cell
}
else if indexPath.row == 1 {
let cell = tableView.dequeueReusableCellWithIdentifier("cell2", forIndexPath: indexPath) as! CustomTableViewCell2
ImageLoader.sharedLoader.imageForUrl(self.headerImage , completionHandler:{(image: UIImage?, url: String) in
cell.mainImage.image = image
})
return cell
}
else if indexPath.row == 2 {
let cell = tableView.dequeueReusableCellWithIdentifier("cell3", forIndexPath: indexPath) as! CustomTableViewCell3
//cell.aurthorImage.image = UIImage(named : "obama")
ImageLoader.sharedLoader.imageForUrl(self.headerImage , completionHandler:{(image: UIImage?, url: String) in
cell.aurthorImage.image = image
})
cell.aurthorImage.tag = aurthorID
cell.aurthorImage.layer.cornerRadius = cell.aurthorImage.frame.height/2
cell.aurthorImage.clipsToBounds = true
cell.aurthorImage.userInteractionEnabled = true
cell.aurthorImage.addGestureRecognizer(aurthorImageTapRecignizer)
cell.aurthorName.text = self.authorName
cell.time.text = self.time
self.followButton = cell.followButton
return cell
}
else if indexPath.row == 3 {
let cell = tableView.dequeueReusableCellWithIdentifier("cell4", forIndexPath: indexPath) as! CustomTableViewCell4
let htmlHeight = contentHeights[indexPath.row]
cell.webElement.tag = indexPath.row
cell.webElement.delegate = self
cell.webElement.loadHTMLString(HTMLContent, baseURL: nil)
cell.webElement.frame = CGRectMake(0, 0, cell.frame.size.width, htmlHeight)
return cell
}
else if indexPath.row == 4 {
let cell = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! CustomTableViewCell1
cell.heading.text = "Related Posts"
return cell
}
else if indexPath.row == 5{
let cell = tableView.dequeueReusableCellWithIdentifier("cell6", forIndexPath: indexPath) as! CustomTableViewCell6
return cell
}
else if indexPath.row == 6 {
let cell = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! CustomTableViewCell1
cell.heading.text = "Comments"
return cell
}
else if indexPath.row == 7 {
let cell = tableView.dequeueReusableCellWithIdentifier("cell5", forIndexPath: indexPath) as! CustomTableViewCell5
let htmlHeight = contentHeights[indexPath.row]
self.commentSection = cell.commentsView
self.commentSection.tag = indexPath.row
self.commentSection.delegate = self
let url = NSURL(string: commentsURL)
let requestObj = NSURLRequest(URL: url! )
self.commentSection.loadRequest(requestObj)
self.commentSection.frame = CGRectMake(0, 0, cell.frame.size.width, htmlHeight)
commentSectionDidNotLoad = false
return cell
}
else {
let cell = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! CustomTableViewCell1
cell.heading.text = headerText
return cell
}
You can use this code to update the cell's height without reloading their data:
tableView.beginUpdates()
tableView.endUpdates()
You can also use this method followed by the endUpdates method to animate the change in the row heights without reloading the cell.
UITableView Class Reference
start an update of the tableview and then end it without changing anything.
tableView.beginUpdates()
tableView.endUpdates()
This should make the tableview set the new height
You can regulate the height by either implementing the (a)heightForRowAtIndexPath with logic setting the heights or (b)with auto layout and automatic tableview row height
A.
override func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
if [your condition, row == 5 in your comment] {
return 100
} else {
return 40
}
}
Whenever you want to change the height you would just call these two rows
tableView.beginUpdates()
tableView.endUpdates()
B.
in viewDidLoad
tableView.estimatedRowHeight = 40.0
tableView.rowHeight = UITableViewAutomaticDimension
and then you can just expose the layout constraint that set's the cells height and access it to set the height when you want
cell.heightConstraint.constant = 100
tableView.beginUpdates()
tableView.endUpdates()
About your question for example, to give a specific height dimension :
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.row == 3 {
return 50.0
}
return 72.0
}
But I think you have a webView inside a cell so, generally, to calculate the dynamic height of a UITableViewCell with a UIWebView:
(this example have two webViews)
class MyTableViewController: UITableViewController, UIWebViewDelegate
{
var content : [String] = ["<!DOCTYPE html><html><head><title>Page Title</title></head><body><h1>My First Heading</h1><p>My first paragraph</p></body></html>", "<HTML><HEAD><TITLE>Coca-Cola</TITLE></HEAD><BODY>In Chinese, Coca-Cola means Bite the Wax Tadpole</BODY></HTML>"]
var contentHeights : [CGFloat] = [0.0, 0.0]
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("myCustomCell", forIndexPath: indexPath) as! MyCustomCell
let htmlString = content[indexPath.row]
let htmlHeight = contentHeights[indexPath.row]
cell.webView.tag = indexPath.row
cell.webView.delegate = self
cell.webView.loadHTMLString(htmlString, baseURL: nil)
cell.webView.frame = CGRectMake(0, 0, cell.frame.size.width, htmlHeight)
return cell
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
return contentHeights[indexPath.row]
}
func webViewDidFinishLoad(webView: UIWebView)
{
if (contentHeights[webView.tag] != 0.0)
{
// height knowed, no need to reload cell
return
}
contentHeights[webView.tag] = webView.scrollView.contentSize.height
tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: webView.tag, inSection: 0)], withRowAnimation: .Automatic)
}
}
You can use the view height constraint in the cell, by updating the view in cell height you can update the specific cell height.
let height = cell.Height_constraint.constant
cell.Height_constraint.constant = height + 200 //200 you can use any number
Height_constraint: is the height constraint of subView in my cell.

Custom Checkbox AccessoryView for UITableView

I am using Custom AccessoryView for UITableViewCell. I am using a checkbox. Here, I want multiple selection. The code works fine while selection but while deselecting it I am having issues. I am not sure what I should be writing in cellForRowAtIndexPathMethod. The issue I am facing is, after deselecting the checkbox, I cannot select it again. Here is my code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let fourthCell = tableView.dequeueReusableCellWithIdentifier("fourthCell", forIndexPath: indexPath) as! ItemDetailFourthTableViewCell
if indexPath != self.lastIndexPath {
fourthCell.accessoryView = UIImageView(image: UIImage(named: "checkBoxPassive.png"))
fourthCell.accessoryView?.tag = 2
}
if indexPath == self.lastIndexPath {
fourthCell.accessoryView = UIImageView(image: UIImage(named: "checkBoxActive.png"))
}
return fourthCell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
self.isSelected = true
self.lastIndexPath == indexPath
if indexPath.section == 1 {
let cell = tableView.cellForRowAtIndexPath(indexPath) as! ItemDetailFourthTableViewCell
print("The cell here is \(cell)")
print("Here the cell accessoryView is \(cell.accessoryView?.tag)")
if cell.accessoryView?.tag == 0 {
print("It is active")
cell.accessoryView = UIImageView(image: UIImage(named: "checkBoxPassive.png"))
} else if cell.accessoryView?.tag == 2 {
print("It is passive")
cell.accessoryView = UIImageView(image: UIImage(named: "checkBoxActive.png"))
}
}
}