webview delegate method is not fired in background process - iphone

I am developing one application.In that i am using my static library.And i run the app in background using below code
-(IBAction)sendKeyValuePair:(id)sender
{
[NSThread detachNewThreadSelector:#selector(startTheBackgroundJob) toTarget:self withObject:nil];
}
-(void)startTheBackgroundJob
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//create the object for library class and do something and release that object.
[pool release];
}
And int that library class i created one webview object and add that webview to my main class like below
web=[[UIWebView alloc]init];
//web.delegate=self;
web.frame=CGRectMake(1, 1, 100,100);
[web loadHTMLString:html_str baseURL:nil];
[main_View.view addSubview:web];
[html_str release];
[web release];
Here my problem is if i set the delegate as self then app is crashing.And if i didn't set then delegate methods are not firing.And delegate methods are implemented in library class only.I want to set the delegate as self and run that delegate methods in library class.How to do this one.

Add this
[web setDelegate:Your library class name instance];

Related

Block main thread until UIWebView finishes loading

I'm trying to block the main thread until a UIWebView finishes loading, but calling [NSThread sleepForTimeInterval:] after calling [UIWebView loadRequest:] makes the loading never complete: The webViewDidFinishLoad delegate is called after the sleep finishes.
Why is this happening? and How can I block the main thread?
I'm calling loadPage from [ViewController willRotateToInterfaceOrientation:], what I'm trying to do is to prevent the iOS interface to rotate until the webview with the other orientation is loaded.
- (void)loadPage {
UIWebView* webview2 = [[UIWebView alloc] initWithFrame:rect];
webview2.delegate = self;
NSString* htmlPath = ..... ; // Path to local html file
NSURL *newURL = [[[NSURL alloc] initFileURLWithPath: htmlPath] autorelease];
NSURLRequest *newURLRequest = [[[NSURLRequest alloc] initWithURL: newURL] autorelease];
[webview2 loadRequest:newURLRequest];
[NSThread sleepForTimeInterval:10.0f]; // Why this blocks the uiwebview thread ??
[self.view addSubview:webview2];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSLog(#"Finished loading!");
}
I think you're trying to solve the wrong problem.
You don't want to block the main thread. Firstly, the entire UI is drawn on the main thread, so it will appear as though your app has hung. Secondly, a UIWebView is part of the UI (which is probably why what you're doing doesn't work).
So, what to do?
Have a look at the following method:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
When you start loading your page you can return NO from this method. When it's finished loading you can return values normally. UIWebView has a delegate that tells you the status.
Have a look at the class reference for NSURLRequest, there is a sync method in there.
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error

Update UILabel from applicationDidBecomeActive?

I want to update UILabel by clicking a reload button. Additionally, I want to update the label in background, because it is fetching the new data via XML from my website. Of course it would be nice to auto-update the label when the application is opened. And there is my problem:
I was able to make it work well when user were clicking the button manually. But I don't understand how to do the same by calling my method via "applicationDidBecomeActive". I tried to do it the same way, but it obviously doesn't work because my label is returned nil.
I suppose there is a problem of my understanding and the solution should be quite easy. Thanks for your input! Note: I am a beginner with Objective-C and have sometimes problems with "easy" things. ;-)
Below is a summary of the important code parts:
AppDelegate
- (void)applicationDidBecomeActive:(UIApplication *)application {
[[MyViewController alloc] reloadButtonAction];
}
MyViewController
#synthesize label
- (void)reloadButtonAction {
[self performSelectorInBackground:#selector(updateData) withObject:nil];
}
- (void)updateData {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Parse the XML File and save the data via NSUserDefaults
[[XMLParser alloc] parseXMLFileAtURL];
// Update the labels
[self performSelectorOnMainThread:#selector(updateLabels) withObject:nil waitUntilDone:NO];
[pool release];
}
- (void)updateLabels {
NSUserDefaults *variable = [NSUserDefaults standardUserDefaults];
myLabel.text = [variable stringForKey:#"myLabelText"];
// myLabel is nil when calling all of this via AppDelegate
// so no changes to the myLabel are done in that case
// but: it works perfectly when called via button selector (see below)
NSLog(#"%#",myLabel.text);
}
-(void)viewDidLoad {
// Reload button in the center
UIButton *reloadButton = [UIButton buttonWithType:UIBarButtonSystemItemRefresh];
reloadButton.frame = CGRectMake(145,75,30,30);
[reloadButton setTitle:#"" forState:UIControlStateNormal];
[reloadButton addTarget:self action:#selector(reloadButtonAction) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:reloadButton];
}
First:
[[MyViewController alloc] reloadButtonAction];
Doesn't make sense. You allocate memory, without initializing an object. And then you want to call a method on it. Doesn't work
Use an instance for it:
[myViewControllerInstance reloadButtonAction];
In your app delegate you should have an reference to your rootcontroller instance if that is the object contains the reload method, use that instance.
Note:
Alloc only reserves space in the memory for an object which size the size of MyViewController instance. An init method will fill it.

how to solve error when using thread?

I have following error msg in console when using NSThread
"Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now..."
I have submit my sample code here
- (void)viewDidLoad {
appDeleg = (NewAshley_MedisonAppDelegate *)[[UIApplication sharedApplication] delegate];
[[self tblView1] setRowHeight:80.0];
[super viewDidLoad];
self.title = #"Under Ground";
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[NSThread detachNewThreadSelector:#selector(CallParser) toTarget:self withObject:nil];
}
-(void)CallParser {
Parsing *parsing = [[Parsing alloc] init];
[parsing DownloadAndParseUnderground];
[parsing release];
[self Update_View];
//[myIndicator stopAnimating];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
here "DownloadAndParseUnderground" is the method of downloding data from the rss feed and
-(void) Update_View{
[self.tblView1 reloadData];
}
when Update_View method is called the tableView reload Data and in the cellForRowAtIndexPath create error and not display custom cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
CustomTableviewCell *cell = (CustomTableviewCell *) [tblView1 dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"customCell"
owner:self
options:nil];
cell = objCustCell;
objCustCell = nil;
}
if there is a crash, there is a backtrace. Please post it.
Method names start with lowercase letters, are camelCased, and do not contain underscores. Following these conventions will make your code easier to read by other iOS programmers and learning these conventions will make it easier to understand other iOS programmer's code.
You can't directly or indirectly invoke methods in the main thread from background threads. Your crash and your code both indicate that you are freely interacting with the main thread form non-main threads.
The documentation on the use of threads in iOS applications is quite extensive.
Your problem should come because you load your UIViewController from a thread that's not the main thread. Tipically when you try to charge data before loading the view.
To arrange this you can try to do this
1. Add a method to load your viewcontroller with just one param
-(void)pushViewController:(UIViewController*)theViewController{
[self.navigationController pushViewController:theViewController animated:YES];}
2.Change your code (commented below) in your asynchronous loading to "PerformSelectorOnMainThread"
-(void)asyncLoadMyViewController
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
MyViewController *myVC = [[myVC alloc] initWithNibName:#"myVC" bundle:nil ];
[self performSelectorOnMainThread:#selector(pushViewController:) withObject:myVC waitUntilDone:YES];
// [self.navigationController pushViewController:wnVC animated:YES];
[wnVC release];
[pool release];
}
ok please explain proper Why you require thread in parsing method? in your code u use table reload method in properly in thread....
because
u cant put any thing which relavent to your VIEW in thread...
u can put only background process like parsing in it... if u want reload table after parsing u can use some flag value in your code and after parsing u load table
Try change CallParser method to
-(void)CallParser {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Parsing *parsing = [[Parsing alloc] init];
[parsing DownloadAndParseUnderground];
[self performSelectorOnMainThread:#selector(Update_View)
withObject:nil
waitUntilDone:NO];
[parsing release];
[pool release];
}
And move
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
line to Update_View method
You can't access any UI elements from a background thread. You certainly can't create views on a background thread. Use "performSelectorOnMainThread" method instead of "detachNewThreadSelector" method.
All the best.

Warning: UIKit should not be called from a secondary thread

In my iPhone app I need to connect to a web server as this can take some time I'm using threads like this:
[NSThread detachNewThreadSelector:#selector(sendStuff) toTarget:self withObject:nil];
- (void)sendStuff {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//Need to get the string from the textField to send to server
NSString *myString = self.textField.text;
//Do some stuff here, connect to web server etc..
[pool release];
}
On the row where I use self.textField I get a warning in console saying:
void _WebThreadLockFromAnyThread(bool), 0x5d306b0: Obtaining the web lock from a thread other than the main thread or the web thread. UIKit should not be called from a secondary thread.
How can I use the textField without getting this error?
It depends a little bit on what you want to do with the textField. If reading the value is the only thing, you can do:
[NSThread detachNewThreadSelector:#selector(sendStuff) toTarget:self withObject:self.textField.text];
- (void)sendStuff:(NSString*)myString {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//Do someting with myString
[pool release];
}
If you want to change a value on the textField you could:
[self.textField performSelectorOnMainThread:#selector(setText:) withObject:#"new Text"];
Perform any selectors that handle UI updates on the main thread. You can do this with the NSObject method -performSelectorOnMainThread:withObject:waitUntilDone:
Why not:
[NSThread detachNewThreadSelector: #selector(sendStuff:) toTarget: self withObject: self.textField.text];
?
This is indeed unsafe behaviour. The MainThread is the only one that should interface the UI. Have your thread return for instance a string to the mainthread and have a method there update the UI. You can do for instance do this by passing a selector to the other thread method, and then have the other thread call the selector on the mainthread.
[self performSelectorOnMainThread:#selector(myMethod) withObject:nil waitUntilDone:NO];
will work

Send message to a different class (Obj C)

I have a UITableViewController (OnTVViewController) who's viewDidLoad is similar to below (basically parses some XML in the background and shows an activity indicator while this is happening).:
- (void)viewDidLoad {
OnTVXMLParser *xmlParser = [[OnTVXMLParser alloc] init];
/* Runs the parse command in the background */
[NSThread detachNewThreadSelector:#selector(parse) toTarget:xmlParser withObject:self];
//[xmlParser parse];
// new view to disable user interaction during downloading.
loadView = [[UIView alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
loadView.backgroundColor = [UIColor darkGrayColor];
//Loader spinner
UIActivityIndicatorView *act = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[loadView addSubview:act];
act.center =loadView.center;
[self.view addSubview:loadView];
[self.view bringSubviewToFront:loadView];
[act startAnimating];
[act release];
[super viewDidLoad];
}
OnTVViewController also has this method to remove the activity indicator (just trying to log a message while debugging):
- (void)removeActivityView {
//[loadView removeFromSuperview];
NSLog(#"Should remove activity view here");
}
In my OnTVXMLParser class I have:
- (BOOL)parse{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(#"Sleeping for 5 seconds");
[NSThread sleepForTimeInterval:5.0];
NSLog(#"Sleep finished");
// Simulated some elapsed time. I want to remove the Activity View
[self performSelectorOnMainThread:#selector(removeActivityView) withObject:nil waitUntilDone:false];
// Create and initialize an NSURL with the RSS feed address and use it to instantiate NSXMLParser
NSURL *url = [[NSURL alloc] initWithString:#"http://aurl.com/xml"];
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
// Lots of parsing stuff snipped, this all runs fine
[pool release];
return YES;
}
Basically once the "parse" method on the XMLParser class has finished I want to call the removeActivityIndicator on the OnTVViewController object. It's probably really simple but I am new to iPhone programming and banging my head against the wall.
I understand I need to use performSelectorOnMainThread - but how do I reference the instance of OnTVViewController I want to target? I've imported the OnTVViewController header file into OnTVXMLParser.
At the moment I get the error:
-[OnTVViewController removeActivityView:]: unrecognized selector sent to instance 0x8840ba0'
Typically cocoa handles this with a delegate pattern. Basically add an ivar to the XML parser named delegate, and launching the parade set the delegate to self (the OnTVViewController), and then later use the delegate for all callbacks in the XML parser
The problem is that the selector called:
-[OnTVViewController removeActivityView:]: unrecognized selector sent to instance 0x8840ba0'
does not exist. You tell it to call this selector:
[self performSelectorOnMainThread:#selector(removeActivityView) withObject:nil waitUntilDone:false];
Which doesn't exist (note the :), because your method is named:
- (void)removeActivityView
Try calling it
- (void)removeActivityView:(id)dummy
and see what happens.
You might also want to consider using NSNotificationCenter. This allows you to add an observer for a given key (a string) and selector, then post a notification from other parts of your application.
Examples:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(methodToCall:) name:#"SomeKeyName" object:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"SomeKeyName" object:nil];