I want to change an uiimage.image by calling a method in my viewcontroller:
-(void) aendereBild: (NSData*)bildngage {
UIImage *uiimageAusData = [UIImage imageWithData: bildngage];
drawImage.image = uiimageAusData;
}
To make it short: The image is not changing. This is not due to the NSData I pass (which i first thought was the problem). This method is called from the appdelegate.
In another void in this viewcontroller I can change the image with drawimage.image = otherimage without any problems, so I think the call from the AppDelegate is causing the problem. Anyone an idea what I did wrong? Thankie!
I think #tonclon has it right that the call from the appDelegate isn't happening in the right thread. An easy way to check is to put a breakpoint on the line that works and the line that doesn't and look at what thread is being used (shown in the Xcode debugger).
This question shows you how you do the update in the right thread if that's the case:
How do I update the UI in the middle of this thread?
You need something like
[drawImage performSelectorOnMainThread:#selector(setImage:) withObject:uiimageAusData waitUntilDone:NO];
The threads that make calls onto the appDelegate aren't necessarily the main thread -- you should check. Your app starts with a few threads, even if you didn't make any.
Related
I am using this Project from github, it is an image picker. I have had to make a very small change since ios7 to make the preview images from your albums show again but with that change now when you leave the picker and come back into it the photos selected (2/5) resets to 0/5 even though I have photos selected. How can I fix this?
The dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0) seems to be taking forever to update the ui even with dispatch_async(dispatch_get_main_queue() to reload the ui inside of it. When I comment out the dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0) the pictures load instantly but other things get broken that depend on the queue.
here is the code snippet with the dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0) I changed with the code i changed commented out
AGIPCAssetsController.m:
- (void)loadAssets
{
[self.assets removeAllObjects];
__ag_weak AGIPCAssetsController *weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
__strong AGIPCAssetsController *strongSelf = weakSelf;
#autoreleasepool {
[strongSelf.assetsGroup enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (result == nil)
{
return;
}
AGIPCGridItem *gridItem = [[AGIPCGridItem alloc] initWithImagePickerController:strongSelf.imagePickerController asset:result andDelegate:strongSelf];
if ( strongSelf.imagePickerController.selection != nil &&
[strongSelf.imagePickerController.selection containsObject:result])
{
gridItem.selected = YES;
}
[strongSelf.assets addObject:gridItem];
}];
}
dispatch_async(dispatch_get_main_queue(), ^{
[strongSelf reloadData];
});
});
[strongSelf reloadData];
}
AGIPCGridItem is subclass of UIView. Don't work with UIKit objects on background thread.
Make sure you need the background thread and if you do, put only heavy tasks to background. Creating an UIView should not be that case.
Also, it's not recommended to use PRIORITY_LOW use simple PRIORITY_DEFAULT.
Edit: If you are curious why it did work on iOS 6: That's implementation detail of UIKit. It still was wrong, but somehow did what you expected.
I spent quite a bit of time with this code and I couldn't find a proper solution. Apparently the issue has come up on github, and a user offered a fix:
https://github.com/arturgrigor/AGImagePickerController/issues/19
But apparently he just removed all the blocks running in background, so I suppose that for a large amount of images the performance would be bad.
My hunch is that inside a dispatch_async block runs code that calls some UIKit function,
and thus the behaviour is basically undefined.
For example it seems to me that the setAsset function in AGIPGridItem.m is called inside the dispatch_async you posted. It is calling UImage, and although it's inside a lock, it should be still be executed on the background thread, while all the UIKit code should be executed on the main one.
UITableViewCell load images and reused cells
But even if I wrap the call inside a dispatch_async(dispatch_get_main_queue()...) it doesn't work yet.
It seems that the call [view removeFromSuperview]; in setItems in AGIPGridell.m is responsible somehow, but removing it has the side effect of creating a memory leak (unsurprisingly).
The global dispatch queue is a shared resource. DISPATCH_QUEUE_PRIORITY_LOW tasks run after every other task in the queue at a higher priority has run. If the queue is getting a lot of blocks submitted with a higher priority, your DISPATCH_QUEUE_PRIORITY_LOW task may not run for a very long time!
This is documented in the Concurreny Programming Guide as well as the libdispatch man pages
So, basically, other higher priority tasks are keeping things busy and your low priority task is not getting an opportunity to go.
I highlighted iMartin's answer "AGIPCGridItem is subclass of UIView. Don't work with UIKit objects on background thread." He's got it.
I had a very similar issue when moving iOS6 to 7. I was dispatching an ALAssets request in a background thread. Once the fetch completed, I would construct the UIImageView, a UILabel, and a wrapper and then send this object to the main/foreground thread to be rendered. This worked fine on iOS6, but on 7 it was not draw for about 20 seconds. It would sometime draw after a UI event like a touch.
Fix was to fetch the ALAsset in the background, send that to the main thread where I created the image view, etc. Works like a charm now.
I need to use a scratch effect for my game. here is an example code years ago.
https://github.com/oyiptong/CGScratch
It works just fine, but when i use it together with navigation controller, it crashes.
My project uses ARC, i flag the file as -fno-objc-arc. Here is the source code:
https://github.com/lifesaglitch/ScratchWithError
it crashes when i push the view controller, then pop, then reenter.
Edit:
When you convert all to arc, and flag the view controller that uses the scratch view as -fno-objc-arc, it works. But when you flag the scratch view as -fno-objc-arc instead, it crashes again. My project uses arc and i dont think i can convert my own view controller to be -fno-objc-arc.
Edit 2:
I modify the initialization code to:
scratchable = CGImageRetain([UIImage imageNamed:#"scratchable.jpg"].CGImage);
it does not crash anymore, but there's a memory leak. and CGImageRelease did get called once in dealloc method.
Use CGImageCreateCopy.
The reason for this is that you send a release to your CGImageRef at your dealloc, but if you inspect the actual CGImage object you'll see that it points to the same memory address each time (I guess it's part of Apple's optimizations, so it's just like you would have a static UIImage object and reference its CGImage).
So in your initWithFrame: you should get your scratchable like this:
UIImage *sci = [UIImage imageNamed:#"scratchable.jpg"]; // This guy is always the same
scratchable = CGImageCreateCopy(sci.CGImage);
PS: You had an actual leak with pixels, so you also need a CFRelease(pixels);
I tested, analyzed and measured the code and it seems to be OK now.
Here is also a link to the fixed project (I've also put a navigation controller and a button to push/pop) - and uses ARC of course.
This one works great for me https://github.com/moqod/iOS-Scratch-n-See !
In AppDelegate.m
[window addSubview:viewController.view];
You should instead replace it with:
[window setRootViewController:viewController];
I have a EAGLView that I wish to convert into a UIImage. I can do this with the solution posted here:
How to get UIImage from EAGLView?
However, I can only accomplish this if a small amount of time has gone by between the creation of the EAGLView and the UIImage.
This code, which creates a EAGLView, and then a UIImageView right afterwards, does not work:
EAGLView *EAGLphoto = [[EAGLView alloc] initWithImage:photo.workAreaImage];
[theWorkArea.photoArea1 addSubview:EAGLphoto];
//I put a glfinish() here but it didn't help
UIImageView *photoView = [[UIImageView alloc] initWithImage:[self glToUIImage]];
//glToUIImage is taken from the link above
[theWorkArea.photoArea2 addSubview:photoView];
I'm assuming the UIImageView tries to get created before the EAGLView is finished being created. I tried to put a glfinish() in between but it did nothing. When I run the code, the EAGLView shows up fine but the UIImageView shows up as black.
However, this modified version of the above code works:
EAGLView *EAGLphoto = [[EAGLView alloc] initWithImage:photo.workAreaImage];
[theWorkArea.photoArea1 addSubview:EAGLphoto];
[self performSelector:#selector(getUIImage) withObject:nil afterDelay:0.1];
- (void)getUIImage {
UIImageView *photoView = [[UIImageView alloc] initWithImage:[self glToUIImage]];
//glToUIImage is taken from the link above
[theWorkArea.photoArea2 addSubview:photoView];
}
Am I using glfinish() incorrectly? Is there a better method than my hack?
Usually when you make visible changes to UIKit objects instead of updating instantly they merely flag themselves to make those changes in the future, then do the whole set of changes as a batch next time you relinquish the main thread (by returning to the runloop). That's not a misfeature or a failure in the implementation, it's actually usually what you implicitly expect, being the reason you can write code like:
view.frame = someNewFrame;
view.someOtherProperty = someOtherValue;
And not worry that every so often the view will visibly adopt the new frame before adopting the other changes. As a rule, you want the things you do to views to appear to be atomic.
Occasionally you run into a situation, as you have here, where the fact that changes you've already requested haven't come into effect yet is exposed. frame and someOtherProperty would return their new values in the above example so that you don't care whether the changes took effect immediately or not, but based on your observation about performSelector:withObject:afterDelay: it seems likely that you've stumbled upon a situation where the change doesn't pretend to be immediate.
EAGLView is difficult to diagnose because it's not really a UIKit feature. The CAEAGLLayer that it's built upon is, but EAGLView is just the name Apple have adopted for a custom UIView subclass built on CAEAGLLayer in various example projects. They've not been consistent about the interface or implementation of EAGLView across their examples, but at a guess I'd say that probably it's creating the OpenGL frame buffer object (that is, the thing that OpenGL uses to store numbers related to pixels, allowing glReadPixels to work) only when asked to lay itself out, and it doesn't lay itself out until UIKit asks it to do so. Which isn't until you've dropped out to the runloop.
That diagnosis can be confirmed by checking the code of the EAGLView that you have. If there are a bunch of calls to things like glGenFramebuffers that are triggered as a result of layoutSubviews, drawRect or some other method that isn't called directly by the relevant init then that proves it.
Assuming that diagnosis to be correct, you could adapt EAGLView but I think probably the best solution is just to stick to performSelector:withObject:afterDelay:0. This isn't a race condition, so that solution isn't in the slightest bit flakey or unreliable, it's just a slightly roundabout way of saying "let UIKit catch up with all of those instructions, given that I know that'll let EAGLView get into a meaningful state, then continue with my stuff".
Here's the issue – I followed along with the Apple lazy-load image sample code to handle my graphical tables. It works great. However, my lazy-load image tables are being stacked within a navigation controller so that you can move in and out of menus. While all this works, I'm getting a persistent crash when I move into a table then move immediately back out of it using the "back" button. This appears to be a result of the network connections loading content not being closed properly, or calling back to their released delegates. Now, I've tried working through this and carefully setting all delegates to nil and calling close on all open network connections before releasing a menu. However, I'm still getting the error. Also – short posting my entire application code into this post, I can't really post a specific code snippet that illustrates this situation.
I wonder if anyone has ideas for tasks that I may be missing? Do I need to do anything to close a network connection other than closing it and setting it to nil? Also, I'm new to debugging tools – can anyone suggest a good tool to use to watch network connections and see what's causing them to fail?
Thanks!
Have you run it through the debugger (Cmd-Y)? Does it stop at the place where the crash is happening? That should show you in code where the issue is happening. I'm betting the issue has to do with over-releasing something rather than cleaning up connections. Are you getting EXC_BAD_ACCESS? Check any delegates and make sure they are nil when -viewWillDisappear gets called. That way, if anything tries to call back to a delegate, it will just be a no-op.
You may also want to try enabling zombies (NSZombieEnabled) which will tell you when an object that has been released is being accessed again. It's very helpful in finding over-released objects.
Ah ha... after a large zombie hunt (thanks, Matt Long), I discovered that the issue stems from an error in Apple's LazyTableImages sample code. That example provides the following implementation for canceling all image loads, which I turned into a general-purpose stopAllImageLoads method...
From RootViewController.m in LazyTableImages sample code:
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// terminate all pending download connections
NSArray *allDownloads = [self.imageDownloadsInProgress allValues];
[allDownloads performSelector:#selector(cancelDownload)];
}
There is in error in the last line of the above method where performSelector is called on an array of objects. The above implementation calls the selector on the array itself, rather that on each object in the array. Therefore, that last line should be this:
[allDownloads makeObjectsPerformSelector:#selector(cancelDownload)];
Once that line was changed, everything else fell into place. It turns out I wasn't calling my stopAllImageLoads method where I meant to – I had disabled it at one point because it was causing an error. Once that was back in place, the memory issues cleared up because image loads were successfully canceled before the table delegate was released.
Thanks all for your help.
If you're doing ANY asynchronous function (network requests, Core Location updates, etc), you run the risk that your view controller that is the delegate of that action is deallocated by the time the async function returns. i.e. you back out of the view and take the delegate target away from the background process. I've dealt with this several times.
Here's what you do. Use the ASIHTTPRequest library (which you should be doing anyway--it's brilliant). Create a synthesized property to hold your request. Then in viewWillDisappear, call -cancel on your request. To be safe, I also set its delegate to nil, but that should be unnecessary.
Here's a sketch of what you want to do. Note I typed this right here, haven't syntax-checked it or anything.
#implementation MyViewController
#synthesize req //this is an ASIHTTPRequest *req.
-(void)viewDidLoad
{
//make an NSURL object called myURL
self.req = [ASIHTTPRequest requestWithURL:myURL];
self.req.delegate = self;
[self.req startAsynchronous];
}
-(void)viewWillDisappear
{
[self.req cancel];
}
-(void)requestFinished:(ASIHTTPRequest *)request
{
NSString *string = [request responseString];
}
i didn't really know how to title this question, but here's a thing that really kills me:
In my app i have a UITableView, UISegmentedControl and UINavigationBar. Once UISegmentedControl gets one of its segments selected i want to show a UIActivityIndicatorView on the UINavigationBar and then parse an xml file and present the results in a table.
Everything works almost as i want it to, except one thing, the activity indicator view gets added to the uinavigationbar after the parser finishes, even though the method showLoading that adds UIIndicatorView to UINavigationBar gets before parser is initialised.
Can anyone explain it? is there something i might be missing? maybe the ui needs to get redrawn?
thanks
peter
It looks that you parse your xml in main thread and so it becomes blocked for UI changes. Try to move xml parsing to separate thread (e.g. by calling your parsing method via -performSelectorInBackground:)
Edit: Actually you're (almost certainly) using autorelease implicitly in your application - as many standard functions return autoreleased objects. When you're running your functions on separate thread you need to create NSAutoreleasePool object there to handle autoreleased objects and avoid memory leaks (see Autorelease Pools in docs). So your parseXML function must look like:
- (void)parseXML{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
... //xml parsing routines etc
[pool release];
}