I want to see in a moment my UIAlert above my UIActivityIndicatorView, i have this code:
- (void)showWithLabelDeterminate{
HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
[self.navigationController.view addSubview:HUD];
// Set determinate mode
HUD.mode = MBProgressHUDModeDeterminate;
HUD.delegate = self;
HUD.labelText = #"Loading";
// myProgressTask uses the HUD instance to update progress
[HUD showWhileExecuting:#selector(myProgressTask) onTarget:self withObject:nil animated:YES];
}
- (void)myProgressTask
{
float progress = 0.0f;
while (progress < 0.99f)
{
progress += 0.01f;
HUD.progress = progress;
usleep(50000);
if(progress > 0.02f & progress < 0.05f)
{
NSString * string = [NSString stringWithFormat:#"http://localhost:8080/......"];
NSURL *url = [NSURL URLWithString:string];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
urlData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if(!urlData)
{
[self simpleAlert];
}
else
{
datos = [[NSMutableString alloc]initWithData:urlData encoding:NSISOLatin1StringEncoding];
}
}
}
}
-(void)simpleAlert
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Dont connect!!" delegate:self cancelButtonTitle:#"Refresh" otherButtonTitles:nil];
[alertView show];
[alertView release];
}
The problem is that i dont see my UIAlertView above my UIActivityIndicatorView.....
myProgressTask is either executing on a background thread or is blocking the main thread with its calls to usleep.
If it's on a background thread then your use of UIAlertView is invalid because UIKit is, in the general case, usable from the main thread only. The quickest solution would be to use performSelectorOnMainThread:... to call simpleAlert.
If it's on the main thread then you're probably blocking UIKit from operating. Obvious comments would follow about using a non-blocking NSURLConnection and using NSTimer or performSelector:withObject:afterDelay: to schedule things to happen after a pause.
Related
I cannot make back button (previous page in navigation) work during html parsing process. What I am trying to do is parsing an html and get a pdf file on viewDidAppear. During that time I want back button to be enabled because parsing can take long time. So users can decide to leave parsing process.
here is my code:
-(void)viewDidAppear:(BOOL)animated
{
[self performSelector:#selector(getPDFUrl) withObject:nil afterDelay:0];
}
-(void) getPDFUrl
{
NSURL *programURL = [NSURL URLWithString:#"http://www.example.com/somepdf/"];
NSData *programHtmlData;
#try
{
programHtmlData = [NSData dataWithContentsOfURL:programURL];
}
#catch(NSException* ex)
{}
// 2
TFHpple *programHTMLParser = [TFHpple hppleWithHTMLData:programHtmlData];
NSString *studiosXpathQueryString =
#"//div[#class='ultra_wrapper']/div[#class='container columns extra_pad boxed_lay centered']/div[#id='prk_ajax_container']/div[#id='centered_block']/div[#id='main_block']/div[#id='content']/div[#id='main']/div[#class='twelve columns sidebarized']/div[#class='prk_no_composer']/p/a";
NSArray *programNodes = [programHTMLParser searchWithXPathQuery:studiosXpathQueryString];
NSMutableArray *activities = [[NSMutableArray alloc] init];
Tutorial *tutorial;
if (programNodes.count > 0) {
for (TFHppleElement *element in programNodes)
{
#try
{
tutorial = [[Tutorial alloc] init];
tutorial.url = [element objectForKey:#"href"];
}
#catch(NSException* ex)
{
}
}
NSURL *targetURL = [NSURL URLWithString:tutorial.url];
webView.scalesPageToFit=YES;
NSURLRequest *request = [NSURLRequest requestWithURL:targetURL];
[webView loadRequest:request];
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"warning"
message:#"warning!"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
}
[self performSelector:#selector(getPDFUrl) withObject:nil afterDelay:0];
will execute on main thread. Therefore Blocking your UI.
You should use
[self performSelectorInBackground:#selector(getPDFUrl) withObject:nil];
Or you can use NSOperationQueue or just NSOperation or more simple way NSBlockOperation.
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(#"dasds");
}];
[op start];
When i will try to post some image or text from application to server that situation MBProgressHUD not worked. but without posting method its work perfectly. i am used below code.Please any one help me. thanks.
HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
HUD.dimBackground = YES;
HUD.delegate = self;
NSString *urlRequest =[NSString stringWithFormat:#"URL"];
NSString *pStrLegalURLString =[urlRequestn stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [[NSURL alloc] initWithString:pStrLegalURLString];
NSMutableURLRequest *request1 = [NSMutableURLRequest requestWithURL:url];
[request1 setHTTPMethod:#"POST"];
NSData *returnData1 = [ NSURLConnection sendSynchronousRequest: request1 returningResponse: nil error: nil ];
NSString *returnString1 = [[NSString alloc] initWithData:returnData1 encoding: NSUTF8StringEncoding];
Try like this:
It will work, but your problem as i guess is because of sendSynchronousRequest which is a blocking call hence if the main thread hangs up you won't see any update in UI. So try to make such calls in background.
Try one of the two approaches as I see:
Approach 1: (send request in background using selectors and do UI update in main thread)
-(void)method1 {
HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
HUD.dimBackground = YES;
HUD.delegate = self;
NSString *urlRequest =[NSString stringWithFormat:#"URL"];
NSString *pStrLegalURLString =[urlRequest stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [[NSURL alloc] initWithString:pStrLegalURLString];
NSMutableURLRequest *request1 = [NSMutableURLRequest requestWithURL:url];
[request1 setHTTPMethod:#"POST"];
// send request in background.
[self performSelectorInBackground:#selector(method2) withObject:nil];
}
-(void)method2 {
NSData *returnData1 = [ NSURLConnection sendSynchronousRequest: request1 returningResponse: nil error: nil ];
NSString *returnString1 = [[NSString alloc] initWithData:returnData1 encoding: NSUTF8StringEncoding];
[self performSelectorOnMainThread:#selector(method3) withObject:nil waitUntilDone:NO];
}
-(void)method3
{
// update ui in main thread.
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
}
Approach 2: Use Dispatch_Async and Dispatch_Sync
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void){
// Do background work here
dispatch_sync(dispatch_get_main_queue(), ^(void){
// Update UI here
});
});
I have a piece of code in the .m file as below:
- (IBAction)btnLogin:(UIButton *)sender
{
NSString *strURL = [NSString stringWithFormat:#"http://www.myworkingdomain.com/fn_checkLogin2.php?name=%#&pass=%#", self.email.text, self.password.text];
// to execute php code
NSData *dataURL = [NSData dataWithContentsOfURL:[NSURL URLWithString:strURL]];
// to receive the returend value
NSString *strResult = [[[NSString alloc] initWithData:dataURL encoding:NSUTF8StringEncoding]autorelease];
if ([strResult isEqualToString:#"0"]) {
UIStoryboard *loginFailStoryBoard = [UIStoryboard storyboardWithName:#"loginFailStoryboard" bundle:nil];
UIViewController *initialFailLogin = [loginFailStoryBoard instantiateInitialViewController];
initialFailLogin.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:initialFailLogin animated:YES];
//NSLog(#"%#", strResult);
} else {
UIStoryboard *memberMenuBoard = [UIStoryboard storyboardWithName:#"memberMenuStoryboard" bundle:nil];
UIViewController *initialMemberMenu = [memberMenuBoard instantiateInitialViewController];
initialMemberMenu.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:initialMemberMenu animated:YES];
//NSLog(#"%#", strResult);
}
NSLog(#"%#", strResult);
}
#end
I am trying to determine if a person entered the right email and password and then to move on to the next view. I have the 'strResult' in my log and it shows that the php script it working correctly. It is now seems that the if..else statement that is not working due to the part [strResult isEqualToString:#"0"]
Can someone please advice where should I change to correct this so that when a member logins with the right password, he can go to the member view?
Thanks
#dan
The main thread is where all your UI elements are displayed. If you launch a connection on the main thread, your UI will become unresponsive until the connection is resolved (Unable to press buttons, unable to scroll etc...) In order to avoid that you should use another thread. There are other ways of doing it but the following code should do the trick without blocking your UI.
NSString *loginString =[NSString stringWithFormat:#"fn_checkLogin2.php?name=%#&pass=%#", self.email.text, self.password.text];
NSURL *url = [NSURL URLWithString:#"http://www.myworkingdomain.com/"];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url ];
[urlRequest setTimeoutInterval:30.0f];
[urlRequest setHTTPMethod:#"POST"];
[urlRequest setHTTPBody:[loginString dataUsingEncoding:NSUTF8StringEncoding]];
NSOperationQueue *queue= [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
if ([data length]>0 && error==nil) {
NSString *html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"HTML = %#",html);
}
}
];
So if data is returned you can do stuff, if connection fails you can do other stuff without blocking your UI.
In one of the views of my app there's a button. When pressed it is supposed to begin taking a video, trigger a sound file to start, and hide itself from view while unhiding another button. The second button is supposed to stop the video recording and make it save. Here's the code I have for the video recording, which initially worked with no problems:
in viewDidLoad:
finishButton.hidden = TRUE;
session = [[AVCaptureSession alloc] init];
movieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
NSError *error;
AVCaptureDeviceInput *videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self cameraWithPosition:AVCaptureDevicePositionFront] error:&error];
if (videoInput)
{
[session addInput:videoInput];
}
AVCaptureDevice *audioCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
NSError *audioError = nil;
AVCaptureDeviceInput *audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioCaptureDevice error:&audioError];
if (audioInput)
{
[session addInput:audioInput];
}
Float64 TotalSeconds = 35; //Total seconds
int32_t preferredTimeScale = 30; //Frames per second
CMTime maxDuration = CMTimeMakeWithSeconds(TotalSeconds, preferredTimeScale);
movieFileOutput.maxRecordedDuration = maxDuration;
movieFileOutput.minFreeDiskSpaceLimit = 1024 * 1024;
if ([session canAddOutput:movieFileOutput])
[session addOutput:movieFileOutput];
[session setSessionPreset:AVCaptureSessionPresetMedium];
if ([session canSetSessionPreset:AVCaptureSessionPreset640x480]) //Check size based configs are supported before setting them
[session setSessionPreset:AVCaptureSessionPreset640x480];
[self cameraSetOutputProperties];
[session startRunning];
and for the button:
-(IBAction)start:(id)sender
{
startButton.hidden = TRUE;
finishButton.hidden = FALSE;
//Create temporary URL to record to
NSString *outputPath = [[NSString alloc] initWithFormat:#"%#%#", NSTemporaryDirectory(), #"output.mov"];
self.outputURL = [[NSURL alloc] initFileURLWithPath:outputPath];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:outputPath])
{
NSError *error;
if ([fileManager removeItemAtPath:outputPath error:&error] == NO)
{
//Error - handle if required
}
}
//Start recording
[movieFileOutput startRecordingToOutputFileURL:outputURL recordingDelegate:self];
finally, under the last button:
[movieFileOutput stopRecording];
and here's the code to save the video:
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput
didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
fromConnections:(NSArray *)connections
error:(NSError *)error
{
NSLog(#"didFinishRecordingToOutputFileAtURL - enter");
BOOL RecordedSuccessfully = YES;
if ([error code] != noErr)
{
// A problem occurred: Find out if the recording was successful.
id value = [[error userInfo] objectForKey:AVErrorRecordingSuccessfullyFinishedKey];
if (value)
{
RecordedSuccessfully = [value boolValue];
}
}
if (RecordedSuccessfully)
{
//----- RECORDED SUCESSFULLY -----
NSLog(#"didFinishRecordingToOutputFileAtURL - success");
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:outputURL])
{
[library writeVideoAtPathToSavedPhotosAlbum:outputURL
completionBlock:^(NSURL *assetURL, NSError *error)
{
if (error)
{
}
}];
}
}
}
All of this was working just fine. Then I added a few lines so that a song file would play when the start button was pressed.
in viewDidLoad:
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/Song.aiff", [[NSBundle mainBundle] resourcePath]]];
NSError *audioFileError;
player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&audioFileError];
player.numberOfLoops = 0;
[self.player prepareToPlay];
and under the start button:
if (player == nil)
NSLog(#"Audio file could not be played");
else
[player play];
Now when the start button is pressed the song plays with no problems, but the video capture is messed up. Before adding the AVAudioPlayer stuff I would get the "didFinishRecordingToOutputFileAtURL - enter" and "didFinishRecordingToOutputFileAtURL - success" logs when I pressed the finish button, and now I get the first log as soon as I press the start button, nothing happens when I press the finish button, and no video is recorded. If I comment out the lines that make the song play then the video capture works just fine again. Any ideas what's going on here?
- (void)setupAudioSession
{
static BOOL audioSessionSetup = NO;
if (audioSessionSetup)
{
return;
}
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error: nil];
UInt32 doSetProperty = 1;
AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof(doSetProperty), &doSetProperty);
[[AVAudioSession sharedInstance] setActive: YES error: nil];
audioSessionSetup = YES;
}
- (void)playAudio
{
[self setupAudioSession];
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:#"btnClick" ofType:#"wav"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:soundFilePath];
AVAudioPlayer *newPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
[fileURL release];
self.audioPlayer = newPlayer;
[newPlayer release];
[audioPlayer setDelegate:self];
[audioPlayer prepareToPlay];
audioPlayer.volume=1.0;
[audioPlayer play];
}
NOTE: Add the framework: AudioToolbox.framework.
#import <AudioToolbox/AudioServices.h>
I am doing a NSURLConnection that downloads a file only if there is a new one (checked with the last-modified date). But to accomplish this I am using two methods with two different NSURLRequests and NSURLConnection. Well, they do the same.
- (IBAction)uppdatera:(id)sender
{
checkHeaders = YES;
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.forellgatan.se/site/ftp_files/Kapareskolan.zip"] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10.0];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection)
{
self.receivedData = [[NSMutableData data] retain];
}
else
{
UIAlertView *connectFailMessage = [[UIAlertView alloc] initWithTitle:#"Ingen internetanslutning! 1" message:#"Anslut dig till internet för att ladda ner!" delegate: self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[connectFailMessage show];
[connectFailMessage release];
}
}
- (void)downloadNewFile
{
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.forellgatan.se/site/ftp_files/Kapareskolan.zip"] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10.0];
NSURLConnection *theConnection2 = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection2)
{
self.receivedData = [[NSMutableData data] retain];
}
else
{
UIAlertView *connectFailMessage = [[UIAlertView alloc] initWithTitle:#"Ingen internetanslutning! 2" message:#"Anslut dig till internet för att ladda ner!" delegate: self cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[connectFailMessage show];
[connectFailMessage release];
}
checkHeaders = NO;
self.progressView.hidden = NO;
}
It goes through the didReceiveResponse method:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
if (checkHeaders == YES)
{
NSHTTPURLResponse *test = (NSHTTPURLResponse *)response;
if ([test respondsToSelector:#selector(allHeaderFields)])
{
NSDictionary *metaData = [test allHeaderFields];
NSString *lastModifiedString = [metaData objectForKey:#"Last-Modified"];
NSString *savedString = [[NSUserDefaults standardUserDefaults] stringForKey:#"LastModified"];
if (![lastModifiedString isEqualToString:savedString])
{
[self downloadNewFile];
}
else if ([lastModifiedString isEqualToString:savedString])
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Ingen uppdatering tillgänglig" message:#"Det finns ingen uppdatering att hämta just nu." delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
[alert release];
}
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
[standardUserDefaults setObject:lastModifiedString forKey:#"LastModified"];
[standardUserDefaults synchronize];
}
}
[self.receivedData setLength:0];
self.fileSize = [[NSNumber numberWithLong: [response expectedContentLength]] retain];
}
Last the connectionDidFinishLaunching method:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (checkHeaders == NO)
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
[self.receivedData writeToFile:[basePath stringByAppendingPathComponent:#"Kapareskolan.zip"] atomically:YES];
[self unzipDownloadedFile];
self.progressView.hidden = YES;
NSLog(#"Finished...");
}
[connection cancel];
}
I know the didFinishLaunching method gets called twice, but I want to know how I could get so the method doesn't get called twice if there is an update?
I know it's a lot asked and much code here but just give me a hint and I'll be very thankful.
If you're done with the first connection object in the didReceiveResponse method you should cancel it then. Otherwise it's going to make it to the connectionDidFinishLoading method.
I think this is what you want:
if (![lastModifiedString isEqualToString:savedString])
{
[connection cancel];
[self downloadNewFile];
}
Also It looks like you're setting checkHeaders to NO after you start the second request which could cause a race condition.
According to the programming guide, a connection "can be canceled any time before the delegate receives a connectionDidFinishLoading: or connection:didFailWithError: message by sending the connection a cancel message".
so why not try moving
[connection cancel];
from the connectionDidFinishLoading method to just after your if-else block in the didReceiveResponse delegate method? You want to cancel the "checkHeaders==YES" connection in either case; either you're about to kick off a new connection, or you already know all you need to know about the current connection.
UPDATED as requested:
if (![lastModifiedString isEqualToString:savedString]) {
[self downloadNewFile];
} else { // you've already implicitly checked for equality above
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Ingen uppdatering tillgänglig" message:#"Det finns ingen uppdatering att hämta just nu." delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
[alert release];
}
// you've used the connection for everything that you need, so cancel it in either case
[connection cancel];
as downloadNewFile kicks off its NSURLConnection asynchronously, this should be okay in the event that the two strings are equal. Slightly safer would be to move the cancel method call to just prior to the if-else check.