I'm using the three20 project for my iPhone app. I've narrowed my problem down and I'm now just trying to re-create the 'Web Images in Table' example that comes with the project. I've copied the code exactly as in the project, with the exception that I do not use the TTNavigator (which the example does) but I am adding my TTTableViewController manually to a tabBar.
The problem is as follows; the images in the table should load automatically from the web, like in the example. But they only load after I scroll the table up and down.
In the console it clearly says it is downloading the images, and you see the activity indicator spinning like forever.. And unless I scroll up and down once, the images will never appear.
Anyone? Thanks in advance.
P.S:
If I'm using this code in any random UIView, It also doesn't work (only shows a black square):
TTImageView* imageView = [[[TTImageView alloc] initWithFrame:CGRectMake(30, 30, 100, 100)] autorelease];
imageView.autoresizesToImage = YES;
imageView.URL = #"http://webpimp.nl/logo.png";
[self.view addSubview:imageView];
If I put this code in my AppDelegate (right onto the window), it does work .. strange?
POSSIBLE SOLUTION:
Although I stopped using TTImageView for this purpose, I do think I found out what the problem was; threading (hence accepting the answer of Deniz Mert Edincik). If I started the asynchronous download (because basically that is all the TTImageView is, an asynchronous download) from anywhere BUT the main thread, it would not start. If I started the download on the main thread, it would start immediately..
Sounds like a threading problem to me, are you creating TTImageView in the main runloop?
I find one interesting thing. When I use combination TTTableViewController, TTTableViewDataSource and TTModel I have same problem with loading TTImageView. My problem was, that my implementation of Model methods 'isLoading' and 'isLoaded' don't return proper values after initialization of model. That forces me to call reload on model manualy in 'viewDidAppear' method and that causes image loading problem. So I repair my 'isLoading' and 'isLoaded' methods to both return 'NO' after Model init, and everything is fine.
When an image finishes loading try sending a reloadData message to the table view. This forces the table to recalculate the size of the rows and redraw the table. Just be careful that you don't start downloading the image again in response to this message.
I've written something similar to this where an image view will load its own image from the web.
Im my experience, when the image had loaded successfully but was not shown in its view, it was a case that the cell needed to be told to redraw.
When you scroll the table view, the cells are set to redraw when the come onscreen, which is why they appear when you scroll.
When the image loads, tell the cell that it is sitting in to redraw by sending it the message setNeedsDisplay.
That way, when the image finishes downloading, the cell its sitting in (and only that cell) will redraw itself to show the new image.
It's possible that you might not need to redraw the entire cell and might be able to get away with simply redrawing the image view using the same method call. In my experience, my table cells view hierarchy was flattened, so I had to redraw the whole cell.
I don't have an answer for what you want to do, but I will say that this is considered a feature, and the expected behavior. You use TTImageView in UITableView when you want to do lazy loading of images. TTImageView will only load the images whose frames are visible on the screen. That way, the device uses its network resources to download images that the user has in front of them, rather than a bunch of images that the user isn't even trying to look at.
Consider having a long list that may contain a couple hundred thumbnail images (like a list of friends). I know from experience that if you kick off 100+ image requests on older devices, the memory usage will go through the roof, and your app will likely crash. TTImageView solves this problem.
This is a thread problem. You can load the images by including the line:
[TTURLRequestQueue mainQueue].suspended = NO;
in - (void)didLoadModel:(BOOL)firstTime.
Related
I'll try to explain my self, I have ContactsViewController that shows a table view with a list of contacts (the model is an array of Contact objects), each cell display an image of a contact.
Currently what I do to populate the cell's UIImageView is this:
1. I override the Contact image property getter -
- (UIImage *)contactImage
{
if (!_contactImage) {
_contactImage = [[UIImage imageNamed:#"placeHolder.png"] retain];
[self asyncDownloadContactImageFromServer];
}
return _contactImage;
}
Then when I finish downloading the image I set it to the contactImage property and I post a ContactUpdatedImageNotification.
My ContactsViewController then get this notification and reload the cell of this contact, this will set the downloaded image to the cell's imageView.
The result of this is good async fetching of the images without blocking the UI while the user scroll the table view.
BUT there is something small that bothers me, when the a user scroll the table view and reveal new cells the new cell's image get download as expected but the cell's imageView is not updated with the new downloaded image till the user pick up his finger.
I supposed that I need to do something in another thread to make this effect, but I don't know how?
The image is not updated until the user stops scrolling due the code being executed in the default runloop, which gets delayed until scrolling finishes. This other question deals with the difference between the runloops, NSDefaultRunLoopMode vs NSRunLoopCommonModes and it precisely recommends not updating the images while scrolling since that can introduce jerkiness in the scrolling itself if you are not careful.
Also, now that you know about the existence of these runloop modes you will be able to find much more information about them in the xcode documentation or internet.
Hey Eyal visit following url...you will get answer and as well sample code...
tableview with different cell with different images
Hope this will help you...
My application consists of data downloaded from an XML file. data contains short text and images.
currently I'm downloading up all data and building up the view in a view controller, in the ViewDidLoad method, which causes the application not to show up the root view until all data is downloaded. I want it to show up in a more user friendly way, at least to preload some of the data during the splash screen.
By the way I've done the lazy image loading so images can load while the main view is displayed.
As long as the number of views depend on number of rows in XML, loading XML asynchronously while building up the the view does not suite my need (or maybe I'm wrong).
I understand that describing the solution in an answer is quite a challenge, so maybe you could point to an article or even a book that has a detailed explanation of asynchronous and multithread handling.
I don't see why you can't load it asynchronously o_O
You should show some "Loading" view anyway (preferably with an activity Indicator)
A progress bar would be nice, too.
And when it's done downloading you just reload and relayout your view.
If by "number of views" you mean the number of cells in a table row you can just tell the tableview to reload all data whereas your numberOfRowsInSection function (or whatever you want to use) should return appropriate values depending on whether it's loading or not.
EDIT: you shouldn't do that while the application is still loading because that's extremely user-unfriendly and slows down the loading of the application aswell
I think you should parse all data in didFinishLaunchingWithOptions: method of appDelegate and then use following methods to do parsing.
[self performSelectorInBackground:#selector(downloadData:) withObject:nil];
method downloadData: contain parsing procedure.
Does someone know of a way, or has a creative idea as to how to obtain an UIImage that is rendered from a UIWebView? The catch is, that is must be on a background thread.
I'll elaborate:
I'm trying to obtain the image from multiple UIWebViews every second+-, and display it on screen (iPhone's of course). Because rendering a layer to a CGContext is a CPU consuming job, I wouldn't like to use the main thread, so not to hang the UI.
My attempts so far were:
I tried to use renderInContext on the webView's layer to the UIGraphicalContext, but the _WebTryThreadLock error crashed the webviews.
I tried creating a CGBitmapContext, and render the webview's layer to it, but got the same result.
I tried implementing a copy method (by adding a Category) to CALayer, that deep copied all of the public properties, and sublayers. Afterwards, I tried to renderInContext the layer I copied. I got a UIImage that was partially "correct" - meaning, not all of the layers were rendered, so for example, i would get only the website header (or footer, or body, or search bar, or just a some of the frames). The UIWebview's layer consists of all sort of subclassed CALayers, so this is probably why this approached didn't work.
I tried setting the kCATransactionDisableActions in a CATransaction, but it didn't appear to change this behavior (neither of them).
I'm pretty close to giving up.
Is there a savior among you people?
UIWebView hates, and I mean really hates, having anything done to it on a background thread. UIKit is not fully thread safe. Drawing to a graphics context is (this was added in iOS 4), but not creating UIViews on a secondary thread.
Are you creating your UIWebViews off the main thread? Do you perhaps have some code to share? I would suspect your issues are being caused by the fact you're trying to perform operations to a UIWebView on a secondary thread. The drawing operation to render the view's contents as an image can happen off the main thread, but creating the view itself can't.
I've been working on this, too.
In a thread I create a view hierarchy, loop until the web-view has finished loading content.
The webview I create is inside of a UIViewController's viewdidload-- I've tried doing
if ([NSThread isMainThread] == NO) {[self performselectorOnMainThread: #selector(viewDidLoad)return;)}
And I've done the same for dealloc'ing the webView.
But that didn't work.. I've only found that we avoid UIWebView exceptions UNTIL we hit the autorelease pool...
I'm using instruments to figure out why.
Here's my attack strategy...
I'm going to perform the render operation on the main thread with an off-screen view, having a separate thread running some sort of queue to manage them. I'm worried about UI lag, so it'll have to be fairly efficient.
I need help getting dataSource in OpenFlow. I
I want to provide CoverFlow functionality whenever the phone is turned horizontally. I'm using Alex Fajkowski's awesome code OpenFlow ( http://fajkowski.com/blog/2009/08/02/openflow-a-coverflow-api-replacement-for-the-iphone/ ) but the example provided is very different than what I need.
I am using OpenFlow in a horizontal view inside a navbar view controller. I have OpenFlow working already. I can scroll through all my images and works really good. However I am using it with over 100 images and it takes a while to load at first. In looking into performance improvements I realized the AFOpenFlowViewDataSource delegate is not getting called. I was able to get AFOpenFlowViewDelegate working by specifying the delegate in the view controller class "flowView.dataSource = self;". But I am not able to get the datasource delegate working. Not even with "flowView.viewDelegate = self;".
Is the datasource needed at all? It seems it is needed for threading of loading.
Ok, it looks like it is running beautiful now. The DataSource delegate is only called when there the objects are loaded dynamically. Meaning, if I use "[(AFOpenFlowView *)self.view setImage]" then dataSOurce is never called because all it knows images are already loaded. However, using "[(AFOpenFlowView *)self.view setNumberOfImages:30];" triggers the DataSource delegate to load the images as they are needed. I found the GetImageOperation NSThread very useful for my 100+ images. However, images are not unloaded after going offscreen. Anyone know how to unload images as they go off screen?
I have a UITableView that displays a lot of images in the cells, and i am not quit happy about the scroll performance. My UITableView is something like the photo app on the iphone. Does anybody knows why the iphone foto app scrolls so damn fast as if their's nothing on the screen.
And does anybody has some tips/tricks to increase my performance/scrolling speed?
You should precache your images and not do it lazily. As you scroll your table the UITableViewDataSource:cellForRowAtIndexPath: method is called, and if you're loading your images in there then you'll see it requesting your cell contents as your scroll, creating latency in your application. Try putting making your cellForRowAtIndexPath: something like this:
NSDate *date = [NSDate date];
... your cell loading code ...
NSLog( #"Elapsed time to generate cell %.2d", [date timeIntervalSinceNow] );
You'll see how long you're spending for getting each cell.
To get around this, you can be as complex as you need to - if you have a lot of images, you're going to have to be more and more clever. You can do paged loading, where you keep track of the last cell requested's NSIndexPath, and determine if the scroll is going up or down and use +NSImage:imageNamed: to fetch at once some page worth of images forward (ie, 5 images forward of your current position), or whatever works out for you (taking advantage of the fact that people have to return their finger down to the bottom of the table to swipe again, and so the consumption of table elements has pauses - you can make your page size large enough to fill a swipe). This is probably still not great, though, because you'll just be suffering all your impact at once instead of a jittery load for every cell.
You can return control to the UI rapidly and allow the system to schedule your prefetched page of images using NSRunLoop:performSelector:target:argument:order:mode: off the main runloop using NSImage:imageNamed:, and then when the cell is requested if you're fetching far enough ahead it'll be available to display.
You need to be painfully aware of memory concerns though. If you're finding this to be an issue, use NSImage:initWithContentsOfFile:, which will clean up image caches in low memory situations. Depending on the strategy used by the cache invalidation algorithm these situations may cause a "stutter" as you purge caches and have to reload your invalidated prefetches.
Great scrolling performance results have been reported by subclassing UITableViewCell and drawing the contents of each cell directly. See the accepted answer for this question for more details and links to code samples.
The problem here is memory, you are loading too many high resolution pictures at once, the foto app does not use a tableview it uses a scroll view and it only loads a max of 3 pictures at a time , so memory is not a concern, if u are trying to do something similar to the foto app use a scroll view