iPhone SDK SSCollectionView load item from thread - iphone

I have an SSCollectionView which is modeled after NSCollectionView and UITableView. It calls a function to load each item at an indexPath.
I want to asynchronously load images into each one of those paths.
- (SSCollectionViewItem *)collectionView:(SSCollectionView *)aCollectionView itemForIndexPath:(NSIndexPath *)indexPath {
....
[self startLoadingItem:item indexPath:indexPath];
return item;
}
-(void)startLoadingItem:(SSCollectionViewItem *)collectionViewItem indexPath:(NSIndexPath *)indexPath
{
[SEThreadInvocation detachNewThreadSelector:#selector(loadItem:indexPath:) toTarget:self withObjects:collectionViewItem,indexPath, nil];
}
SEThreadInvocation is just a subclass of NSThread so that I can send more than one object to a selector.
-(void)loadItem:(SSCollectionViewItem *)collectionViewItem indexPath:(NSIndexPath *)indexPath {
...
collectionViewItem.imageView.image = imageToDisplay;
}
What is happening is that the code initially works, but every time the collection reloads (often) it puts images all over the collection view randomly.
I believe this is because my SSCollectionViewItem is pointing to a different memory location each time and my item is not thread safe, so it is just stamping my images all over the place when they change in memory.
Also, I thought it might be replacing the images in memory, so I replaced it with numbers and the numbers bounce all over the collection view when reloading as well.
How can I make my objects thread safe in this context?

SSToolkit has a sample Catalog project which has a SSCollectionView demo. It loads images in the way you are needing.

Related

Why are multiple touches causing my iPhone app to crash?

In my app, I am getting the row index as the user taps on the row or selected row. But if a row is tapped twice, my app crashes.
What could be causing this behavior, and how can I fix it? Here's the code I'm using:
(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
abc *xyz = [[abc alloc] init];
dcs = [allHadits objectAtIndex:indexPath.row];
hk = dcs.kokid;
[dcs release];
}
Do you mean tapping on the same row twice makes it crash? If so, it might be because of the [dcs release]. I don't know what dcs is (unless that's supposed to be xyz) but grabbing a pointer to the object in the array and then calling release on it might be releasing the object in the array, making it crash next time the row is hit. Delete the [dcs release] and see if it still crashes. My memory management isn't the greatest though so I could be wrong.
Don't release dcs. Why are your variables named so poorly?

Cocoa Touch: are dynamically XML-defined views possible?

first post. Been an iPhone developer intern for about five weeks now. I've read a lot of introductory Apress material, but please take it easy if I make some vocabulary violations. So far I've been able to find answers by searching and lurking. However I now have a task for which I can find little relevant information.
My iPhone application currently uses a rigid view hierarchy for selection of items. The MainViewController links to (err, Subviews?) a TableView for selecting any from a list of factories. Selecting a factory then loads a TableView for various statistics about that factory. The data to populate these tables loads from a remote JSON server by http.
I would like to have an XML definition of the view hierarchy on the remote server from which the application dynamically constructs the view structure. In this way the view structure is not hard-coded into the client (the iPhone ViewControllers/nibs) and offers more flexibility for reorganizing the content server-side.
Is this possible, and has anybody accomplished this? Most relevant answer was Dynamic UI in iphone, however upon reading I feel Apple's guide to Serializing/Archiving departs quickly from what I am trying to do. Can somebody explain its relevance or point to another resource?
Yes, it is entirely possible, and I (as well as others) have done something similar. Essentially, you will have to write a couple classes:
One is a Controller class to parse the XML to get the content you need upon download into a format you can use in a Model class. There are a number of OSS libraries out there, but don't use Cocoa's XML classes on the iPhone because they are just too slow when parsing. Be sure to double-check the license that comes with the OSS library so that you don't run afoul of GPL and the like.
The second class you'll need is one that will translate your model into a view hierarchy and place it on screen on the fly as the user moves through the content. This is to implement as individual steps, but the challenge is managing the content placement workflow.
The content noted in your question does depart a bit from the core task at hand but it can still be relevant to your interests. At the very least it may give you an idea on how to approach certain aspects of your project.
Here's a follow-up. I have accomplished what I wanted to do.
I have a ListViewController class and a Repository model/object. To parse XML, I used XPathQuery, Matt Gallagher's adaptation of libxml2 for Cocoa (http://cocoawithlove.com/2008/10/using-libxml2-for-parsing-and-xpath.html).
The ListViewController tracks the current XML path. When a cell in the table view is selected, the corresponding path is concatenated to the existing path. The ListViewController creates and pushes another instance of itself and PerformXMLPathQuery() returns an array to populate the table view cells. The Repository holds an NSMutableData object and an NSString with current path. Both are required for XPath Query:
NSArray *PerformXMLXPathQuery(NSData *document, NSString *query);
PerformXMLPathQuery does a lot of back work for you. This is how I used it to get what I wanted from the XML:
-(NSArray*)getListFromRepositoryWithPath:(NSString *)path {
// Get the nodes referred to by the path
NSArray *nodes = PerformXMLXPathQuery(repository.data, path);
// Build an array with the node names
NSMutableArray* sublist = [[NSMutableArray alloc] init];
for (NSDictionary* node in nodes) {
for (id key in node) {
if ([key isEqualToString:#"nodeChildArray"]) {
NSArray* subNodes = [node objectForKey:#"nodeChildArray"];
for (NSDictionary* subNode in subNodes) {
for (id subKey in node) {
if ([subKey isEqualToString:#"nodeName"]) {
[sublist addObject:[subNode objectForKey:subKey]];
}
}
}
}
}
// Ignore duplicate entries in the data
if ([sublist count] > 0) {
return [NSArray arrayWithArray:sublist];
}
}
return [NSArray arrayWithArray:sublist];
}
When a row is selected, I used didSelectRowAtIndexPath to prepare the next path and view controller:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *nextXpathQueryString = [self.xpathQueryString copy];
// Build the path string for the next view's XML path query
if ([[nextXpathQueryString lastPathComponent] isEqualToString:#"*"]) {
nextXpathQueryString = [nextXpathQueryString stringByDeletingLastPathComponent];
nextXpathQueryString = [nextXpathQueryString stringByAppendingPathComponent:#ROOTNAME];
}
nextXpathQueryString = [nextXpathQueryString stringByAppendingPathComponent:[repository.currentListToDisplay objectAtIndex:indexPath.row]];
// Navigation logic. Create and push another view controller.
ListViewController *detail = [[ListViewController alloc] init];
// Populate the new ViewController
[detail setRepository:repository];
[detail setTitle:nextXpathQueryString];
[detail setXpathQueryString:nextXpathQueryString];
[nextXpathQueryString release];
UIBarButtonItem *doneButton = [[self navigationItem] rightBarButtonItem];
[[detail navigationItem] setRightBarButtonItem:doneButton animated:YES];
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detail animated:YES];
[detail release];
}
The cool thing is each instance of ListViewController keeps its own path string and content array. No fooling around with nested tree objects or pointers.
Essentially I have a flexible XML tree browser for the iPhone. Thanks Stack Overflow.

parsing xml for iphone

can i parse my XML in this method
Blockquote
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// here some instance of XMLparser ??
}
Blockquote
like whenevrr uer press table cell then for detail view i want to pull data at that time only and for that specfic only as i have 8k data so i dont want to parse other data
You have to be careful about runtime performance. If the parse runs really fast, like 50 - 100 ms, it is kind of acceptable performance for me. If it runs too long, like > 1 second, it is absolutely sure that you shouldn't put parsing in that method.
If you talk about pulling data from network and parsing it, it is quite risky and slow for me. If you use synchronous, it will block the UI, if you use asynchronous, what will happen when user going to detail view and don't see anything. If it is just like update old data, it is acceptable to use asynchronous
I've used TouchXML and NSURLConnection in the past for doing these kind of things. You'll probably want to use the asynchronous methods so you don't block the GUI thread during the fetches.
yes i am calling in
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
switch(indexPath.section)
{
case 2:
pragma mark postcode
cell.textLabel.text = aBook.telnumber;
NSLog(#"String = %#",aBook.tel);
NSLog(#"String = %#",aBook.telnumber);
}
tel was defined in sql so that was working correct
now in xml i hav changes that tel to telnumber thats it and commnet that aBook.tel in sql query nothing else
but now its null ... do i need to add xmlparsed elemtns to books in didEndElement in PArsing class??

NSFetchedResultsController not updating UITableView's section indexes

I am populating a UITableViewController with an NSFetchedResultsController with results creating sections that populate section headers and a section index. I am using the following method to populate the section index:
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return [fetchedResultsController_ sectionIndexTitles];
}
and now I've run into a problem. When I add a new element to the NSManagedObjectContext associated with the NSFetchedResultsController, the new element is saved and appropriately displayed as a cell in the UITableView ... except for one thing. If the new element creates a new SECTION, the new section index does not show up in the right hand margin unless I pop the UINavigationController's stack and reload the UITableViewController.
I have conformed to the NSFetchedResultsControllerDelegate's interface and manually invoke
[self.tableView reloadSectionIndexTitles];
at the end of both these delegate methods:
controller:didChangeSection...
controller:didChangeObject...
and while I can debug and trace the execution into the methods and see the reload call invoked, the UITableView's section index never reflects the section changes.
Again, the data shows up - new sections are physically visible (or removed) in the UITableView but the section indexes are not updated.
Am I missing something?
Looks like this is a bug we're all having. See http://iphonedevelopment.blogspot.com/2009/11/i-know-youre-tired-of-hearing-about.html for what looks to me like a fairly nasty too-many-lines-of-code solution. I went with this:
- (void)viewWillAppear:(BOOL)animated; {
// This is a dumb hack required by this bug: http://iphonedevelopment.blogspot.com/2009/11/i-know-youre-tired-of-hearing-about.html
[self.tableView reloadData];
}
It may be inefficient but unless you have reams and reams of data it probably won't do any harm. And it's only 1 line of code. So, when apple fixes their bug, you can easily take it out.
Question already 2 months old, but I ran into the same problem today. It seems like -reloadSectionIndexTitles is not working at all, so I tried a couple of potential hacks which of the following works for me:
#implementation UITableView (JKAdditions)
- (UIView *)indexView {
Class indexClass = NSClassFromString(#"UITableViewIndex");
for(UIView *subview in self.subviews){
if([subview isKindOfClass:indexClass]) return subview;
}
return nil;
}
- (void)reloadSectionIndexTitles {
UIView *indexView = [self indexView];
[indexView performSelector:#selector(setTitles:) withObject:[self.dataSource sectionIndexTitlesForTableView:self]];
[indexView setNeedsDisplay];
}
#end
I really have no idea if Apple would reject your App because of this hack, but it seems like the only option for me. Reloading the whole tableView is simply not what I want since I then have to deal with all kinds of animation problems.
I hope this helps anyone having the same problems!
To combine the accepted answer with Alex Reynolds's answer with the delay, just call reloadData with a delay that corresponds to the animation duration, so 0.4 or 0.3 seconds.
In my case, I stick the delayed method call into controller:didChangeSection:atIndex:forChangeType: (it's a Core Data app).
The result, when a section is added or deleted, is the standard animation of the cell, followed by the index being updated when the data is reloaded.
It's ugly and makes me cringe, but I am okay with the result. I also submitted a bug to Apple, #8589547.
Try putting it at the end of -controllerDidChangeContent:, somewhere after [self.tableView endUpdates].
Another thing I do (that works for me, can't guarantee it will work for you) is perform a selector after a very short delay, e.g.:
[self performSelector:(#selector(refreshSectionIndex)) withObject:nil afterDelay:0.2];
// ...
- (void) refreshSectionIndex {
[self.tableView reloadSectionIndexTitles];
}
Core Data and NSFetchedResultsController in particular seem buggy as hell, where delegate table view updates get out of sync with the fetched data, causing the application to crash. I really hope Apple is taking steps to fix the bugs in these frameworks in the 4.0 SDK. It's pretty frustrating.

reload uitableview simultaniously

i have to reload uitableview simultaniously using a thread. i'm already using two threads for loading data from web. Is it possible using a thread for reloading tableview? Is there any other way?
Simultaneous to what? The reloading takes time and you need to reload the backing data model in background, while still displaying being able to display data to the user?
If that is the case, then I would:
Define the data model as property.
Update a temporary data model in a background thread.
When updated I update the data model property on the main thread.
It is important to do the update of the real model property, and request of reloading data for the table view on the main thread. Otherwise there will be a time slot where the table view can request a view for a data model item that is no longer available.
An implementation would be something like this:
-(void)releadData;
{
[self performSelectorInBackground:#selector(reloadDataInBackground)
withObject:nil];
}
-(void)reloadDataInBackground;
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
MyDataModel* model = nil;
// Do what is needed to setup model.
[self performSelectorOnMainThread:#selector(updateModelOnMainThread:)
withObject:model
waitUntilDone:NO];
[pool release];
}
-(void) updateModelOnMainThread:(MyDataModel*)model;
{
self.model = model;
[self.tableView reloadData];
}
simultaneously with what?
everything UIKit has to be done on the main thread, so you can use background threads to talk to the internet or to do your own processing, but any actual interaction with a UITableView has to be on the main thread.