UITableView - setting cell contents based on index path - xcode

- (UITableViewCell *)tableView:... cellForRowAtIndexPath:... {
// init and sanity check
if (indexPath.row == 0) {
// make cell look like a section header
// easier and less complex than using a custom header view
NSLog(#"header");
// should prove that only four cells get the bg image
// and yet, almost every time the table is reloaded
// one more cell unexpectedly gets the background
// without putting another line in the debugger
} else {
// normal cells after row 0
// BUG: some of these are getting the row 0 background
NSLog(#"row");
// firing exactly the right number of times --
// once for each row with an index greater than 0
// so why are some of these cells getting the header bg?
// when these cells get the headers' contents
// they behave exactly as one would expect
// except that they should be the normal rows
}
// misc stuff, not causing problems
return cell;
}
Short of forcing the user to completely relaunch the app just to have different data dumped into the table, I can't figure out how to fix the bug.
The problem is less severe if I collapse every section (that is, empty the expandedSections set and reload, leaving only the pseudo-headers visible), but it doesn't go away.
Edit:
Initial load: screenshot
After reloading: screenshot
Links instead of images because it's an iPad app.
Using some dummy content for testing.
Is this any help? I know there needs to be more code for serious help, but I don't know what else to add, short of a link to the code for the entire view.

Have you tried setting a different cell identifier for the first cell and another one for the rest of them?
The tableView reuses the same cell type when creating new cells so it might get them mixed up. By using different cell identifiers for each type, it will know exactly when to use each cell.

It's probably related to the cell cache...something about setting the image for row == 0 and not clearing it otherwise...but with the limited amount of code you're showing it's hard to be more specific.

Related

In the Format cells dialog box, under category General, how to remove 0 from the sample box?

When we are pulling values from another sheet, if there is nothing in a particular cell, then it should return the same instead of returning Zero
I tried to remove the zero from sample section. but i doesn't know how to do that

I want to print 90,000 toString lines to a TextField in JavaFX. It's taking super long. Any fixes?

I'm using an iterator to iterate through a Linked List of 90,000 Book Objects. Each of them have titles, ISBNs, authors and prices.
In my GUI, I have a text field, and a button which would display these 90,000 Books.toString() in the text field. The user can search for a book, remove a book, or update fields in the book, so I feel like the user should be able to see all the books and all their fields.
The problem is, this takes way too long, I get the beach ball of death and it never loads. When I change the for loop to 10 or 1000, it works fine. Must be an O(n^2), right?
Or is there some other problem?
Thank you.
#FXML
void refreshListButtonPressed(ActionEvent event) {
listBooksTextArea.clear();
bookbag.myIter.reset();
for(int i = 0; i < 1000; i++) {
listBooksTextArea.appendText(bookbag.myIter.getCurrent().getData().toString() +
"\n");
bookbag.myIter.nextLink();
if(bookbag.myIter.atEnd()) {
listBooksTextArea.appendText(bookbag.myIter.getCurrent().getData().toString()+
"\n");
}
}
}
Ideally, I wouldn't even use a for loop, I'd use while (!bookbag.myIter.atEnd()), but the for loop proves to me the code works and the size or efficiency is the issue.
Your code is inefficient in a couple of different ways:
The TextArea stores the text as a String, which is an immutable object. Consequently, each time you call appendText(...) a new String is created, by copying the existing characters plus the new characters to a new string object. Since the size of the string grows essentially linearly on each iteration, this becomes an O(n^2) operation.
The TextArea creates a UI node for the entire text. UI nodes are generally quite expensive, and here you are creating a huge node to lay out, style, and display all 90,000 lines of text, the vast majority of which are not visible on the screen at any given time.
You can at least partially fix the first by concatenating the string in a StringBuilder, and then setting the text of the TextArea once, though this will not fix the second issue.
The preferred approach here is to use a virtualized control, such as a ListView or TableView. These controls create a limited number of cells (one per row in the case of a ListView, one per column per row in the case of a TableView), essentially creating cells only for the visible data. As the user scrolls around, cells are reused to display new data. This greatly enhances performance.
Additionally, these controls probably allow for a better interaction between your UI and your actual data. Using a TableView, each row would represent a Book object, and each cell in the row a property of that object. If you make your table editable, you can validate changes to each cell, i.e. on a property-by-property basis. Using a text area, you would need to carefully parse the changes to the text to make sure the result was a valid list of books.
I would generally recommend working through a tutorial on TableView, e.g. this one. In brief, create a Book class using JavaFX Properties. Create a TableView<Book> and configure the columns to point to appropriate properties using the setCellValueFactory(...) method. You can make the data editable by supplying, for example, a TextFieldTableCell in the cell factory for the column. Then simply add your Book instances to the table's items list.

D3 Brush Custom Handles null selection visibility

I am trying to figure out how to have custom brush handles highlight data in a range equal to a single value, which seems to result in a null selection.
Here the Plunkr I recreated on one of Mike Bostock's examples. It's in Angular 2/ionic, so if that's an issue, please go here to view his example's plain javascript. The primary thing I edited was commenting out where he hides the custom handles with css when there is a null selection.
if (s == null) {
//handle.attr("display", "none");
//circle.classed("active", false);
}
http://plnkr.co/edit/tRyhlJ
https://bl.ocks.org/mbostock/4349545
If you look in the plunkr example, you can see that if you click and let go the brush is still a perfect sphere, with the line where you clicked. Instead if disappearing, I want the brush handles to stay... and not cause errors when you click to expand the handles.
The error I am currently getting, and can't seem to find a workaround, is this:
How do I gracefully ignore this internal d3 error and continue on letting my selection expand?
I've tried all sorts of things like turning pointer-events off when it's in this state, and manually unhiding/resizing the default handles, to no avail. Every time, when I click the handles I get this error.
As far as I can tell, there's no "clean" way to deal with it. The reason is that a single click defines a range whose size is 0, which the brush considers an empty range (see source) and so it purposely nullifies the selection (see source).
That all means that unless you create your own version of d3-brush to do what you want, there's no way to have an empty selection that's not null nor a way to render a brush for an empty selection.
There's one workaround I can think of: when you detect an empty selection (where s == null) use brush.move to set the selection to something. That something would have to be a range whose size is not 0 (because if you make it 0 then d3, again, would consider that an empty selection and nullify it). To make a non-zero selection that looks like a zero-sized selection (which it has to be, because it's being defined by a single click event) you'd have to make it a tiny selection, eg [123, 123.0001]. Instead of 123, you need to know the mouse position of the click, which you can get using d3.mouse. Putting it all together, it looks like this:
if (s == null) {
var mousex = d3.mouse(this)[0]
gBrush.call(brush.move, [mousex, mousex+.001]);
}

Adding, Removing, and Caching SKNodes

Background info:
I'm creating a SpriteKit 2D platform-style game with multiple "floors". When the user enters a "portal" he is transported to another floor (either one up or one down). If the user dies at a different floor from where the most recent spawnpoint is, he is transported back to the floor with the spawnpoint.
Problem: After upgrading to iOS8, this scenario causes the game to crash with an EXC_BAD_ACCESS exception/ error. Even after going through multiple tutorials on how to debug such errors, I cannot seem to find the problem.
So I'd really appreciate if someone could take a look at my code and tell me if they see a better way of doing what I'm doing, preferably without causing the game to freeze.
Game layout:
I have a custom class TileMapLayer which is based on the same class from Ray Wenderlich's book iOS Games By Tutorials. It is basically a custom SKNode class containing multiple 32x32p tiles, combined creating the background for my game.
I load the appropriate floor when the game begins, and then call to load another floor when my user goes up/down. In this scenario, I cache the current floor first in a NSDictionary so I can access it later if the user returns to this floor.
// Cache the current floor
[_allFloors setObject:_bgLayer forKey:[NSString stringWithFormat:#"floor_%tu", (unsigned)_currentFloor]];
// Remove the current floor
[_bgLayer removeFromParent];
// Get the cached (next) floor if it exists, if not create it
TileMapLayer *cachedFloor = [_allFloors objectForKey:[NSString stringWithFormat:#"floor_%tu", (unsigned)(_currentFloor + 1)]];
if (!cachedFloor) {
_bgLayer = [self createScenery:[NSNumber numberWithInteger:(_currentFloor + 1)]];
NSLog(#"creating new floor");
} else {
_bgLayer = cachedFloor;
NSLog(#"getting cached floor");
}
// Display the new floor
[_worldNode addChild:_bgLayer];
// Increment the floor number
_currentFloor++;
(I have a similar method for going down a floor as well).
This works perfectly, both before and after upgrading to iOS8.
When the user dies, he is transported back to the last spawnpoint. If the last spawnpoint is on a different floor, the floor also changes appropriately.
I call this custom SKAction on the ball itself as a part of an animation:
SKAction *changeFloor = [SKAction runBlock:^{
if (self.spawnFloor != _currentFloor) {
[_bgLayer removeFromParent];
_bgLayer = [_allFloors objectForKey:[NSString stringWithFormat:#"floor_%tu", (unsigned)self.spawnFloor]];
[_worldNode addChild:_bgLayer];
_currentFloor = self.spawnFloor;
NSLog(#"Made it here");
}
}];
As you can see there isn't much difference. The Made it here gets logged to the console, but the game freezes immediately after. The next method I call (to move the ball to the correct location) is not executed at all.
Just for fun I tried caching the _bgLayer before removing it from its parent like so:
if (self.spawnFloor != _currentFloor) {
[_allFloors setObject:_bgLayer forKey:[NSString stringWithFormat:#"floor_%tu", (unsigned)_currentFloor]];
...
And for some reason the game does not freeze when I do this. However, the floors end up being mixed as if the _bgLayer never was removed from its parent (note: the "old" tiles does no longer react to any physics-simulations, they're just in the background doing nothing).
I also tried [_bgLayer removeAllChildren] (removing the individual tiles) right after removing from the parent:
[_bgLayer removeFromParent];
[_bgLayer removeAllChildren];
But this causes the _bgLayer to be empty when I return to this floor after respawning. As if I removed all the nodes before I stored it in the dictionary. My guess is that the dictionary only references the location of the _bgLayer, not the content itself. So when I remove all the children on screen, I effectively remove all of them in the cache as well.
Wrapup:
I know this is a long question, and I'd like to say thank you if you made it this far. If you have any questions, please don't hesitate to ask them in the comments below.
Ultimately, what I'd like to know is this: How can I resolve my problem so the game doesn't freeze? What's the best practice for caching the floors without causing memory problems? What's the best practice for removing and adding the nodes on screen, and always referring to the current floor (the one that's on screen) as _bgLayer?
Again, thank you so much!
iOS8 seems to behave different when removing nodes inside blocks, as if you tried to do it on a background thread so sometimes it causes strange crashes. It might be a bug but until then I'd suggest two things:
1.- Mark the node to be removed inside the block but do it on the update: loop. You will not notice any difference.
2.- Make sure the removeFromParent happens in the main thread. But not sure this will fix the problem.
__strong typeof(_bgLayer) strongLayer = _bgLayer;
dispatch_async(dispatch_get_main_queue(), ^{
[strongLayer removeFromParent];
});

Does anyone know why an object would miss a property?

We have a script that export our Indesign documents to HTML and one of the routine is to export tables. In this script we go throught each Tables->Rows->Cells and evaluate some of the properties (i.e. bottomEdgeStrokeType, topEdgeStrokeType, etc...) and transport them to HTML.
Now yesterday we had problem converting one particular document because some cells were missing the "bottomEdgeStrokeType" property entirely. I've discovered this by outputting the properties of each cells and compare the faulty ones with the others.
This line bellow was trowing the error: "Invalid object for this request.".
var cellType = cell["bottomEdgeStrokeType"];
Now, to fix this I've wrapped this around a try catch block to handle the case when it's not there, but now what is puzzling me is how on earth can Extendscript instantiate an object with missing properties?
Indesign version: CS5.5
A property is not only 'undefined' if it cannot exist at all (such as asking for the parent text frame for a character in overset text), but InDesign's Javascript engine also fails to return a reasonably accurate result for multiple values.
If you ask for "the" point size of a paragraph, where this paragraph contains multiple sizes, poor ID does not consider to return something like CONSTANT.Mixed, or the first value only, or (what I might have preferred) an array of the values; it returns undefined instead.
So how can a single table cell have multiple bottom strokes? If the cell underneath it is split into multiple cells, and one has a "top" stroke but the other has not.
It's difficult to recommend an adequate solution. You could first test if the current cell is "merged" (as far as InDesign's internal table model is concerned) with columnSpan; and if so, iterate over the number of columns spanned and test the next row's cells for their top stroke, which in theory should match the bottom stroke of the cell above. (I find myself wondering if this is always true. ID's table model is ... weird. It's not entirely like a HTML table, despite the functional overlaps.)
If columnSpan is greater than 1 and equal to the number of cells immediately below the current one, you could test if all of their "top" values are the same and if so use that value. (I never tested this so ID's table model may simply fail because a cell is merged, regardless of same-values or not.)
One could attempt to flag this cell's next row to output "top" strokes as well -- but alternating top and bottom strokes may not align nicely in CSS, side to side. Perhaps it's best to translate only the first top stroke value to "the" bottom stroke property for your current cell, and fix up manually where needed (how?) or, a reasonable action, hope that no-one will ever notice it.

Resources