对于tableview中,在某个位置
可能是中间某个位置
也可能是最后
插入一行
然后最后是通过:
messageTableView.reloadData()
搞定了数据的刷新问题。
因为之前没发用:
reloadRowsAtIndexPaths
否则会出错:
现在还是希望去搞懂:
对于新的insert的row,
如何在reloadData之后
去scroll到对应位置
去参考:
[已解决]Swift中如何在TableView的最后添加一行cell并且滚动到底部
去试试:
结果直接去insert并scroll:
//first do reload table data // msgTVC.messageTableView.reloadData() //then do scroll dispatch_async(dispatch_get_main_queue(), { () -> Void in //scroll to that row msgTVC.messageTableView.beginUpdates() let insertedRowIndexPath:NSIndexPath = NSIndexPath(forRow: insertedRowIdx, inSection: 0) print("insertedRowIndexPath=\(insertedRowIndexPath)") msgTVC.messageTableView.insertRowsAtIndexPaths( [insertedRowIndexPath], withRowAnimation: UITableViewRowAnimation.Automatic) msgTVC.messageTableView.endUpdates() msgTVC.messageTableView.scrollToRowAtIndexPath(insertedRowIndexPath, atScrollPosition: UITableViewScrollPosition.Middle, animated: true) }) |
还是insert时就出错:
Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (2) must be equal to the number of rows contained in that section before the update (0), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).’ |
加了reloadData:
msgTVC.messageTableView.reloadData() |
也还是同样错误。
swift tableview insert row and scroll
ios – inserting row at bottom in uitableview swift – Stack Overflow
iphone – Keep uitableview static when inserting rows at the top – Stack Overflow
Inserting row at bottom in uitableview swift – www.scriptscoop.net
Programmatic UITableView Scrolling – Grok Swift
最后经过,调试,两种办法都是可以:
第一种是:
插入新的行对应的数据之后,再在主界面的线程中reloadData和滚动到对应的新插入的行
代码如下:
//method 1: insert data and reloadData, and scroll //insert to correct position according datetime var insertedRowIdx:Int = 0 var hasInserted:Bool = false for (curIdx, curMessage) in msgTVC.messageList.enumerate() { print("[\(curIdx)] newMessage.timestamp=\(newMessage.timestamp), curMessage.timestamp=\(curMessage.timestamp), newMessage.text=\(newMessage.text)") if newMessage.timestamp < curMessage.timestamp { msgTVC.messageList.insert(newMessage, atIndex: curIdx) insertedRowIdx = curIdx hasInserted = true break } } if !hasInserted { msgTVC.messageList.append(newMessage) insertedRowIdx = msgTVC.messageList.count – 1 hasInserted = true } //reload table data dispatch_async(dispatch_get_main_queue(), { () -> Void in msgTVC.messageTableView.reloadData() let insertedRowIndexPath:NSIndexPath = NSIndexPath(forRow: insertedRowIdx, inSection: 0) print("insertedRowIndexPath=\(insertedRowIndexPath)") //scroll to that row msgTVC.messageTableView.scrollToRowAtIndexPath(insertedRowIndexPath, atScrollPosition: UITableViewScrollPosition.Middle, animated: true) }) |
其中:
由于reloadData是对于整个列表的数据都刷新的
-》感觉应该是效率不是太高
-》虽然官网文档中说了,只是刷新显示那些当前处于可见的行
-》但是至少还是刷新多行的
-》感觉应该是下面的,只刷新当然新插入的行,效率更高些
第二种是:
确保都处于主界面线程中:
先去:beginUpdates
再去插入数据源中新行的数据:messageList.insert或messageList.append
然后再去插入(界面中的)新的行:insertRowsAtIndexPaths
最后再去:endUpdates
之后就可以(随意的,按照你所希望的)去滚动到对应的新插入的行了:
scrollToRowAtIndexPath
//method 2: insert data and row, then scroll //insert/append and scroll dispatch_async(dispatch_get_main_queue(), { () -> Void in //1. beginUpdates msgTVC.messageTableView.beginUpdates() //2. insert new data for tablview row //insert to correct position according datetime var insertedRowIdx:Int = 0 //TODO: calculate correct index position, not in main UI thread, to improve perfomance var hasInserted:Bool = false for (curIdx, curMessage) in msgTVC.messageList.enumerate() { print("[\(curIdx)] newMessage.timestamp=\(newMessage.timestamp), curMessage.timestamp=\(curMessage.timestamp), newMessage.text=\(newMessage.text)") if newMessage.timestamp < curMessage.timestamp { msgTVC.messageList.insert(newMessage, atIndex: curIdx) insertedRowIdx = curIdx hasInserted = true break } } if !hasInserted { msgTVC.messageList.append(newMessage) insertedRowIdx = msgTVC.messageList.count – 1 hasInserted = true } //3. insert new row for tablview cell let insertedRowIndexPath:NSIndexPath = NSIndexPath(forRow: insertedRowIdx, inSection: 0) print("insertedRowIndexPath=\(insertedRowIndexPath)") msgTVC.messageTableView.insertRowsAtIndexPaths( [insertedRowIndexPath], withRowAnimation: UITableViewRowAnimation.Automatic) //4. endUpdates msgTVC.messageTableView.endUpdates() //scroll to that row msgTVC.messageTableView.scrollToRowAtIndexPath(insertedRowIndexPath, atScrollPosition: UITableViewScrollPosition.Middle, animated: true) }) |
即可。
两种方式,都不会再出现那种NSInternalInconsistencyException的错误了。
都可以正常刷新对应的新的列表的行,且滚动到对应的新插入的行的位置了。
如图:
新收到的消息,插入到最底部,现在可以滚动到最底部了:
[后记 2015-12-15]
后来发现,先去执行:
messageList.insert
再去:
beginUpdates+insertRowsAtIndexPaths+endUpdates
以及:
scrollToRowAtIndexPath
是可以的:
// insert new data for tablview row //insert to correct position according datetime var insertedRowIdx:Int = 0 var hasInserted:Bool = false for (curIdx, curMessage) in msgTVC.messageList.enumerate() { print("[\(curIdx)] newMessage.timestamp=\(newMessage.timestamp), curMessage.timestamp=\(curMessage.timestamp), newMessage.text=\(newMessage.text)") if newMessage.timestamp < curMessage.timestamp { msgTVC.messageList.insert(newMessage, atIndex: curIdx) insertedRowIdx = curIdx hasInserted = true break } } if !hasInserted { msgTVC.messageList.append(newMessage) insertedRowIdx = msgTVC.messageList.count – 1 hasInserted = true } //method 3: insert data and row, then scroll //insert/append and scroll dispatch_async(dispatch_get_main_queue(), { () -> Void in //1. beginUpdates msgTVC.messageTableView.beginUpdates() //2. insert new row for tablview cell let insertedRowIndexPath:NSIndexPath = NSIndexPath(forRow: insertedRowIdx, inSection: 0) print("insertedRowIndexPath=\(insertedRowIndexPath)") msgTVC.messageTableView.insertRowsAtIndexPaths( [insertedRowIndexPath], withRowAnimation: UITableViewRowAnimation.Automatic) //3. endUpdates msgTVC.messageTableView.endUpdates() //scroll to that row msgTVC.messageTableView.scrollToRowAtIndexPath(insertedRowIndexPath, atScrollPosition: UITableViewScrollPosition.Bottom, animated: false) }) |
不会报错的。
而之前为何出错,则估计是insert参数有误?
结果后续测试发现,
scrollToRowAtIndexPath
却又出错了。。。
insertedRowIndexPath=<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 – 0} 2015-12-15 17:45:38.442 JianDao[34102:1771517] *** Terminating app due to uncaught exception ‘NSRangeException’, reason: ‘-[UITableView _contentOffsetForScrollingToRowAtIndexPath:atScrollPosition:]: row (0) beyond bounds (0) for section (0).’ *** First throw call stack: ( 0 CoreFoundation 0x000000010edd9e65 __exceptionPreprocess + 165 1 libobjc.A.dylib 0x0000000111180deb objc_exception_throw + 48 2 CoreFoundation 0x000000010edd9d9d +[NSException raise:format:] + 205 3 UIKit 0x000000010fd9e059 -[UITableView _contentOffsetForScrollingToRowAtIndexPath:atScrollPosition:] + 1445 4 UIKit 0x000000010fd9ed7e -[UITableView scrollToRowAtIndexPath:atScrollPosition:animated:] + 39 5 JianDao 0x000000010e11b26c _TTSf2n_d_i___TFFC7JianDao26ConversationViewController22insertMessageAndScrollFS0_FTCS_7Message6msgTVCCS_26MessageTableViewController_T_U_FT_T_ + 748 |
搜:
swift insert new cell
swift scroll after insert new cell
swift scroll after insert cell
UITableView 在顶部insertRowsAtIndexPaths 后,如何保持当前位置不变 | iOS开发 – CocoaChina CocoaChina_让移动开发更简单
swift scrollToRowAtIndexPath insertRowsAtIndexPaths contentOffsetForScrollingToRowAtIndexPath
ios – Best way to select a row after inserting it? – Stack Overflow
UITableView _contentOffsetForScrollingToRowAtIndexPath:atScrollPosition: How to solve This issue?
swift scrollToRowAtIndexPath contentOffsetForScrollingToRowAtIndexPath
iphone – error with scrollToRowAtIndexPath – Stack Overflow
swift contentOffsetForScrollingToRowAtIndexPath
Ray Wenderlich | Tutorials for Developers and Gamers
说是collection view的bug
-》感觉此处好像也是UITableView的bug。。。
结果代码都改为了:
当list有row时,且确保要滚动的位置小于最大row:
//scroll to that row //Terminating app due to uncaught exception ‘NSRangeException’, reason: ‘-[UITableView _contentOffsetForScrollingToRowAtIndexPath:atScrollPosition:]: row (0) beyond bounds (0) for section (0).’ let messageListCount:Int = msgTVC.messageList.count print("messageListCount=\(messageListCount)") let curMaxRowNum:Int = msgTVC.tableView(msgTVC.messageTableView, numberOfRowsInSection: 0) print("curMaxRowNum=\(curMaxRowNum)") if curMaxRowNum > 0 { let curMaxRowIdx:Int = curMaxRowNum – 1 print("curMaxRowIdx=\(curMaxRowIdx)") if insertedRowIndexPath.row <= curMaxRowIdx { msgTVC.messageTableView.scrollToRowAtIndexPath(insertedRowIndexPath, atScrollPosition: UITableViewScrollPosition.Bottom, animated: false) } } else { print("can not scroll to that row, due to still not updated table data") } |
然后确保list中count也是1了:
messageListCount=1 MessageTableViewController numberOfRowsInSection curMaxRowNum=1 (lldb) po msgTVC.messageList.count 1 curMaxRowIdx=0 |
却还是出错:
Terminating app due to uncaught exception ‘NSRangeException’, reason: ‘-[UITableView _contentOffsetForScrollingToRowAtIndexPath:atScrollPosition:]: row (0) beyond bounds (0) for section (0).’
swift row (0) beyond bounds (0) for section (0)
iphone – UITableView scrollToRowAtIndexPath – Stack Overflow
swift insertRowsAtIndexPaths NSInternalInconsistencyException reason Invalid update: invalid number of rows in section 0
iphone – NSInternalInconsistencyException (invalid number of rows) – Stack Overflow
最后的最后,用的是如下代码:
//method 1: insert data and reloadData, and scroll //insert to correct position according datetime var insertedRowIdx:Int = 0 var hasInserted:Bool = false for (curIdx, curMessage) in msgTVC.messageList.enumerate() { print("[\(curIdx)] newMessage.timestamp=\(newMessage.timestamp), curMessage.timestamp=\(curMessage.timestamp), newMessage.text=\(newMessage.text)") if newMessage.timestamp < curMessage.timestamp { msgTVC.messageList.insert(newMessage, atIndex: curIdx) insertedRowIdx = curIdx hasInserted = true break } } if !hasInserted { msgTVC.messageList.append(newMessage) insertedRowIdx = msgTVC.messageList.count – 1 hasInserted = true } msgTVC.messageTableView.reloadData() //reload table data dispatch_async(dispatch_get_main_queue(), { () -> Void in let insertedRowIndexPath:NSIndexPath = NSIndexPath(forRow: insertedRowIdx, inSection: 0) print("insertedRowIndexPath=\(insertedRowIndexPath)") // //scroll to that row // msgTVC.messageTableView.scrollToRowAtIndexPath(insertedRowIndexPath, atScrollPosition: UITableViewScrollPosition.Middle, animated: true) // let messageListCount:Int = msgTVC.messageList.count // print("messageListCount=\(messageListCount)") // if messageListCount > 0 { // let lastRowIndexPath:NSIndexPath = NSIndexPath(forRow: messageListCount – 1, inSection: 0) // print("lastRowIndexPath=\(lastRowIndexPath)") // msgTVC.messageTableView.scrollToRowAtIndexPath(lastRowIndexPath, atScrollPosition: UITableViewScrollPosition.Bottom, animated: false) // } let curMaxRowNum:Int = msgTVC.messageTableView.numberOfRowsInSection(0) print("curMaxRowNum=\(curMaxRowNum)") if curMaxRowNum > 0 { let curMaxRowIdx:Int = curMaxRowNum – 1 print("curMaxRowIdx=\(curMaxRowIdx)") if (insertedRowIndexPath.row <= curMaxRowIdx) { msgTVC.messageTableView.scrollToRowAtIndexPath(insertedRowIndexPath, atScrollPosition: UITableViewScrollPosition.Bottom, animated: false) } } else { print("can not scroll to that row, due to still not updated table data") } }) |
暂时可以工作,没有挂掉。