I am trying to create a general diffing function for doing batch updates in table views. Basically, it takes two arrays of sections, each containing an identifier for each section and an identifier for each row in each section, and calculates which sections to delete, insert or move and which individual rows to delete, insert or move. I.e., suitable input for deleteSections(_:with:), insertSections(_:with:), moveSection(_:toSection:), deleteRows(at:with:), insertRows(at:with:) and moveRow(at:to:).
I thought this could be done quite generally, but it seems I've found limits to what can be done, and I wanted to just check if I'm missing something.
So let's say I have two sections, "Fruits" and "Vegetables", containing one element each: "Banana" in Fruits, "Carrot" in Vegetables.
Let's say I want to switch so that suddenly a banana is a vegetable and a carrot is a fruit. Easy enough, I generate a moveRow(at: [0, 0], to: [1, 0] and a moveRow(at: [1, 0], to: [0, 0]) and update my data source accordingly; the rows will switch place.
Now, let's say instead I want the two sections to switch places. I will do a moveSection(0, toSection: 1) (or I can do moveSection(1, toSection: 0, or both - it doesn't matter in this case). Ok. The carrot row now moves along with the vegetables sections and the banana with the fruits.
But now... I'd like to do both of these things at the same time. The sections should switch places, but the items should stay put - or, to put it differently, they should switch which logical section they belong to but keep their physical row. This does not seem possible.
I've tried to do the moveSection and at the same time (== within the same beginUpdate/endUpdate) do moveRow(at: [0, 0], to: [0, 0]) and moveRow(at: [1, 0], to: [1, 0]), but these calls to moveRow are just the no-ops they appear to be.*
I've also tried to do deleteRows/insertRows to make the rows stay where they are; that instead gives me a crash:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Attempt to create two animations for cell'
reloadRows doesn't work either; afaik that does the same as an deleteRows/insertRows pair.
So, basically my question is:
Am I missing something? Is this possible?
(Not interested in workarounds like reconfiguring the cells or headers with the new data, I don't actually have a situation where I need to do this, I just want to know!)
Demo code if anyone wants to play around.
(* Actually, they are just no-ops when combined with both moveSection(0, toSection:1) and moveSection(1, toSection:0); if only one of these are performed, we get that internal consistancy crash!)
Change your switchBothNoOp() into this:
func switchBothNoOp() {
switch state {
case .first:
sections = [("Fruits", ["Banana"]),
("Vegetables", ["Carrot"])]
case .second:
sections = [("Vegetables", ["Banana"]),
("Fruits", ["Carrot"])]
}
tableView.moveSection(0, toSection: 1)
}
Then you move rows later using another begin/end updates block. For example:
func update() {
tableView.beginUpdates()
// Pick on of these:
// switchItems()
// switchSections()
switchBothNoOp()
// switchBothCrash()
tableView.endUpdates()
tableView.beginUpdates()
tableView.moveRow(at: [0, 0], to: [1, 0])
tableView.moveRow(at: [1, 0], to: [0, 0])
tableView.endUpdates()
}
Related
If I am using all of column A for example,
I want to do this:
IF cell value is between 30-34 then * 1.5, IF cell value is between 35-39 then * 2, IF cell value is between 40-44 then * 2.5???
You need to use nested if statements ...
If(and(A1>=30,A1<=34), A1*1.5, if(And(a1>=36,A1<=39),A1*2, if(and(A1>=40,A1<=44),A1*2.5,"")))
Paste the above into cell H1 and drag the formula down.
At some point, nested IF statements become unwieldy and a lookup to hard-coded values or a lookup table is more efficient.
=A2*lookup(A2, {0, 30, 35, 40}, {1, 1.5, 2, 2.5})
How could I load more data while scrolling to the top without losing the current offset oh the UITableView?
Here is what I am trying to achieve:
This is the whole set of data:
row 1
row 2
row 3
row 4
row 5
row 6
row 7
row 8*
row 9
row 10
row 11
row 12
row 13
row 14
row 15
Now, imagine that the user loaded the ones marked in bold and the offset is at row 8, if the user scroll up and reached row 7, I want to load and insert the rows from 1 to 5 without jumping from row 7. Keeping in mind that the user may be scrolling so when data reached the phone it is at row 6, so I can't jump it back to row 7, but keep the scroll smooth and natural (just how happen when you load more data while scrolling down, that the data is reloaded without the tableview jumping from between rows).
By the way, by offset I mean the contentOffset property of the UITableView.
Thanks for your help, I do really appreciate it!
When you are updating your data, you need to get the current offset of the tableView first. Then, you can add the height of the added data to the offset and set the tableView's offset like so:
func updateWithContentOffsset(data: [String]) {
guard let tableView = tableView else {
return
}
let currentOffset = tableView.contentOffset
let yOffset = CGFloat(data.count) * tableView.rowHeight // MAKE SURE YOU SET THE ROW HEIGHT OTHERWISE IT WILL BE ZERO!!!
let newOffset = CGPoint(x: currentOffset.x, y: currentOffset.y + yOffset)
tableView.reloadData()
tableView.setContentOffset(newOffset, animated: false)
}
You can also take a look at the gist that I created. Simply open up a new iOS Playground and copy-paste the gist.
The only thing you have to be aware of is that make sure you know your row height to add to the offset.
#Pratik Patel Bellow answer is best one.
Right down or call bellow function in the web-service response.
func updateWithScroll(data: [String]) {
guard let tableView = tableView else {
return
}
guard let currentVisibleIndexPaths = tableView.indexPathsForVisibleRows else {
return
}
var updatedVisibleIndexPaths = [IndexPath]()
for indexPath in currentVisibleIndexPaths {
let newIndexPath = IndexPath(row: indexPath.row + data.count, section: indexPath.section)
updatedVisibleIndexPaths.append(newIndexPath)
}
tableView.reloadData()
tableView.scrollToRow(at: updatedVisibleIndexPaths[0], at: .top, animated: false)
}
Now, imagine that the user loaded the ones marked in bold and the offset is at row 8, if the user scroll up and reached row 7, I want to load and insert the rows from 1 to 5 without jumping from row 7.
Loading data into the table as its needed is one of the things that UITableView does for you automatically. Don't try to insert rows into the table because they'll soon be needed as the user scrolls toward them -- the table view will request those rows from its data source as they're needed. You just need to make sure that your data source has the information it needs in order to fulfill the tables requests for cells as they arrive.
Things get a little more complicated if populating the rows requires making a network request for each row. Given your use of "load" in the question, I don't think that's what you're talking about, but in case that's your situation, here are some tips:
Request the data for as many rows as you reasonable can as early as you reasonably can. The amount of data displayed in a single row is typically small, so requesting a few hundred rows all at once shouldn't be a big deal.
If you don't have the data you need for a given row, make the necessary request, but return a cell immediately. The cell you return could use a spinner or other indication that the data is pending. When the request completes, you can tell the table to reload the appropriate row so that the proper content will display.
I have a collection View that I made horizontal scrolling. It has 3 rows and 5 columns. Once I enabled horizontal scrolling the cells fill up going down the columns instead of across the rows. For example i have an array 1,2,3,4,5,6,8,9,10,11,12,13,14,15,16 that i am using to fill my collection view. The cells would look like this
1,4,7,10,13,16
2,5,8,11,14
3,6,9,12,15
How can I fix this.
That's the default behavior of horizontal.If you want you can sort the array in some way so it'll be displayed the way you want it.
Like this:
var array = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
var horizontalSortedArray = [Int]()
let columns = 5
let rows = 3
for i in 0..<columns {
for j in 0..<rows {
horizontalSortedArray.append(array[j*columns+i])
}
}
print(horizontalSortedArray)
// [1, 6, 11, 2, 7, 12, 3, 8, 13, 4, 9, 14, 5, 10, 15]
Now if you use this new array as your data source in horizontal mode it'll be displayed like this:
1 ,2 ,3 ,4 ,5
6 ,7 ,8 ,9 ,10
11,12,13,14,15
I am using fro drawing my spline chart. here is my series data example array:
data: [
[1371563990000,1,530,100],[1373204470000,2,529,0],[1373464877000,0.5,531,50]
]
.
I want to access the fourth index value for the selected series. I am using this.series.data[this.myIndex].config['z'] to get the third index value but don't know how to get the fourth index value.
Thanks in advance
You need to use object, instead of array,
{
x: 1371563990000,
y: 1,
customParam1: 530,
customParam2: 100
}
Paramaters will be available in point.options.customParam1
I have a segmented control and when the user selects the first segment I want it to pass the number 0, second segment 1, and third segment 2. So that I later can do like this:
NSString *submitFormJS = [NSString
stringWithFormat:#"document.getElementById('usertype').value='**%#**';", **segment**];
where the segment is equal to the number: 0, 1 or 2.
UISegmentedControl has a property named selectedSegmentIndex that you might find useful for this.