View does not appear because of iOS 7 - ios

I tried my app on my iPhone with iOS 7 and everything works perfectly except one thing. On iOS 6 versions, when I executed the following code, the loading view (with the activityindicator) appeared and disappeared at the end of the loading. On iOS 7, the view does not appear at all during the loading.
self.loadingView.alpha = 1.0;
[self performSelector:#selector(accessServices) withObject:nil afterDelay:0.0f];
AccessServices method :
- (void)accessServices {
// Getting JSON stuff
// The code is OK, I just don't copy/paste it here
self.loadingView.alpha = 0.0;
}
What happens ? Is it an iOS 7 bug ?

I wouldn't have expected the behavior of the above code to change, though I'm not surprised that it might not work the way you expect. If you do the JSON stuff on the main queue, your code will be dependent on idiosyncrasies of when the UI update takes place, rather than making it explicit.
You probably want to explicitly dispatch the JSON code to a background queue, and then dispatch the final UI update back to the main queue. The standard pattern for something like this is:
self.loadingView.alpha = 1.0;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Getting JSON stuff
// The code is OK, I just don't copy/paste it here
dispatch_async(dispatch_get_main_queue(), ^{
self.loadingView.alpha = 0.0;
});
});
You can use GCD (like above) or operation queues, or whatever. But the idea is the same, that UI updates should happen on the main queue but that anything remotely computationally expensive or slow else should happen on a background queue.
This pattern should work regardless of iOS version.

Related

MBProgressHUD is not shown on time when main thread is blocked

I'm developing a game that uses cocos2d-x, I'm having a problem on old devices such as ipad 1 where large scene takes a lot of time to load.
so the scene transition can take a few seconds, therefore I tried to implement a "busy" animation between scene transitions while the new scene is being loaded.
I implemented this using MBProgressHUD on IOS and ProgressDialog on android.
I decided that I don't want to start showing this animation immediately, instead I could schedule the animation to start 1-2 seconds after the scene transition starts, so that on newer devices the animation will not be shown at all.
Initially what I did was this:
- (void) showProgressDialog: (int) runWithoutDelay
{
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(showProgressDialogAfterDelay) object:nil];
shouldShow = YES;
if (runWithoutDelay){
[self showProgressDialogAfterDelay];
}
else{
[self performSelector:#selector(showProgressDialogAfterDelay) withObject:nil afterDelay:delay];
}
}
- (void) showProgressDialogAfterDelay
{
if (shouldShow){
isShown = YES;
[progressHUD show:YES];
}
}
and if I pass the scene transition part I would just set shouldShow flag to false and won't start the animation.
The problem is that because cocos2d-x scene transition is done in main\ui thread sometimes instead of calling the show method after 2 seconds it takes up to 6-8 seconds for it to be called and sometimes it even gets called after I set my flag to false.
As I understand it happens because performSelector (and so is NSTimer which I also tried) both run on the same thread by placing the call in the thread run loop queue.
I needed something like performSelectorInBackground that takes delay, so I tried using dispatch_after (even though I still haven't figure out how this could be canceled, as I need to cancel a previous schedule when I create a new one) this looked more accurate according Xcode's logs but even though the logs said that the method was called exactly 2 seconds after being scheduled the time would take 5-8 seconds to show and sometimes would not show at all.
As I understand it, and correct me if I'm wrong, this happens because MBProgressHUD changes to UI must happen on main\UI thread so even though I call [ProgressHUD show:YES] on a background thread the actual update of the UI is scheduled to be executed somehow on the main thread and because of it being stuck on cocos2d-x stuff it only starts to show after that, when the scene transition is completed and its too late.
is there any way around this problem? can I somehow schedule it to start with a delay but have it display right when I want it?
What I don't understand is why if I start it with no delay the animation works smoothly without being stuck even though the main thread is busy with cocos2d-x processing.
I‘ve managed a way around this somehow!
Create a normal method where you will call the progressHUD and call the second
Create the second method where you do the time consuming stuff (loading views)
Perform that method on the main thread
Sample:
-(void)callHUD {
[progressHUD show];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self performSelectorOnMainThread:#selector(loadView) withObject:nil waitUntilDone:YES];
dispatch_async(dispatch_get_main_queue(), ^{
[progressHUD dismiss];
});
});
}
-(void)loadView {
//Perform your segue or transition which needs to load
}
Hope that could help you a little.

Xcode load slow things after the View is visible

I have a very annoying problem. I have a ViewController with an UIImageView in it. The UIImageView should display a slide show. The images for the are coming from NSURL, so it takes a bit of time.
- (void)viewDidLoad {
[super viewDidLoad];
[self loadImages];
}
This is how I get the Images from the NSURL. The problem I have is, that while the Images are loading I only see a black screen.
At the beginning and at the end of the -(void)loadImages method I implemented a UIActivityIndicator to display the loading time.
I already tried -(void)viewWillAppear and -(void)viewDidLayoutSubviews but nothing worked.
Thanks for help
Jannes
What you need to do is load your images asynchronously and populate them as they come in (with delegate or block callbacks, depending on the API you use). You should absolutely never run networking code, or any other potentially long-running operation, synchronously on the main thread. The reason for this is that the UI runs on the main thread, so if you block it with your own operations, the UI cannot update and your app will become unresponsive.
Whether you're using Apple's networking frameworks or something like AFNetworking (highly recommended), there are many ways to do networking asynchronously with minimal work.
You're probably downloading your images synchronously. Try wrapping it in a dispatch_async block to run it on a different thread.
Example:
-(void)loadImages {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
// download the images here
dispatch_async(dispatch_get_main_queue(), ^(void) {
// update the UI on the main thread
});
});
}
Check out the SDWebImage library as well as most of this heavy lifting is done for you.

brightness control slider is really slow in responding in ios

I really need help.
I am creating simple image processing app, where I load an image from the camera roll or I take a picture. I have a brightness control (slider) that will adjust the brightness of the image. The problem is slider works in real time on the simulator, but on the ipad there is small delay in response. I have tried everything, but don't seem to have any luck.
Please help. I have seen other apps where slider works smoothly without any delay. What am I doing wrong?
Thanks
I'm going to take a guess on some things that were left out of the initial question.
1) To clarify, the problem is: movement of the slider is not smooth.
2) Also, as a result of, or in combination with, this UI roughness, there is a delay in change to the image.
I'm not sure what your implementation looks like, but, it sounds like you're doing too much work and/or too much on the main thread.
So, heres what a functioning implementation might do:
- (void)sliderChanged:(UISlider *)sender
{
[self adjustImageBrightnessWithValue:sender.value;
}
- (void)adjustImageBrightnessWithValue:(CGFloat)value
{
[self cancelCurrentWork]; // Maintain a reference to an operation and cancel it
[self adjustImageBrightnessAsyncWithValue:(CGFloat)value originalImage:self.imageView.image completion:^(UIImage *finalImage)
{
self.imageView.image = finalImage;
}
}
adjustImageBrightnessAsyncWithValue takes a value, original image, and completion block. It creates an operation (via NSOperation or NSOperationQueue, probably both) and keeps track of that operation. The operation applies the algorithm to the original image in the background. Once its done, the completion block sets the final image in your image view, on the main thread.
The only things that happen on the main thread are: get the slider changed callback, canceling previous work, starting new work, and setting the final image. Everything else should happen in the background. Canceling work is an optimization for the case that the user moves the slider too fast for the image to be modified before the value changes again. Once the slider doesn't change long enough to do the modification, the result will be visible. The slider should always be smooth because nothing is blocking the main thread.
EDIT
Using an operation queue...
Declare a member variable:
NSOperationQueue *m_queue;
...
Initialize it in an init method:
m_queue = [NSOperationQueue new];
m_queue.maxConcurrentOperationCount = 1; // So it only does one brightness calculation at a time and there are no concurrency issues.
...
- (void)adjustImageBrightnessWithValue:(CGFloat)value
{
[m_queue cancelAllOperations];
[m_queue addOperationWithBlock:^
{
UIImage *adjustedImage = [mainimage brightness:value]; // Not sure where this method is coming from, but this code assumes it returns a copy of mainimage with the brightness adjusted.
dispatch_async(dispatch_get_main_queue(), ^
{
imageview.image = adjustedImage;
});
}];
}
Also, as an aside, you might take a look at GPUImage, discussed here: http://nshipster.com/gpuimage/ for numerous fast, powerful image modification techniques/APIs.
Use
slider.isContinuous = false
That's it will solve the problem.

UIViewController animations stop working

My app runs fine in iOS6, but in an unspecified upcoming version of iOS that I cannot name for NDA reasons, all UIViewController transition animations stop working. New views just pop into place instantly. I am not sure if this unspecified future version of iOS is the cause, as I've seen this happen occasionally in iOS6.
Sometimes animations start working for a while and then stop shortly after, making me think it's some sort of memory warning issue, but my app is using a fairly reasonable ~125MB of RAM at most times. Can anyone offer any advice or things to investigate?
The described behavior has always existed: if you do work on background threads and then call and UIKit methods then more often than not the update will be delayed in a weird way.
Because of this you should always dispatch_async onto the main queue to update the UI.
Those bugs are very hard to catch since they do not always occur predictably.
To catch them I built a method that swizzles some UIKit methods to check if they are called on the main thread. This allows you to stop on a symbolic breakpoint, whenever you have forgotten to dispatch back onto main queue.
https://github.com/Cocoanetics/DTFoundation/blob/develop/Core/Source/iOS/Debug/UIView%2BDTDebug.m
A good workaround from the Apple dev forums on this issue:
Do this:
[UIView setAnimationsEnabled:YES]
And animations start working again. I suspect that this is either a straight up iOS7 bug, or somewhere in my code an animation or UIViewController launch is happening on a background thread, causing animations to stop. Probably unrelated to the unspecified future version of iOS.
This issue appears to be caused by doing UIKit stuff in background threads. I have a pre-render cache full of NSOperations that renders complex UIViews to UIImages to cache the output. This seemed to work fine in iOS6, but probably does cross the line somewhat. I'll need to replace this functionality with something that renders images and text to a graphics buffer rather than using UIViews and UILabels at all.
All you have to do is catch hold of main queue while updating UI on receiving response from an API.Ios uses main queue by default for updating UI but it is not 100 percent efficient.Hence you have to make sure that the UI gets updated on main thread only and the way to do that is as below:
DispatchQueue.main.async{
//UI related code eg:
self.label.text = "abc"
self.button.setTitle("xyz",.normal)
self.tableView.reloadData()
}
If you are not catching hold of main thread animations may or may not work.
But if you are using main thread animations will definetely work.
Correct Code while updating UI on api response:
Alamofire.getApiCall(paramaters: parameters, completion:{
response in
// UI related code.
DispatchQueue.main.async{
self.label.text = "abc"
self.button.setTitle("xyz",.normal)
self.tableView.reloadData()
}
})
Incorrect Code which may cause animations to stop and lead to weird crashes:
Alamofire.getApiCall(paramaters: parameters, completion:{
response in
// UI related code.
self.label.text = "abc"
self.button.setTitle("xyz",.normal)
self.tableView.reloadData()
})

UIWebView stringByEvaluatingJavaScriptFromString hangs on iOS5.0/5.1 when called using GCD

I have the following code in viewDidLoad, which works properly on iOS 4.3, but it hangs on iOS 5/5.1. On iOS 5/5.1, the alert dialog is shown but can not be dismissed, the UI thread freezes, the OK button just can not be clicked.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_sync(dispatch_get_main_queue(), ^{
[self.webview stringByEvaluatingJavaScriptFromString:#"alert('HELLO WORLD!')"];
});
});
Is this a bug?
After test, I consider it as a Bug, and changing code to use
[webView performSelectorOnMainThread:#selector(stringByEvaluatingJavaScriptFromString:) withObject:js waitUntilDone:NO]
will solve it.
Try it in - (void)viewDidAppear:(BOOL)animated instead
Why are you using GCD at all here?
Remember that in GCD there is no guarantee which thread your code will called on, regardless of queue. My guess is that your DISPATCH_QUEUE_PRIORITY_DEFAULT code happens to be running on the main thread, which is then triggering a deadlock on your dispatch_sync() call. This could be verified by examining your thread states in the debugger or Instruments.
Which gets back to my original question: what do you get by doing these convolutions instead of just calling the method directly on the main thread? You are going to block either way.
You can also change your dispatch_sync() to dispatch_async() - that should also suppress a deadlock, but should only be done if you have other concurrency needs and don't need a background thread to block.
Try
dispatch_async(dispatch_get_main_queue(), ^{
[self.webview stringByEvaluatingJavaScriptFromString:#"alert('HELLO WORLD!')"];
});

Resources