Assign button from another class to a current class selector with cocoa - objective-c

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.

Related

Cocoa - awakeFromNib is not called

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.

UIViewController and UIImagePickerController: Unable to create and managing views as expected

I have a UIViewController subclass that contains an instance of UIImagePickerController. Let's call this controller CameraController. Among other things, the CameraController manages the UIImagePickerController instance's overlayView, and other views, buttons, labels etc. that are displayed when the UIImagePickerController, let's call this instance photoPicker, is displayed as the modal controller.
The photoPicker's camera overlay and the elemets that are part of the CameraController view hierarchy display and function as expected. The problem I'm having is that I cannot use UIViewController's default initializer to create the CameraController's view heirarchy.
I am initializing CameraController from within another UIViewController. Let's call this controller the WebViewController. When the user clicks on a button in a view managed by WebViewController, the launchCamera method is called. It currently looks like this:
- (void) launchCamera{
if (!cameraController) {
cameraController = [[CameraController alloc] init];
// cameraController = [[CameraController alloc] initWithNibName:#"CameraController"
// bundle:[NSBundle mainBundle]];
cameraController.delegate = self;
}
[self presentModalViewController:cameraController.photoPicker animated:NO];
}
I want to be able to create CameraController by calling initWithNibName:bundle: but it's not working
as I'll explain.
CameraController's init method looks like this:
- (id) init {
if (self == [super init]) {
// Create and configure the image picker here...
// Load the UI elements for the camera overlay.
nibContents = [[NSBundle mainBundle] loadNibNamed:#"CameraController" owner:self options:nil];
[nibContents retain];
photoPicker.cameraOverlayView = overlay;
// More initialization code here...
}
return self;
}
The only way I can get the elements to load from the CameraController.xib file is to call loadNibNamed:owner:options:. Otherwise the camera takes over but no overlay nor other view components are displayed. It appears that a side-effect of this problem is that none of the view management methods on CameraController are ever called, like viewDidLoad, viewDidAppear etc.
However, all outlets defined in the nib seem to be working. For example, when the camera loads a view is displayed with some instructions for the user. On this view is a button to dismiss it. The button is declared in CameraController along with the method that is called that dismisses this instructions view. It is all wired together through the nib and works great. Furthermore, the button to take a picture is on the view that servers as photoPicker's overlay. This button and the method that is called when it's pressed is managed by CameraController and all wired up in the nib. It works fine too.
So what am I missing? Why can't I use UIViewController's default initializer to create the CameraController instance. And, why are none of CameraController's view mangement methods ever called.
Thanks.
Your problem is easy but need some steps.
Well... First, if overlay is an IBOutlet, it can not be loaded at init time. So move picker and co in viewDidLoad. Place also here all other items that your say that they are not loaded. They should be loaded there (viewDIDLoad). Check that outlets are connected.
Second, call
cameraController = [[CameraController alloc] initWithNibName:#"CameraController"
bundle:nil];
and ensure that CameraController contains (just) a view, and CameraController inherits UIViewController. Check also file's owner.
And at some time, you may consider that calling :
[self presentModalViewController:cameraController.photoPicker animated:NO];
does not make the CameraController control your picker. Does that make sense to you ?
What does that do regarding your problem ?
It seems you are confusing some things. I try to explain in another way :
The one that controls the picker is the one that is its delegate. Your may consider creating in a MAIN view.
The controller of the overlay (added as subview) is the one that own its view in File's Owner. That may be created from the MAIN view, adding its view as subview of the controller. Basically, it is loaded just to get the overlay, but viewDidLoad, ... won't be called.
That's all and I belive those steps are not ok in your code.
That should give something like :
MainController
Loadcamera {
self.picker = [UIImagePicker alloc] init.....];
self.picker.delegate = self;
SecondController* scnd = [[SecondController alloc] initWithNibName:#"SecondController" bundle:nil];
[self.picker addOverlay:scnd.view];
[self presentModalViewController:self.picker animated:NO];
}
/// And here manage your picker delegate methods
SecondController
// Here manage your IBActions and whatever you want for the overlay

Update UI from another Class Method - Cocoa

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.)

Presenting a view controller modally - iPad

I found this code to display a modal view:
- (void)add:(id)sender {
// Create the root view controller for the navigation controller
// The new view controller configures a Cancel and Done button for the
// navigation bar.
RecipeAddViewController *addController = [[RecipeAddViewController alloc]
initWithNibName:#"RecipeAddView" bundle:nil];
addController.delegate = self;
// Create the navigation controller and present it modally.
UINavigationController *navigationController = [[UINavigationController alloc]
initWithRootViewController:addController];
[self presentModalViewController:navigationController animated:YES];
// The navigation controller is now owned by the current view controller
// and the root view controller is owned by the navigation controller,
// so both objects should be released to prevent over-retention.
[navigationController release];
[addController release];
}
My question is how do I implement this code (I'm going to place it in a buttonPress method)
Do I need to define anything in my header file? The bit that confuses me is that apple on provides this and no header file so i cant tell if anything should be there?
The code refers to RecipieAddViewController what do I repleace this with, "UIViewController" ?
What do I put as the delegate in the headerfile ? do I need to set this up anywhere else ? like with a property?
Is there anything else I need to do once I have copid this code in my buttonPress method to make it work?
Thanks and sorry for all the questions.
My question is how do I implement this code (I'm going to place it in a buttonPress method)
Define the method as an IBAction like -(IBAction)add:(id)sender and in interface builder bind a button's touch up inside event to the view controller object's add: action outlet.
Do I need to define anything in my header file? The bit that confuses me is that apple on provides this and no header file so i cant tell if anything should be there?
Nope. All this stuff needs is UIKit.h You usually need to change your header to add methods, add instance variables, or include custom classes. You may need a #import RecipeAddViewController.h somewhere (in your header or your implementation file) in order to use that class, however. This is true for any custom class you write that you want to use in another file.
The code refers to RecipieAddViewController what do I repleace this with, "UIViewController"?
Replace that with the view controller class you want to push. UIViewController itself is rarely useful naked. It's made to subclassed. So you create a new class that inherits from UIViewController, import it's header, create and instance of it, and push it on the navigation controller.

Pointer to NSWindow Xib after loading it?

In my code below, CustomWindow is a subclass of NSWindow.
CustomWindow *window = [[CustomWindow alloc] init];
if (![NSBundle loadNibNamed:#"NibName" owner:window])
[window center]; // doesn't work
How do you get a pointer to control your XIB after you load it so you can do things such as centering the NSWindow (I mean the serialised one that resides inside the XIB)?
What am i doing wrong here?
You should be using an NSWindowController subclass. NSWindowController is specifically designed to do exactly what you want to achieve and solves several problems that you will run into if you load the nib directly using the methods of NSBundle. You generally should always use an NSWindowController subclass to manage windows.
Create a subclass of NSWindowController:
#interface MyWindowController : NSWindowController {}
#end
#implementation MyWindowController
- (id)init
{
self = [super initWithWindowNibName:#"MyWindow"];
if(self)
{
//initialize stuff
}
return self;
}
//this is a simple override of -showWindow: to ensure the window is always centered
-(IBAction)showWindow:(id)sender
{
[super showWindow:sender];
[[self window] center];
}
#end
In Interface Builder, set the class of File's Owner to be MyWindowController and connect the window outlet of File's Owner to the window object in your nib.
You can then display the window by doing this:
MyWindowController* controller = [[MyWindowController alloc] init];
[controller showWindow:self];
In my code below, CustomWindow is a subclass of NSWindow.
CustomWindow *window = [[CustomWindow alloc] init];
if (![NSBundle loadNibNamed:#"NibName" owner:window])
[window center]; // doesn't work
How do you get a pointer to control your XIB after you load it so you can do things such as centering the NSWindow inside the XIB?
“centering the NSWindow inside the XIB” makes no sense (you would center it on the screen), unless you mean centering the NSWindow object that is inside the xib, in which case, why are you creating another NSWindow (CustomWindow) object outside of the xib?
Remember that a nib (or xib) is an archive of objects. If you want to use a window that you have in your nib, you need to create an outlet to point to that window, set the class of the File's Owner to be the class where you've added the outlet, hook up the outlet in IB, and appoint the object with the outlet as the File's Owner by passing it to the owner: argument. That object, as the owner, will then be responsible for working with the window. It may be (usually is, in my code) the same object that loads the nib.
Also, init doesn't work on NSWindow; you must use initWithContentRect:styleMask:backing:defer: or initWithContentRect:styleMask:backing:defer:screen:. Using init would only be valid if you've implemented init yourself in CustomWindow, and used one of those two selectors for the [super init…] message.
You probably don't want to make your window the File's Owner. Normally you would pass self or some controller object there. Then if self or that controller object has a CustomWindow IBOutlet, it will get hooked up when you call loadNibNamed:. Check out this post for example code.
A XIB is a container for objects, it's not equal to a window. You can't center a XIB, you can only center a window contained in a XIB.
Also, the objects in the XIB are created when you load it. You don't pass an object as owner that then stands in for one of the objects in the XIB, you instead use IBOutlets to get references to the new objects created when loading the XIB and then you can interact with them.
The File's Owner object is a special object in XIBs, as it's the only object that is not created and that you can specify by passing it to loadNibNamed:owner:. It's your gateway between the XIB-created objects and your application.
Usually, the owner object is some kind of controller class. Set the File's Owner's class in Interface Builder to your controller class, then define some IBOutlets in the class, they will show up in Interface Builder on the File's Owner and you can connect your objects in the XIB to them.
Finally, when you pass your controller object to loadNibNamed:owner:, Cocoa will connect your IBOutlets to the newly created objects and you can use them to interact with them, e.g. to center a window in your XIB.