UIView (subview) does not send IBActions to AppDelegate - iphone

I'm having this problem because I originially made everything in the main NIB, but then after reading learned that it is better to use subviews.
I've got my IBActions in the AppDelegate, and I've successfully connected and loaded my subviews. However, when I try to connect my buttons in the subviews to the IBActions in the AppDelegate, the IBActions appear under the "First Responder". They seem to connect fine, but when running the application they do not trigger the IBActions (I've confirmed this with some NSLogs, it's not an error in the code within the IBActions). What am I doing wrong?
Thanks!

The AppDelegate should only be used for very specific items such as implementing the UIApplicationDelegate protocol (i.e. methods like applicationDidFinishLaunching) or in some cases storing global variables.
You should keep IBActions and other outlets in their respective view controller files (i.e. if you created MyViewController.h and MyViewController.m which are linked with MyViewController.xib where you have some buttons, images, etc.). They can then be hooked up via dragging the inspector control you want (i.e. TouchUpInside) to the File's Owner.
Something you should read to better understand view controllers: http://developer.apple.com/iphone/library/featuredarticles/ViewControllerPGforiPhoneOS/Introduction/Introduction.html
Typically it is best to create a unique view controller for each view you will present to the user. For instance, if I had a main screen and then an "about" or a settings screen, I would make each of those their own view controller. It helps organize things better than using one view with a whole bunch of subviews that you hide/show and will also improve loading times and general performance.
Update for your 2nd question in the comments about accessing the app delegate:
First, you need to import the .h file (i.e. #import "AppDelegate.h") for the app delegate into whichever view controller .m file you wanna use to access whatever variables, arrays, etc you have stored in the app delegate files. Make sure you synthesize whichever objects you create in the app delegate's .h file in the app delegate's .m file so the getters and setters are created (so you can access them).
Then in the view controller .m file, in whichever method you are using:
-(void)someMethod {
// here we just create a shortcut to the app delegate called "theAppDelegate"
YourAppDelegateFileNameHere *theAppDelegate = (YourAppDelegateFileNameHere *)[[UIApplication sharedApplication] delegate];
// now you can use the dot notation if you wanna access a variable
int SomeNewInteger = theAppDelegate.someIntegerYouHaveStored;
// or some array you have stored
return [theAppDelegate.someArrayYouCreated count];
}
Hope that helps!

Related

How to make a delegate object accessible throughout a ViewController

I'm trying to get an instance of my AppDelegate accessible to all methods in each ViewController that I have. If I try to declare it with other class variables I get Initializer Element is not a compile-time constant. If I declare it in a method within the ViewController however I am able to use it. I am trying to save integers and floats to properties I have set up in the AppDelegate (a big no-no I know, but this is a project for an introductory class and I'm not expected to get too advanced, especially since everything we've done so far is not compliant with the MVC paradigm). The app uses a toolbar to switch between views using the app's ViewController to load the other ViewControllers as subviews. I was going to put the AppDelegate declaration and update statements in the ViewDidUnload method of each view controller, but I'm not sure that the Views are unloaded whenever they are switched (they're switched by removing the current View from the SuperView and loading the new one as a Subview at index 0). What happens to the views that are not currently being viewed then? Is there a method that detects that that I could implement the AppDelegate declaration and updates into?
I guess ideally I'd like to be able to access the AppDelegate object in any method in my ViewControllers because I have a lot of quantities being updated throughout and would like to have those quantities updated in the AppDelegates values as soon as they happen, since I'm not sure what happens with a View is cleared from SuperView
Thanks in advance everyone
You can access your app delegate via [[UIApplication sharedApplication] delegate] from anywhere in your application.
You should never instantiate another copy of the object on your own. The system does this for you at startup.
As for detecting changes, you can override the viewDidDisappear method of UIViewController. (You're correct--in general, they will not be unloaded when switched, and viewDidUnload will not be called)

UITableViewController loading inside a UIViewController inside a UIViewController issue

I don't really know if what I'm doing is the right way to do it. Right now it seems to be working until it hits a certain point with a EXC_BAD_ACCESS message.
I'll describe what I'm doing as best and with the most relevant details I can tell:
I have a CalendarViewController that inherits UIViewController which is loading from a .xib file (CalendarViewController.xib). The class contains a UIView called contentView which I created and which I initialize with another nib file based on a class which is also inherited from UIViewController:
- (void)viewDidLoad {
[super viewDidLoad];
calendarView = [[CalendarView alloc] initWithNibName:#"CalendarView" bundle:nil];
[contentView addSubview:calendarView.view];
}
(calendarView is the class inheriting UIViewController and viewDidLoad is from CalendarViewController.
CalendarView.xib has a UITableViewController with it's respective UITableView. This Table View Controller is linked to a CalendarTableController to which I also generated a .xib file for it.
Everything is being created just right (apparently) but it is crashing somewhere very unexpected. CalendarTableController also implements a DateLoaderDelegate which loads information from an xml on an external url.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// When the data has all finished loading, we set a copy of the
// loaded data for us to access. This will allow us to not worry about
// whether a load is already in progress when accessing the data.
self.lastLoadedMatchXMLData = [self.matchXMLData copy];
// Make sure the _delegate object actually has the xmlDidFinishLoading
// method, and if it does, call it to notify the delegate that the
// data has finished loading.
if ([_delegate respondsToSelector:#selector(xmlDidFinishLoading)])
{
[_delegate xmlDidFinishLoading];
}
}
The application is getting to this point without any problem. _delegate is containing the correct object (a CalendarTableController which implements the DateLoaderDelegate). But when it arrives to the line:
if ([_delegate respondsToSelector:#selector(xmlDidFinishLoading)])
it crashes with the EXC_BAD_ACCESS, I don't really know the reason, if I look at the debugger, the crash is not occurring in any of my classes as any of them are appearing in the execution stack. URLConnectionClient seems to be generating it, but I don't really know why. The loading of the xml had worked earlier before I rearranged the ViewControllers to work as I have described now.
Any ideas?
It's weird. I fixed the problem but I had to dedicate the whole UIViewController to contain the UITableView. What I did was this:
Create an IBOutlet with the custom UITableViewController (CalendarTableViewController) in the custom UIViewController.
In the loaded .xib file I linked the IBOutlet to a created UITableViewController declared as a CalendarTableViewController.
This way I solved the problem of the UITableViewController being deallocated without reason. But now the image views I had placed in the intermediate UIViewController wouldn't appear. I had to set that UIViewController to contain solely the CalendarTableView and place the image views in the initial UIViewController. Weird, isn't it? I don't like much the hierarchy I created... but for now that will do =S
Check that you have defined properties for all of your subviews and that you are retaining everything that you need to be. Bad Access May mean that you're attempting to call respondsToSelector on an object that has been released.
Have you tried calling loadView before adding the nested controller's view to the parent's view?
Maybe viewDidLoad is not executing before you add the view and some variables were never initialized.

Using Interface Builder efficiently

I am new to iPhone and objective c. I have spent hours and hours and hours reading documents and trying to understand how things work. I have RTFM or at least am in the process.
My main problem is that I want to understand how to specify where an event gets passed to and the only way I have been able to do it is by specifying delegates but I am certain there is an easier/quicker way in IB.
So, an example.
Lets say I have 20 different views and view controllers and one MyAppDelegate.
I want to be able to build all of these different Xib files in IB and add however many buttons and text fields and whatever and then specify that they all produce some event in the MyAppDelegate object. To do this I added a MyAppDelegate object in each view controller in IB's list view. Then I created an IBAction method in MyAppDelegate in XCode and went back to IB and linked all of the events to the MyAppDelegate object in each Xib file.
However when I tried running it it just crashed with a bad read exception.
My guess is that each Xib file is putting a MyAppDelegate object pointer that has nothing to do with the eventual MyAppDelegate adress that will actually be created at runtime.
So my question is...how can I do this?!!!
If you create an instance of MyAppDelegate in each nib file then, yes, you do end up with a lot of different instances of the class when all the nibs load. The app delegate is not identified by class or even protocol but rather by being the object pointed to by the application instance's delegate property. To find the true app delegate, you have have to ask the application object itself for its delegate
You should have all your view controllers descend from a parent view controller class that has an appDelegate property. Implement something like this:
#import "MyAppDelegateClass.h"
#interface ViewControllerBaseClass :UIViewController {
MyAppDelegateClass *appDelegate;
}
#property(nonatomic, retain) *appDelegate;
#end
#implementation ViewControllerBaseClass
#synthesize appDelegate;
-(MyAppDelegateClass *) appDelegate{
self.appDelegate=(MyAppDelegateClass *)[[UIApplication sharedInstance] delegate];
return appDelegate;
}
#end
When the view controller needs the app delegate it just calls self.appDelegate. If you want to access an attribute of the app delegate use self.appDelegate.attributeName.
The important thing is that you ask the application for its specific delegate instance at runtime. You can't do that from a nib file.
I'm not entirely clear what exactly you're trying to do, but it's probably a bad idea. There should only be one app delegate per application, and it should deal with behavior for the whole application. Typically, the app delegate initializes the root view controller(s) and displays them, but not much else (other than handling things like opening and saving data sources).
The view controllers (subclasses of UIViewController) should interact with the XIBs. Having the view-specific behavior in the view controllers makes the app much easier to manage and maintain. Typically, there should be 0 or 1 XIBs per view controller (more than that is complicated). You set up the interaction with the views using the Target/Action pattern with IBOutlets and IBActions (see here for a complete guide). It's generally a bad idea to make view controllers or XIBs dependent on the app delegate (since reducing dependencies again makes the code easier to manage).
In general you should be making a view controller for each of the views you are building, and link events to those view controllers - not the app delegate. In fact usually no event ever is wired to the app delegate from any nib file, even in the sample projects you'll note that view controllers are created and held onto by the app delegate, but it does not receive events.

iPhone: How to Pass Data Between Several Viewcontrollers in a Tabbar App

I have following problem:
I have built a tabbar application with 4 tabs. I want to pass a object/variable from the first tab controller to the third one and initialize this controller with the corresponding object.
I've already done some research. The best way, corresponding to a clean model approach, would be to call some initWithObject: method on the called viewcontroller.
How can I achieve this? How can I call the init method of the receivercontroller within the callercontroller? Can you give me some code example?
Edit:
To pass data between several views/classes etc simply create some Kind of data class which holds the data beeing shared between several classes. For more information follow the link:
Singleton
You need a data model object that stores the data for application.
A data model is a customized, standalone object accessible from anywhere in the application. The data model object knows nothing about any views or view controllers. It just stores data and the logical relationships between that data.
When different parts of the app need to write or read data, they write and read to the data model. In your case, view1 would save its data to the data model when it unloads and then view2 would read that data from the data model when it loads (or vice versa.)
In a properly designed app, no two view controllers should have access to the internal data of another controller. (The only reason a view controllers needs to know of the existence of another controller is if it has to trigger the loading of that other controller.)
The quick and dirty way to create a data model is to add attributes to the app delegate and then call the app delegate from the view controllers using:
YourAppDelegateClass *appDelegate = [[UIApplication sharedApplication] delegate];
myLocalProperty = appDelegate.someDataModelProperty;
This will work for small project but as your data grows complex, you should create a dedicated class for your data model.
Edit:
To clarify for your specific case, you would add the call to the data model when the receiver viewController becomes active.
Placing the data in an init method or a viewDidLoad won't work because in a UITabBar the users can switch back and forth without unloading the view or reinitializing the view controller.
The best place to retrieve changing data is in the viewWillAppear controller method. That way the data will be updated every time the user switches to that tab.
You might want to consider NSNotificationCenter (Reference); you register the one viewcontroller with the application notification center, and send a notification when a selection is made. When the notification is received, the other viewcontroller updates itself accordingly.
I don't think this is best practice (also check syntax) however I have got away with:
in the .h
otherclassref *otherclassname
#property (assign) otherclassname otherclassref;
and in the .m
#synthesize otherclassref;
then I just assign the reference from somewhere convenient e.g. the app delegate or wherever you are instantiating your viewcontrollers.
then the view controller can get a reference to the other view controller.
I add #class secondviewcontroller to the .h file for the firstviewcontroller and put put the #imports "secondviewcontroller.h" in the .m file of the first view controller. These are called forward references and prevent compiler errors resulting from having .h files referencing each other.

How to use generic (NSObject) controller with subviews of a UIViewController?

I have a UIViewController that is loading several subviews at different times based on user interaction. I originally built all of these subviews in code, with no nib files. Now I am moving to nib files with custom UIView subclasses.
Some of these subviews display static data, and I am using loadNibNamed:owner:options: to load them into the view controller. Others contain controls that I need to access.
I (sort of) understand the reasons Apple says to use one view controller per screen of content, using generic controller objects (NSObjects) to manage subsections of a screen.
So I need a view controller, a generic controller, a view class and a nib. How do I put this all together?
My working assumptions and subsequent questions:
I will associate the view class with
the nib in the 'class identity' drop
down in IB.
The view controller will coordinate
overall screen interactions. When
necessary, it will create an instance
of the generic controller.
Does the generic controller load the
nib? How?
Do I define the outlets and actions
in that view class, or should they be
in the generic controller?
How do I pass messages between the
view controller and the generic
controller?
If anyone can point me to some sample code using a controller in this way, it will go a long way to helping me understand. None of the books or stackoverflow posts I've read have quite hit the spot yet.
Okay, I think I figured it out:
Extend NSObject to make your CustomController
Define your outlets & actons in CustomController.h, including a reference to the UIView in your nib
Set the File's Owner of your nib to CustomController
Hook up all your outlets & actions as usual, including the UIView outlet
In your CustomController.m init, load the nib
- (id)init {
self = [super init];
if (self != nil)
[self loadNib];
return self;
}
- (BOOL)loadNib {
NSArray *topLevelObjs = nil;
topLevelObjs = [[NSBundle mainBundle] loadNibNamed:#"CustomView" owner:self options:nil];
if (topLevelObjs == nil) {
NSLog(#"Error! Could not load nib file.\n");
return NO;
}
return YES;
}
The new NSObject based controller will work very much like a view controller.
It sounds like what you want is what I've coined "reusable UIView widgets" -- reusable widgets that do something / present a display that you can incorporate in your app screens wherever and how many times as you want -- you can create them purely in code or instantiate them by placing their frame in another xib file (but you don't get to change the widgets' internal parameters in the xib file, that would require an IB plugin).
This is an organization that I've never seen discussed anywhere. Part of my frustration early on with iOS programming is wanting something like this but not seeing any way to express it in any example.
See my answer in this question to see how it can be structured:
UIView and initWithFrame and a NIB file. How can i get the NIB file loaded?
I recommend placing all widget-internal handling of direct/low-level events in the uiview widget subclass, and implememnt a delegate protocol for a highler level interaction with the widget client (i.e., "loginRequested:userName:passWord" instead of manually accessing button and textfields internal to the widget).
The (optional but recommended) xib file for the widget has an owner of the widget, and the init code in the widget owns the duty of loading the xib file. The customer of the widget simply instantiates the widget and implements whicever widget delegate functions makes sense for it.