How to make a class as a delegate for another? - iphone

I am learning objective-c and I get some trouble with delegates.
In a test application, i have a viewcontroller having a IUbutton, and another UITableViewcontroller. I want to make the TableViewController appears when I click the button.
the problem that the window object is not identified in this ViewController.
[self.window addSubview:viewController.view]; this line shows errors.
I think I should delegate the appdelagate to my viewcontroller ? how to do ?

Add this in the applicationDidFinishLaunching method of app delegate
[self.window addSubview:viewController.view];
Or if you want to access the main window in a view controller u can refer this link
Referencing AppDelegate instance variables

you cant access the window by self you need an object of appDelegate class
so code like this
yourAppDelegate *objDelegate=(yourAppDelegate *)[[UIApplication sharedApplication] delegate];
now you can access the window,
[objDelegate.window addSubview:viewController.view];

I just copy pasted this code form my project.. I have created a delegate in this class "appImageDidLoad" this delegate called when the image downloaded completely. Hope it will help you can post comment if you need any explaination
#import "MixtapeInfo.h"
#class Record;
#protocol IconDownloaderDelegate;
#interface IconDownloader : NSObject
{
MixtapeInfo *appRecord;
NSIndexPath *indexPathInTableView;
id <IconDownloaderDelegate> delegate;
NSMutableData *activeDownload;
NSURLConnection *imageConnection;
}
#property (nonatomic, retain) MixtapeInfo *appRecord;
#property (nonatomic, retain) NSIndexPath *indexPathInTableView;
#property (nonatomic, assign) id <IconDownloaderDelegate> delegate;
#property (nonatomic, retain) NSMutableData *activeDownload;
#property (nonatomic, retain) NSURLConnection *imageConnection;
- (void)startDownload;
- (void)cancelDownload;
#end
#protocol IconDownloaderDelegate
- (void)appImageDidLoad:(NSIndexPath *)indexPath;
#end
#import "IconDownloader.h"
#import "MixtapeInfo.h"
#define kAppIconHeight 48
#define TMP NSTemporaryDirectory()
#implementation IconDownloader
#synthesize appRecord;
#synthesize indexPathInTableView;
#synthesize delegate;
#synthesize activeDownload;
#synthesize imageConnection;
#pragma mark
- (void)dealloc
{
[appRecord release];
[indexPathInTableView release];
[activeDownload release];
[imageConnection cancel];
[imageConnection release];
[super dealloc];
}
- (void)startDownload
{
self.activeDownload = [NSMutableData data];
// alloc+init and start an NSURLConnection; release on completion/failure
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:
[NSURLRequest requestWithURL:
[NSURL URLWithString:appRecord.mixtape_image]] delegate:self];
self.imageConnection = conn;
[conn release];
}
- (void)cancelDownload
{
[self.imageConnection cancel];
self.imageConnection = nil;
self.activeDownload = nil;
}
#pragma mark -
#pragma mark Download support (NSURLConnectionDelegate)
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.activeDownload appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// Clear the activeDownload property to allow later attempts
self.activeDownload = nil;
// Release the connection now that it's finished
self.imageConnection = nil;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// Set appIcon and clear temporary data/image
UIImage *image = [[UIImage alloc] initWithData:self.activeDownload];
self.appRecord.mixtape_image_obj = image;
self.activeDownload = nil;
[image release];
// Release the connection now that it's finished
self.imageConnection = nil;
// call our delegate and tell it that our icon is ready for display
[delegate appImageDidLoad:self.indexPathInTableView];
}
#end

i would guide you through this in a tutorial.
1) Create a new project in xcode using view application template
2) Open up your view's xib file
3) Drag a UITableView and a UIButton from the library to the view.
4) Select your UITableView view and checked the hidden property.
5) Go to your view's header file and declare two IBOutlets (for your button and table), 1 IBAction (for your button's target action).
6) Link up the outlets to the respective UI objects in your Interface Builder and set the target action of the button (touch up event) to your controller.
7) In your IBAction, set the hidden property of the UITableView to NO and button's hidden property to YES.
8) Volia, you should see your table view now when you click on the button and the button would become hidden after you clicked
I skip the part on adding the tableview delegates so you will not see anything in your table. You can check them out on Apple's UITableView example.
EDIT:
In your IBAction for the button, put something similar like this
- Require a navigation controller:
[self.navigationController pushViewController:putTheNameOfYourTableViewControllerHere animated:YES];
- Present in a modal view:
[self presentModalViewController:putTheNameOfYourTableViewControllerHere animated:YES];

Related

UIWebView is not loading url

I know this is a common question but none of the answers fix my problem. I have a UIWebView in my app. I have everything set up correctly; the delegate is set, the webView is a property of my view controller. It is set up to load an html string upon loading the viewController. When the html string loads, in the webViewDidFinishLoad method it checks if this is the first time it loaded, and if it is it is told to load a request with a url. The string i use for the url is a property of my viewController. That string is exactly what it is supposed to be. There is just something preventing the webView from actually loading the request. Any ideas would be very much appreciated. Thank you
#interface WebViewController : UIViewController <UIWebViewDelegate, UIActionSheetDelegate, UIPrintInteractionControllerDelegate, MFMailComposeViewControllerDelegate, NSURLConnectionDelegate>
#property (nonatomic, assign) BOOL firstLoad;
#property (nonatomic, retain) UIWebView *webView;
#property (nonatomic, retain) NSString *prefixURLString;
#property (nonatomic, retain) NSString *suffixURLString;
#property (nonatomic, retain) UIActionSheet *actionSheet;
- (id)initWithTitle:(NSString *)newTitle andSuffixURL:(NSString *)suffix;
- (void)reload;
- (void)addActionButton;
- (void)showActions:(UIBarButtonItem *)sender;
- (void)dismissActionSheet;
#end
#implementation WebViewController
#synthesize webView;
#synthesize prefixURLString;
#synthesize suffixURLString;
#synthesize firstLoad;
#synthesize actionSheet;
- (id)initWithTitle:(NSString *)newTitle withPrefixURL:(NSString *)prefix andSuffixURL:(NSString *)suffix
{
self = [super initWithNibName:nil bundle:nil];
if (self) {
// Custom initialization
[self setPrefixURLString:prefix];
[self setFirstLoad:YES];
[self setTitle:newTitle];
[self setSuffixURLString:suffix];
[self setActionSheet:nil];
}
return self;
}
- (id)initWithTitle:(NSString *)newTitle andSuffixURL:(NSString *)suffix
{
return [self initWithTitle:newTitle withPrefixURL:#"https://customer.stld.com/flatroll/ios/%#" andSuffixURL:suffix];
}
- (void)reload
{
if(self.prefixURLString && self.suffixURLString)
{
NSString *fullURLString = [NSString stringWithFormat:self.prefixURLString, [self.suffixURLString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:fullURLString]]];
}
}
- (void)dealloc
{
[suffixURLString release]; suffixURLString = nil;
[prefixURLString release]; prefixURLString = nil;
[actionSheet release]; actionSheet = nil;
[super dealloc];
}
- (void):(UIWebView *)aWebView didFailLoadWithError:(NSError *)error
{
//NSLog(#"WebViewController:DidFailLoadWithError: %#", [error localizedFailureReason]);
[self setTitle:#"Loading Error"];
[self.webView loadHTMLString:[NSString stringWithFormat:#"<html><head><meta name=\"viewport\" content=\"width=device-width, user-scalable=yes\" /></head><body>Loading error:<br />%#</body></html>", [error localizedFailureReason]] baseURL:nil];
}
- (void)webViewDidFinishLoad:(UIWebView *)aWebView
{
if(self.firstLoad)
{
[self setFirstLoad:NO];
[self reload];
[self addActionButton];
}
}
#pragma mark - View lifecycle
- (void)loadView
{
UIWebView *newWebView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
[newWebView setDelegate:self];
[newWebView setScalesPageToFit:YES];
[newWebView setUserInteractionEnabled:YES];
[self setWebView:newWebView];
[self setView:newWebView];
[newWebView release];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.webView loadHTMLString:#"<html><head><meta name=\"viewport\" content=\"width=device-width, user-scalable=yes\" /></head><body><br />Gathering data and generating report.<br />Depends on data and parameters you have requested, this could take time. Please wait...</body></html>" baseURL:nil];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
[self.webView setDelegate:nil];
[self setWebView:nil];
[self setView:nil];
}
I had the same problem, I'm not entirely sure why, but I guess it has to do something with viewControllers life cycle. Anyway try load URL in viewDidAppear method. It fixed it for me. Hope it will help
It turns out my problem had nothing to do with the UIWebView. I just had to change the credential persistence in the URLConnectionDelegate I had set up for my app. I had it set as NSURLCredentialPersistanceNone but I changed it to NSURLCredentialPersistenceSession. I hope this can help anyone else out there who had the exact problem as I did

Passing variables to the view from AppDelegate

i open a view in AppDelegate
with:
AppDelegate
PushDetail *pushdtls = [[PushDetail alloc] initWithNibName:nil bundle:nil];
pushdtls.seminarurl = [NSURL URLWithString:resourcePathURL]; /passing URL
[self.window presentModalViewController:pushdtls animated:YES];
PushDetail.h
#property (nonatomic) NSURL *seminarurl;
PushDetail.m
- (void)viewDidLoad
{
NSURLRequest *requestObj = [NSURLRequest requestWithURL:seminarurl];
[pushDetails loadRequest:requestObj];
}
But the webview is empty... what I do wrong?
and the second question how to close the view I opened?
- (IBAction) closeNews{
//[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
//[self release];
[self dismissModalViewControllerAnimated:YES];
}
does not work :(
You may implement the UIWebViewDelegate protocol within your view controller and fetch the results/error messages yourself.
Namely didFailLoadWithError - see the UIWebViewDelegate Protocol References for more.
Maybe its just a typo on your URL.
Example:
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
NSLog(#"oops, failed with error: %#", error);
}
You've not specified what pushDetails is, but I'm assuming it's an IBOutlet to a UIWebview on your PushDetail controller. My guess is that you've forgotten to bind your pushDetails outlet to the webview in your nib file.
You could create lets say a file that holds all your global variables lets call it GlobalVariables.h and .m. In the GlobalVariables.h file add this
extern NSURL *seminarurl;
#interface GlobalVariables
#property (retain, nonatomic) NSURL *seminarurl;
#end
and in the GlobalVariables.m file add
#import "GlobalVariables.h"
#implements GlobalVariables
#synthesize seminarurl;
NSURL *seminarurl; // You could add what your URL is here as well you would just use '='
#end
So when you want to access it or assign a variable to it, it would just be like accessing or assigning any other variable. Just remember to import 'GlobalVariables.h' into which ever .m file you are using it in.
make your property retained. ex >> #property (nonatomic,retain).
if the url is a property in your app delegate .. you can access it like this
[UIApplication SharedApplication].delegate.yourpropertyname;

Regarding Apple's KMLViewer placemarkDescription and annotation subtitle

In my app I am using Apple's KMLViewer to show annotations that I get from a KML file.In the file KMLParser.m, there is an instance variable, placemarkDescription that converts the information under Description tags from kml file to annotation subtitle.Now, in my file every annotation has the information stored under Description in this way:
<table width="280px"><tr><td></td><td></td></tr></table><table width="280px"><tr><td><b>Fitness Bulls</b>---Palester sportive. Sporti dhe koha e lire.....<a href="http://www.site.com/BIZ_DIR/810180432/Article-Fitness-Bulls.aspx" style="color:Green;" >Shikoni detajet >></a></td></tr><tr><td><a href="http://www.site.com/HartaV2/AddReview.aspx?gisDataId=8123855e-b798-40bc-ad2e-00346a931211" style="color:Green;" >Shkruani pershtypjen tuaj >> </a> <p style="float:right;">Postuar nga:<i>Import</i></p></td></tr></table>
In KMLParser.m i have transformed the placemarkDescription from that to this:
<html><body>
<table width="280px"><tr><td></td><td></td></tr></table><table width="280px"><tr><td>
<b>Fitness Bulls</b>---Palester sportive. Sporti dhe koha e lire.....
<a href="http://www.site.com/BIZ_DIR/810180432/Article-Fitness-Bulls.aspx" style="color:Green;" >Shikoni detajet >></a></td></tr><tr><td>
<a href="http://www.site.com/HartaV2/AddReview.aspx?gisDataId=8123855e-b798-40bc-ad2e-00346a931211" style="color:Green;" >Shkruani pershtypjen tuaj >> </a> <p
style="float:right;">Postuar nga:<i>Import</i></p></td></tr></table>
</body></html>
I've done this because I want to pas this string to a webView and visualize this in it.
The problem is that when the kml loads, the methods get the description information, get called severe times.Exactly as times as placemarks stored in the kml.So passing the string directly has no effect.If i choose to set active the subtitle option (annotation.subtitle = placemarkDescription in KMLParser), maybe I gen get the subtitle info of the annotation the user tapped, but I don't want to show this information because it shows like this
<table width="280px"><tr><td></td><td></td></tr></table><table width="280px"><tr......
By the way, I don't have any idea of how to get the subtitle info of the selected annotation.
So far, I have managed only to store the description information in an array (done this in the KMLParser.m).But what should I do with that array?How to know to which array entry corresponds the annotation the user tapped (the annotation that has the callout bubble opened).
So I don't know what to do.
Maybe I have not been too clear: What I want to do is get the Description information of a placemark (annotation), when the user taps an annotation in the map, tapping the disclosureButton should redirect him to a webView that shows the description Information.
EDIT code Added:
DetailViewController.h
#import <UIKit/UIKit.h>
#interface DetailViewController : UIViewController<UIWebViewDelegate> {
UIWebView *webView;
UITextField *addressBar;
UIActivityIndicatorView *activityIndicator;
NSString *placemarkDescription;
}
#property (nonatomic, retain) IBOutlet UIWebView *webView;
#property (nonatomic, retain) IBOutlet UITextField *addressBar;
#property (nonatomic, retain) IBOutlet UIActivityIndicatorView *activityIndicator;
#property (nonatomic, retain) NSString *placemarkDescription;
-(IBAction) gotoAddress:(id)sender;
-(IBAction) goBack:(id)sender;
-(IBAction) goForward:(id)sender;
#end
DetailViewController.m
#import "DetailViewController.h"
#implementation DetailViewController
#synthesize webView, addressBar, activityIndicator, placemarkDescription;
- (void)viewDidLoad {
[super viewDidLoad];
[webView loadHTMLString:placemarkDescription baseURL:nil];
}
-(IBAction)gotoAddress:(id) sender {
NSURL *url = [NSURL URLWithString:[addressBar text]];
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
[webView loadRequest:requestObj];
[addressBar resignFirstResponder];
}
-(IBAction) goBack:(id)sender {
[webView goBack];
}
-(IBAction) goForward:(id)sender {
[webView goForward];
}
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
if (navigationType == UIWebViewNavigationTypeLinkClicked) {
NSURL *URL = [request URL];
if ([[URL scheme] isEqualToString:#"http"]) {
[addressBar setText:[URL absoluteString]];
[self gotoAddress:nil];
}
return NO;
}
return YES;
}
- (void)webViewDidStartLoad:(UIWebView *)webView {
[activityIndicator startAnimating];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[activityIndicator stopAnimating];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)dealloc {
[super dealloc];
}
#end
PlacemarkAnnotation2.h
#import <Foundation/Foundation.h>
#import <MapKit/Mapkit.h>
#interface PlacemarkAnnotation2 : NSObject <MKAnnotation> {
CLLocationCoordinate2D coordinate;
NSString * title;
NSString * subtitle;
NSString * placemarkDescription;
}
#property (nonatomic, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, retain) NSString * title;
#property (nonatomic, retain) NSString * subtitle;
#property (nonatomic, retain) NSString * placemarkDescription;
#end
PlacemarkAnnotation2.m
#import "PlacemarkAnnotation2.h"
#implementation PlacemarkAnnotation2
#synthesize coordinate, title, subtitle, placemarkDescription;
- (id) initWithCoordinate:(CLLocationCoordinate2D)coord andTitle:(NSString *)maintitle andSubtitle:(NSString *)subTitle {
self.coordinate = coord;
self.title = maintitle;
self.subtitle = subTitle;
return self;
}
-(NSString *) placemarkDescription
{
return placemarkDescription;
}
- (void) setPlacemarkDescription: (NSString *) pd
{
placemarkDescription = pd;
}
- (void) dealloc {
[title dealloc];
[subtitle dealloc];
[placemarkDescription dealloc];
[super dealloc];
}
#end
Changes in KMLParser.M
//KMLPoint class
- (MKShape *)mapkitShape
{
PlacemarkAnnotation2 *annotation = [[PlacemarkAnnotation2 alloc] init];
annotation.coordinate = point;
return [annotation autorelease];
}
//KMLPlacemark class
- (void)_createShape
{
if (!mkShape) {
mkShape = [[geometry mapkitShape] retain];
mkShape.title = name;
// Skip setting the subtitle for now because they're frequently
// too verbose for viewing on in a callout in most kml files.
NSString *lessThan = #"<";
NSString *greaterThan = #">";
placemarkDescription = [placemarkDescription stringByReplacingOccurrencesOfString:lessThan
withString:#"<"];
placemarkDescription = [placemarkDescription stringByReplacingOccurrencesOfString:greaterThan
withString:#">"];
NSString *beforeBody = #"<html><body>";
NSString *afterBody = #"</body></html>";
NSString *finalContent = [[beforeBody stringByAppendingString:placemarkDescription]
stringByAppendingString:afterBody];
placemarkDescription = finalContent;
mkShape.placemarkDescription = placemarkDescription;
}
}
Error found in this lines of code (No cause of crash description):
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view
calloutAccessoryControlTapped:(UIControl *)control
{
NSLog(#">>> Entering %s <<<", __PRETTY_FUNCTION__);
DetailViewController *dvc = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:[NSBundle mainBundle]];
PlacemarkAnnotation2 *pa = (PlacemarkAnnotation2 *)view.annotation;
dvc.placemarkDescription = pa.placemarkDescription;
[self presentModalViewController:dvc animated:YES];
[dvc release];
NSLog(#"<<< Leaving %s >>>", __PRETTY_FUNCTION__);
}
It's not clear how much of the KMLViewer sample app code you're using but one way to do this is to create your own annotation class instead of using the MKPointAnnotation class like the sample app does.
The custom class (eg. "PlacemarkAnnotation"), should implement the MKAnnotation protocol or be a sub-class of MKShape (if you are using the KMLViewer code). In the custom class, add a placemarkDescription property.
Where the KMLViewer code currently creates an MKPointAnnotation object, create a PlacemarkAnnotation instead and set its placemarkDescription property instead of the subtitle property.
Then in the viewForAnnotation delegate method, set the rightCalloutAccessoryView to a detail disclosure button.
Next, add to the project a detail view controller with a UIWebView in it. Add a placemarkDescription property to the view controller. In the viewDidLoad method, call loadHTMLString on the web view and pass it placemarkDescription (I think you can pass nil for the baseURL).
In the map view's calloutAccessoryControlTapped delegate method, create the detail view controller, set its placemarkDescription property and present it:
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view
calloutAccessoryControlTapped:(UIControl *)control
{
DetailViewController *dvc = [[DetailViewController alloc] init...
PlacemarkAnnotation *pa = (PlacemarkAnnotation *)view.annotation;
dvc.placemarkDescription = pa.placemarkDescription;
[self presentModalViewController:dvc animated:YES];
[dvc release];
}
Edit:
First, it looks like it will be best to subclass MKShape for your custom class instead of implementing the MKAnnotation protocol. The rest of the KMLViewer code is based on this assumption. So change #interface PlacemarkAnnotation2 : NSObject <MKAnnotation> to #interface PlacemarkAnnotation2 : MKShape. (By the way, for the NSString properties, copy is more appropriate than retain and it'll get rid of warnings.)
It also looks like you may have changed the type of the mkShape ivar in KMLPlacemark (and other places) from MKShape to something else. Change these types back to MKShape.
Next, _createShape might not be the best place to set the placemarkDescription since that method is called for both overlays and annotations. Remove your changes from that method and put them in the point method (also in KMLPlacemark). Note there are a couple of potential memory-related issues with your changes. Here's my suggestion:
- (void)_createShape
{
if (!mkShape) {
mkShape = [[geometry mapkitShape] retain];
mkShape.title = name;
// Skip setting the subtitle for now because they're frequently
// too verbose for viewing on in a callout in most kml files.
}
}
- (id <MKAnnotation>)point
{
[self _createShape];
if ([mkShape isKindOfClass:[PlacemarkAnnotation2 class]])
{
if (placemarkDescription != nil)
//check for nil, otherwise will crash when
//passing to stringByAppendingString below
{
NSString *lessThan = #"<";
NSString *greaterThan = #">";
placemarkDescription = [placemarkDescription stringByReplacingOccurrencesOfString:lessThan
withString:#"<"];
placemarkDescription = [placemarkDescription stringByReplacingOccurrencesOfString:greaterThan
withString:#">"];
NSString *beforeBody = #"<html><body>";
NSString *afterBody = #"</body></html>";
NSString *finalContent = [[beforeBody stringByAppendingString:placemarkDescription]
stringByAppendingString:afterBody];
placemarkDescription = [finalContent retain];
//added retain above since finalContent is autoreleased
//and we are setting the ivar manually. otherwise,
//can result in EXC_BAD_ACCESS later.
}
PlacemarkAnnotation2 *pa2 = (PlacemarkAnnotation2 *)mkShape;
pa2.placemarkDescription = placemarkDescription;
return (id <MKAnnotation>)mkShape;
}
return nil;
}

iPhone - UIActivityIndicatorView does not hide when stopped

I have a custom UIWebView written like this :
.h
#interface MiniWebViewController : UIViewController {
NSString* destinationURL;
UIWebView* webView;
UIActivityIndicatorView* activityIndicatorView;
}
#property (nonatomic, retain) NSString* destinationURL;
#property (nonatomic, retain) IBOutlet UIWebView* webView;
#property (nonatomic, retain) IBOutlet UIActivityIndicatorView* activityIndicatorView;
- (void) run;
#end
.m
#synthesize destinationURL;
#synthesize webView;
#synthesize activityIndicatorView;
- (id) initWithFrame:(CGRect)frame {
if (self = [super initWithNibName:#"MiniWebView" bundle:nil]) {
self.destinationURL = #"";
self.view.frame = frame;
self.activityIndicatorView.center = self.webView.center;
}
return self;
}
- (void) run {
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.destinationURL]]];
}
It is called an initied from another ViewController :
- (void) aFunction {
MiniWebViewController* oneViewController = [[MiniWebViewController alloc] initWithFrame:CGRectMake(/*Some Rect*/];
oneViewController.webView.tag = i;
oneViewController.destinationURL = /*SomeURL*/;
oneViewController.webView.delegate = self;
[self.view addSubview:oneViewController.view]; /* the Web view is inside this one */
[oneViewController run];
}
- (void) webViewDidFinishLoad:(UIWebView *)webView {
int webViewID = webView.tag;
MiniWebViewController* webViewController = [self.webViews objectAtIndex:webViewID];
[webViewController.activityIndicatorView stopAnimating];
}
- (void)webViewDidStartLoad:(UIWebView *)webView {
int webViewID = webView.tag;
MiniWebViewController* webViewController = [self.webViews objectAtIndex:webViewID];
[webViewController.activityIndicatorView startAnimating];
}
Into IB hidesWhenStopped is also checked. Everything is linked. The style of the indicator is set to "Large White" into IB.
When running, the indicator is not large, but small.
It is successfully started and stopped (delegate calls are triggered), but it doesn't hide when stopped.
What's the problem ? I don't see...
After a careful look in your code, you are modifying the UIActivityView in your init method. Change those so that they are in your viewDidLoad. At init, you view is not yet loaded, therefore, there is not an instance of those objects yet created in your controller.
These are the statement that need to be moved:
self.activityIndicatorView.hidesWhenStopped = YES;
self.view.frame = frame;
self.activityIndicatorView.center = self.webView.center;
This goes back to a fundamental of Objective-C: Sending a message to a nil object...returns nil. This is an annoying feature at times because there is no compile time warning, nor is there any runtime exception--a feature of the language.
Stupid I am, and XCode/cocoa really doesn't help.
I had loaded a wrong XIB name that DOES NOT exist.
So I ask to load something that does not exist, and the app stil works, even showing and using objects that does not exist. Untill one call does not work as expected. No crash...
It's nonsense.

iphone app with multiple views/subviews: memory is not being deallocated

I have an iPhone application that loads succesive views in a framework based on the one explained in this link (basically a main ViewController that loads/removes additional views with a displayView method). In my application I am using NIBs (the example link uses coded views) though so each of my ViewControllers has its accompanying nib.
Debugging in Instruments shows no leaks but if I enter/leave a section (ViewController with its View.xib), the nib remains in memory so after a few in/outs memory starts to accumulate.
I know the nib is not being unloaded because one is almost programmatically created (no stuff in IB) while another does have images and buttons created in IB. The large one is loaded first and the small one loads next. You would expect a reduction in allocation in Instruments.
How can I prevent this?
My structure is as follows, with a few comments below:
`MyAppDelegate.h`
#import <UIKit/UIKit.h>
#class RootViewController;
#interface MyAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
RootViewController *viewController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet RootViewController *viewController;
-(void) displayView:(int)intNewView;
#end
`MyAppDelegate.m`
#import "MyAppDelegate.h"
#import "RootViewController.h"
#implementation MyAppDelegate
#synthesize window;
#synthesize viewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
}
-(void) displayView:(int)intNewView {
[viewController displayView:intNewView];
}
- (void)dealloc {
[viewController release];
[window release];
[super dealloc];
}
#end
This controller handles subview load/removes:
`RootViewController.h`
#import <UIKit/UIKit.h>
#interface RootViewController : UIViewController {
}
- (void) displayView:(int)intNewView;
#end
`RootViewController.m`
#import "RootViewController.h"
#import "ViewController.h"
#implementation RootViewController
UIViewController *currentView;
- (void) displayView:(int)intNewView {
NSLog(#"%i", intNewView);
[currentView.view removeFromSuperview];
[currentView release];
switch (intNewView) {
case 1:
currentView = [[ViewController alloc] initWithNibName:#"View" bundle:nil];
break;
}
[self.view addSubview:currentView.view];
}
- (void)viewDidLoad {
currentView = [[ViewController alloc]
initWithNibName:#"View" bundle:nil];
[self.view addSubview:currentView.view];
[super viewDidLoad];
}
- (void)dealloc {
[currentView release];
[super dealloc];
}
#end
There would be as many case as "detail" ViewControllers I have (right now I have 3 case but this will grow to 10 or more). The purpose of this structure is to easily move from one "section" of the application to another (NavBar controller or TabBar controller do not suit my specific needs).
`ViewController.h`
// Generic View Controller Example
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController {
UIImageView *_image1;
UIImageView *_image2;
NSTimer *_theTimer;
}
#property (nonatomic, retain) IBOutlet UIImageView *image1;
#property (nonatomic, retain) IBOutlet UIImageView *image2;
#property (nonatomic, retain) NSTimer *theTimer;
#end
`ViewController.m`
#import "ViewController.h"
#import "MyAppDelegate.h"
#synthesize image1 = _image1, image2 = _image2, theTimer = _theTimer;
- (void)loadMenu {
[self.theTimer invalidate];
self.theTimer = nil;
MyAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
[appDelegate displayView:2];
}
-(void)setView:(UIView*)aView {
if (!aView){
self.image1 = nil;
self.image2 = nil;
}
[super setView:aView];
}
- (void)viewDidLoad {
//some code
[super viewDidLoad];
}
- (void)viewDidUnload {
self.image1 = nil;
self.image2 = nil;
}
- (void)dealloc {
NSLog(#"dealloc called");
[self.theTimer invalidate];
[self.theTimer release];
[self.image1 release];
[self.image2 release];
[super dealloc];
}
Notice the NSLog in dealloc. This is being called (I can see it in the console) but the memory needed for the nib is not freed (Instruments shows an increase in memory allocation when leaving a section, because a new nib is loaded).
Any help will be greatly appreciated. I have tried a million different things and I cannot get the nibs to unload.
After a million different tries I finally ran into this forum.
It states:
Apparently images assigned in IB are loaded into image views using imageNamed. imageNamed caches the images in a way that makes them unloadable. You could load the images in viewDidLoad with initWithContentsOfFile and then assign them to the views.
Somewhere else I had read that imageNamed is the devil so I'd rather not have my images load that way.
(BTW this is iPhone OS 3.1 I'm using)
What I ended up is leaving the UIImageView intact in IB but with an empty .image value. The modified code is something like:
- (void)viewDidLoad {
NSString *path = [NSString stringWithFormat:#"%#/%#", [[NSBundle mainBundle] resourcePath], #"myImageThatBeforeWasAValueinIB.jpg"];
UIImage *image = [UIImage imageWithContentsOfFile:path];
outlet.image = image;
// do the rest of my stuff as it was
[super viewDidLoad];
}
- (void)dealloc {
outlet.image = nil;
[outlet release], outlet = nil;
[super dealloc];
}
And now everything works like a charm! Memory is recovered when I unload a nib and when I get memory warnings.
So pretty much if you have IBOutlets for UIImageViews and memory is a concern (it always is I guess), you can design all you want in IB and when the time comes to connect them to outlets, remove the image reference in IB and create it from code. IB is really good for laying out your app. It would suck to have to do all that thing by code, but I also found this nice utility that converts nibs to objective c code although I haven't tested it yet.
Did you try setting your outlet variables to nil in dealloc?
You are correctly implementing the setView method, but you are setting your outlet variables to nil in the viewDidUnload method instead of dealloc. As discussed here, you should implement dealloc as follows:
- (void)setView:(UIView *)aView {
if (!aView) { // view is being set to nil
// set outlets to nil, e.g.
self.anOutlet = nil;
}
// Invoke super's implementation last
[super setView:aView];
}
- (void)dealloc {
// release outlets and set outlet variables to nil
[anOutlet release], anOutlet = nil;
[super dealloc];
}
EDIT: if the outlets are UIImageViews, then it may be the case that you need to do
anOutlet.image = nil;
because setting the UIImage’s instance image property should increase the retain count of the UIImage’s instance by 1.