I have a viewController with 4 buttons (HomePage) and then a TabBarController with 3 viewControllers.
One of the TabBarController's viewControllers I want to be used as a way to get back to the "HomePage" via a tabBar icon. I have associated a custom class that I created called "HomeViewController" to that viewController. See diagram below
HomeViewController .H file.
I have created a protocol with a method "returnToHomepage"
HomeViewController .M file
As soon as the view is loaded it calls the delegate.
In my HomepageViewController .H file I have made sure that the file adheres the protocol.
HomepageViewController .M file
I instantiate an instance of HomeViewController and set delegate to self but
returnToHomePage method never gets called! Not sure what I'm missing...
I think that your're calling the delegate method before the delegate is set.
When you call alloc-init on the controller, it initializes and ViewDidLoad is called,... and THEN you set the delegate... so this
[self.delegate returnToHomepage];
is called before
homeVC.delegate = self;
The HomeViewController you're creating in viewDidLoad is not the same one that's actually being presented onscreen. You'll need to access it with your UITabBarController's viewControllers method and set it's delegate that way.
Related
So I'm a little confused here. I have a Cocoa app, in the appdelegate header I'm declaring a NSDrawer that I've connected in Interfacebuilder and whose contentView I'm setting programmatically depending on the context. The contentviews contain Buttons that are connected to various functions in the Appdelegate.
#property (strong) IBOutlet NSDrawer *theDrawer;
When my app starts app, and I inspect it in the Debugger "theDrawer" is not nil and correctly instantiated by the Interfacebuilder. In the
Now if the user clicks any button it turns out that references to [[NSApp delegate] theDrawer] will be ignored because theDrawer is nil. which doesn't make sense to me. I tried fix this by specifically setting the delegate when the app launches.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[NSApp setDelegate:self];
}
I've checked that self.theDrawer is not nil at that point. But even after I set the delegate explicitly, any future calls to [[NSApp delegate] theDrawer] are nil.
How can I make sure to access variables on my App delegate? My understanding was that calls to NSapp delegate will return a unique instance of the app.
It seems that when a user clicks on a button that this creates a new thread and NSApp delegate will return nil for all variables.
Any help appreciated
The application delegate is properly set in main in main.m which you'll find in the "Supporting Files" folder of the project. Generally it's embedded in NSApplicationMain() which I believe references NSMainNibFile in the plist and actually has the main nib own the app delegate object instance. However Apple's not totally clear on how all that magic works. Nonetheless you can't set the delegate in applicationDidFinishLaunching -- that's a delegate function!
But if you're using a NSMainNibFile and a non-document application, the app delegate is likely being set to an object in your main NIB... in that NIB, the "File's Owner" is also the application delegate class, and the other outlets are non-nil within [NSApp delegate] because it's the main NIB file's owner. From the NSMainNibFile you can create outlets in the App Delegate class, because it's the file's owner.
If you create a second NIB, there are issues accessing the delegate. You don't want to create a object, because that isn't the same object as [NSApp delegate]. You can use the Application placeholder object and bind with a keypath of "application.delegate." But you can't create outlets because the app delegate can't be the file's owner.
But that's irrelevant, because if you have a second NIB that you're creating outlets for, they belong in a custom viewcontroller or windowcontroller subclass that you've declared is that file's owner. Even if that second NIB is loaded immediately, well then the app delegate should instantiate a controller instance to load and own the NIB, if you want outlets, it must be a custom subclass.
Apple sort of breaks this pattern by making the app delegate class the owner of the main menu "window" in MainMenu.xib in non-document applications... but that is because the menu window's a bit special. Anything you do outside of MainMenu.xib is going to need a custom controller class to have outlets.
Additional discussion in this answer about app delegate instances.
I have an MainMenu.xib, and AppController is its file owner. I added -(void)awakeFromNib method which worked fine. Now, rounds of fixings down the road awakeFromNib stopped being called, and I can't figure out why. It owns the xib, so it should be called when it is unarchived. What's going on?
EDITED:
Well, I renamed awakeFromNib to something, and called that from init... that worked. Still confused as to why awakeFromNib is not. I also have a +(void) initialize method in there, could that be messing something up?
- (id)init {
self = [super init];
if (self) {
[self something];
}
return self;
}
-(void)something {
NSLog(#"yup");
}
Setting the class name of the File's Owner in the nib is only so you can tell Xcode what object's outlets and actions to show you so you can hook things up. It doesn't affect what object is actually the File's Owner when the app runs and the nib gets loaded.
The MainMenu nib's File's Owner is always the application object, no matter what class name you set for the FO in Xcode's inspector. Setting it to any class name but NSApplication[1] is wrong.
When you run your app, you should find error messages in the Console about any outlets or actions of the AppController that you tried to connect. They couldn't be connected because the application object doesn't have them.
Change the class name back in the nib editor, and create your AppController as a custom object in the MainMenu nib.
Well, I renamed awakeFromNib to something, and called that from init... that worked.
That means that init is getting called, which means you're calling it. That's a valid alternative to creating it in a nib, though you shouldn't override awakeFromNib if it's not in a nib or owning one.
Your choice: Continue creating the AppController using alloc and init, or remove that code and create it in the MainMenu nib instead.
[1]: Or, if you've subclassed NSApplication and changed the principal class of your app bundle to be that subclass, the name of that subclass.
Is my model all a mess?
I'm writing an iPad app with multiple views. I have a masterViewController that subviews other view controllers. Some of these subviews contain buttons.
The user clicks these buttons to change views. It is my intention that the masterViewController be responsible for changing these views. But how?
How can the masterViewController tell when the button is pressed?
#implementation subViewControllerOne
- (IBAction) switchToSubViewTwo {
//Where to I send my message?
[masterViewController myMethod] //doesn't work because I need to message the specific instance
}
You could
Pass a reference to your master controller to the sub controllers and tell it that way
Make your master controller a singleton and reference it that way
You could have your sub controllers post an NSNotification that the master listens for.
You might want to go by the AppDelegate to find a reference to your controller.
Make sure you add an outlet for your masterViewController in the AppDelegate and connect it (using Interface Builder). Then, in your subview's controller, get a reference to your AppDelegate and run your great method, like so:
MyAppDelegate *delegate = (MyAppDelegate *)[UIApplication sharedApplication].delegate;
[delegate myMethod];
I instantiate a class and then I try to change the selector of the instantiated class button:
WebViewController *newtab = [[WebViewController alloc] initWithNibName:#"NavigatorNoBottom" bundle:nil];
[[newtab tabsButton]setAction:#selector(addtabs:)];
The button tabsbutton is an outlet of the WebViewController class which is directly linked in interface builder.
The method -(void)Addtabs:(id)sender is a method in my current class.
But it seems that this code does not work, my button is here but it does nothing when I click on it .
If you need more context don't hesitate. I know this is something maybe very simple but I just bug at it....
Note that -initWithNibName:bundle: does not load the nib file immediately. This means that right after sending it you cannot expect your outlets to be set.
In that line where you send -setAction:, your tabsButton outlet is probably nil.
If WebViewController inherits from NSViewController, you can place that line that sets the action in the -loadView method of your WebViewController class to make sure the nib file has been loaded and all outlets are set:
- (void)loadView {
[super loadView];
[tabsButton setAction:#selector(addtabs:)];
// or [self.tabsButton setAction:#selector(addtabs:)];
}
Alternatively, if you don’t want to do this inside the view controller, you can do the following in an arbitrary class:
WebViewController *newtab = [[WebViewController alloc] initWithNibName:#"NavigatorNoBottom"
bundle:nil];
[newtab view];
[[newtab tabsButton]setAction:#selector(addtabs:)];
By sending -view to the view controller, you force it to load the nib file, hence the tabsButton outlet will have been set right after that.
Note that you can set the action in any class since a selector is not tied to a class. Also note that, since you haven’t set a target, the action will follow the action chain.
I would like to update the UI in my application from the AppDelegate, but whenever I call it as so:
Controller *object = [[Controller alloc] init];
[object methodHere];
It doesn't seem to update the UI. What am I doing wrong here? I have put in a NSLog to see if it was being called, and it is. Here is a sample project that shows the error.
Edit: Can someone just show me what to change to the project I provided. I just don't know what to type into my project so that I can change the value of a simple NSTextField from another class.
When you write [[Controller alloc] init], you are not accessing the Controller object that is in your nib. You are creating a new Controller object that is unconnected to anything else in your application.
Remember, every Controller object is not the same any more than every NSArray is the same. Just because you made one Controller in your nib that's connected to an NSTextField does not mean some random Controller that you just created shares that controller's connections.
What you need to do is give the delegate a reference to the Controller that's in the nib.
This is really simple, and Chuck's comments basically explain what you need to do, but I will lay out the code explicitly for you. In testAppDelegate.h:
#interface testAppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
// You can make an IBOutlet to any kind of object you
// want; it's just a way for you to get a reference
// in code to an object that has been alloc'd and
// init'd already by the xib mechanism.
IBOutlet Controller *controller;
}
Then go into your xib in InterfaceBuilder and hook up that outlet from your Test App Delegate object to your Controller object (these objects are already present in the xib).
In testAppDelegate.m:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// This is the key:
// _Don't_ alloc/init a new controller object. The
// objects in your xib are allocated and initialized
// by virtue of being in that file. You just need to
// give your AppDelegate a pointer to it, as above.
[controller setTextValue:#"hello"];
}
It's being called all right, but it's not connected to the interface. There should be a view controller of some sort defined in your appDelegate.h file, call the method on that object instead.
Update for more detail:
One way you could pull this off would be to simply save the Controller when you originally create it (and not release it until later.)
Simply put your own controller object into your .h file
Controller* myController;
And when you create the new view controller you want to flip to, simply set myController to reference that object, and later when you want to update the UI, simply call
[myController methodHere];
A bit clumsy, but it works. Just don't forget to release myController when you're done with that view.
The other idea I'd suggest looking into would be to alter the method you're passing to your delegate. That is, instead of having the method as
-(returnType)callDelegateToDoSomething;
put it in as
-(returnType)callDelegateToDoSomething:(id) sender;
You call the new method the same way, but your controller should automatically pass itself as an argument. Then, inside the method, simply use
[sender methodHere];
and it should hopefully work. (You may need to play around with it a little. I'm not an expert on delegates or the sender argument, but it's worth a shot.)