Using toolbar with several NSViewControllers - objective-c

I've a single window Document-based application that loads several NSViewControllers that serve as the main view. Each view controller is a self-contained class that deals with certain business needs and as such implements all methods necessary to work.
My problem is how to make the toolbar work when a specific view controller is loaded. I've no problems wiring the toolbar to a delegate and the toolbar items to actions as long as these are implemented in the window class. But if I try to set a new action for a toolbar item on the loadView method of a view, the toolbar item simply ignores it and continues calling the method on the main window class.
So, how can I dynamically change the action on a toolbar item?

Are you setting the target as well? (ie [foo setTarget:] in addition to [foo setAction:])
Presumably your window controller has some sort of currentViewController ivar/property? It might be better not to reassign the toolbar actions when switching view controllers, and instead use the window controller to pass them on the currently active view controller.

Related

Access windowController's button From subView

In my cocoa OS X application, I have a WindowController with a xib file, and Two ViewControllers with xib files, I have added a Custom View in the WindowController, Where I am swapping those two Sub Views by removing and adding those Views when clicked in continue or next button.
[[theViewController view] removeFromSuperview];
self.theViewController = [[WelcomeInstallViewController alloc] initWithNibName:newView bundle:nil];
[innerInstallerView addSubview:[theViewController view]];
[[theViewController view] setFrame:[innerInstallerView bounds]];
Now In one of those views i have a button which needs to disable the continue button in the WindowController.I have looked into NSNotificationCenter, this is my first mac,cocoa, objective c app. should i use NSNotificationCenter? i am confused, and didn't understand properly.
There are many ways to skin the cat..
The simplest approach would consist of adding an outlet to your NSWindowController and link the button to that outlet in Interface Builder, then handle the button enablement on whatever conditions you require.
Notifications are one good way of loosely coupling application components, e.g. in case the window controller doesn't initiate the state change that would trigger the button to disable/enable itself.
Other possibilities include NSUserInterfaceValidations, a dedicated mechanism (protocol) in Cocoa to..
allow the target of a user interface element such as a menu item or a toolbar item to decide whether or not the user interface element should be enabled.
Given a similar design requirement (multiple loadable XIBs), I have used the NSViewController paradigm to allow logic to be attached to the sub-views that I load into the main view. In this case, I would create an NSViewController subclass which has a bool property (let's call it canContinue), which I would bind from the main view's button to owner's subview.canContinue.
If you do this, the main view will have to load the view controller (which will take care of loading the XIB) when you bring in each of the individual subviews, and then make sure to assign the subview property in the owner to point to the NSViewController that you load.

NSNotification or Delegate to register for when the visible view changes

I'm developing a project in objective-c for ios, and I have a view with multiple tabs using a subclass of UITabBarController. Each tab has it's own UINavigationController. When a view loads on a tab, the appropriate activation events fire (viewWillAppear, viewDidLoad, etc.). However, once you tap on a different tab, and tap back, not all these events will fire again since the view is already the visible view for that specific tab (viewDidLoad for example).
My question is this: is there a notification or delegate that I can simply register for and get notified when the visible view in the window changes? I've done some research and I didn't find anything specific for this. What I plan on doing is:
Check the visible view when the tab bar index changes: tabBarController:didSelectViewController
Register for this event on each navigation controller: navigationController:didShowViewController:animated:
By doing this, I should be notified whenever the visibleViewController changes by either changing the tab, or navigating within the tab's navigation flow (except for modals, in this case, I don't care about them. They are handled already).
Is this the right approach?
Have you looked at UITabBarControllerDelegate? This method sounds what you are looking for:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
From the documentation:
In iOS v3.0 and later, the tab bar controller calls this method regardless
of whether the selected view controller changed. In addition, it is called only
in response to user taps in the tab bar and is not called when your code
changes the tab bar contents programmatically.
Here's the link: http://developer.apple.com/library/ios/#documentation/uikit/reference/UITabBarControllerDelegate_Protocol/Reference/Reference.html
Hope that helps!
First implement the UITabBarController delegate method "tabBarController:didSelectViewController" and register for it in the app delegate. You can't register for it in each navigation controller. Only one object can be the delegate. In that method, typecast it to a UINavigationController.
Then get the UIViewController by calling "topViewController" on that UINavigationController. Then call the viewWillAppear: method directly on it.

Best alternative to overlapping subviews

I have a UIView containing a login form, however when the user is already logged in, I want to show a logout button instead of the form.
My current approach is creating a subview for both the login form and the login button, overlapping each other in Interface Builder.
I would then only show one of the subviews at a time.
Is there a better way of doing this so it is easier to design in Interface Builder, while still using the same UIViewController?
If it matters the view is a modal view.
You could tweak your UIViewController to actually be a UITabBarController, but tweak the tab bar so that it's not visible when the controller is pushed to the screen:
self.hidesBottomBarWhenPushed = YES;
This should allow you to:
Programmatically switch between views easily
Manage two (or more) separate UIViewController instances in IB easily, without overlapping
For optional / modal parts of a view controller, one approach I've used several times in the past is to create a view for each section as top level objects in the nib. You can arrange each view using interface builder more easily then, and then all you need to do in your code is conditionally add the appropriate view to the main view in your viewDidLoad method. Remember that, as top-level objects in the nib, they should have strong outlets, not weak outlets.

Interplay between UINavigationController’s addChildViewController and topViewController

I have something like a modal view controller that I need to display above my other view controllers. I’m not using the regular modal controller feature (presentViewController: and friends), since I need better control over the process. Instead I am using the view controller containment feature (the addChildViewController: method group).
The containment feature makes the code fairly straightforward. When I need to present the “modal” view controller, I add it as a child to the view controller hierarchy and everything works as expected. One small catch is that the regular view controllers are wrapped in a navigation controller. Therefore I have to add the modal controller as a child of the navigation controller, otherwise it would be covered by the navigation bar and toolbar.
Now the problem is that calling addChildViewController: on a navigation controller also sets the new controller as the topViewController, as if the controller was pushed using the regular pushViewController: method. This means that while the modal controller is displayed, the regular controller underneath it does not receive the appearance and rotation callbacks.
This feels like a bug, or am I missing something?
I had the same problem. I resolved this by writing my own custom view controller, containing a UINavigationController (added via addChildViewController:) and then exposing the UINavigationController as a readonly property. Then you can add your modal view controller as a child of your new custom view controller instead of as a child of the UINavigationController
I missed this sentence in the documentation for addChildViewController:
This method is only intended to be called by an implementation of a
custom container view controller.
So I guess it’s my fault and this scenario is simply not supported. Which sucks, because it’s very convenient to design whatever modal things as regular view controllers and connect them to the hierarchy like proper first-class citizens. I would probably have to rewrite the navigation controller on my own to have built-in support for this.

Access NSWindow's Element via Child NSView/ViewController

Is it possible to access a NSWindowController's element from a child NSViewController?
Essentially I have a NSProgressIndicator that spins on the bottom corner of the NSWindow. This works because my WebView is in my NSWindowController instead of my NSViewController.
I want to break the logic apart now but I'm having trouble understanding how I'd access these elements from my View Controller.
Thanks!
You can't connect an outlet from one NIB to an object in another and the desire to do so indicates a problem with your design. If the view is so intimately connected to other things in the window, maybe it shouldn't be separated out into a different NIB.
A view should only go into a separate NIB when it makes sense as a self-contained unit. It should represent and manipulate its controller's representedObject and not much more. The controller might have a delegate that it informs about what's being done and asks to make customizing decisions.
Maybe you can continue to use a separate NIB if you adopt that sort of design. Perhaps the window will have a reference to some model object. It would configure the view controller to use that model object as its represented object. And perhaps the progress indicator would be bound to that same model object. Then, as the view manipulates its represented object, it would indirectly also affect the progress indicator.
Another option would be for the window controller to set itself as the delegate for the view controller and your view controller could invoke it at appropriate points to inform it of things going on in the view. Then the window controller could do whatever was appropriate to the progress indicator or other stuff in the window outside of the view. This hypothetical delegate is something you would have to add to the view controller class and you'd design its protocol.