I have a question about using NSViewController and switching between views. I have a Cocoa application where I have a window. The idea with the window is that it will display a number of views one by one where each view is stored in a separate XIB file. Each view has a corresponding NSViewController. I have made a minimal example of what I'm doing where only the first view is loaded.
#interface MyWindowController : NSWindowController {
NSViewController *currentViewController;
}
#property (assign) IBOutlet NSView *targetView;
#end
#implementation MyWindowController
#synthesize targetView;
- (id)init
{
return [super initWithWindowNibName:#"MyWindow"];
}
- (void)dealloc
{
[currentViewController release];
[super dealloc];
}
- (void)windowDidLoad
{
[super windowDidLoad];
currentViewController = [[NSViewController alloc] initWithNibName:#"FirstView" bundle:nil];
[self.targetView addSubview:currentViewController.view];
[currentViewController.view setFrame:targetView.bounds];
}
#end
When the window is loaded the view from FirstView.xib is also loaded and the view is displayed in the window. In this case the loaded view only has a text field and I would like the text field to be highlighted so that input can be written to it directly without the user having to click on it but I can't figure out how to do that. Is it possible to have the text field selected when the view is loaded?
After reading the documentation I have found that I probably want to set the window's initialFirstResponder to the text field, but I can't find how to do that when the text field is in a different XIB file than the window.
Whenever you add/replace a subview and want it to be the first responder, have your window controller make the view the first responder of the window managed by the window controller:
[[self window] makeFirstResponder:currentViewController.view];
You’ll want to do this in both -windowDidLoad and whichever other methods add/replace subviews.
Related
I have a NSViewController and a corresponding .xib file.
In the nib file, there's a NSPanel.
I link the NSPanel to the ViewController:
#property (strong) IBOutlet NSPanel *panel;
However, when I wanted to pass panel to my AppDelegate and show on screen, my code didn't work well.
This is part of my code:
//MainViewController.m
-(NSPanel *)passPanel{
if(!self.panel){
NSLog(#"panel is nil");
self.panel = [[NSPanel alloc] init]; //breakpoint
return self.panel;
}else{
NSLog(#"panel is not nil");
return self.panel; //breakpoint
}
}
//AppDelegate.m
MainViewController* vc = [[MainViewController alloc] init];
self.window = [vc passPanel];
I think when I initiate vc, the panel should be initiate as what it's like in the nib file because I've set it as a property.
But why it is always nil?
I've also set breakpoints to debug, I found it not working as I wanted.
In order not to make the question seem confusing, I paste all my code in MainViewController.m below:
#import "MainViewController.h"
#interface MainViewController ()
#end
#implementation MainViewController
#synthesize panel;
- (void)viewDidLoad {
[super viewDidLoad];
// Do view setup here.
}
-(NSPanel*)passPanel{
NSLog(#"passing panel to sender...");
if(!self.panel){
NSLog(#"panel is nil");
self.panel = [[NSPanel alloc] init];
return self.panel;
}else{
NSLog(#"panel is not nil");
return self.panel;
}
}
#end
EDIT
As the code doesn't show clear about the question, I put some screenshots below.
MainMenu.xib
MainViewController.xib
The MainMenu.xib is the default nib file created when the project is created. In this nib file I have a window to show other views. In the MainViewController.xib, I have a NSPanel which is a subclass of NSWindow. Actually I'm not sure it's proper to use a ViewController(MainViewController) to control a window. Now I want to show this NSPanel object. So I assign this panel to my window in MainMenu.xib. It did work(although I felt it's not a good way).
My question is, I think after the window is assigned a NSPanel, the panel would automatically load its view. However, it didn't.
In the passPanel method, it turned out that self.view is nil. So what appear on screen is a blank window.
What I want it to show:
but it shows
I have a MyCustomView subclassed from NSView designed in a .xib.
I would like to insert this view into some of my other xib's round my application. How should I do this? If i drag a custom view and change the class to MyCustomView, but that does not load my xib-file. Can this only be done programmatically or is there a way to do this inside interface builder?
EDIT1:
Here is a very small demo-project:
http://s000.tinyupload.com/index.php?file_id=09538344018446482999
It contains the default MainMenu xib and my CustomView xib. I would like my CustomView.xib to be displayed inside the custom view added to my MainMenu.xib -- using as less code as possible.
For loading the view you need to add on your window:-
Created custom class of view inheriting to NSViewController
#import <Cocoa/Cocoa.h>
#interface NewViewController : NSViewController
#end
#import "NewViewController.h"
#implementation NewViewController
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Initialization code here.
}
return self;
}
#end
Your xib name is yourview.xib
- (void)windowDidLoad {
NSViewController *yourVC = [[NewViewController alloc] initWithNibName:#"NewViewController" bundle:nil];
[[[self window] contentView] addSubview:[yourVC view]];
}
Sounds like you need a container view. But I think you will have to use storyboard for it to be doable in interface builder.
Use a view controller as it will handle nib loading for you and provide a place to hook up IBOutlet and IBActions in a reusable way.
In your app delegate or whatever controller create an instance of your view controller.
Ask your view controller to load its view.
Cast the return type to your view class name.
Then keep a reference to your view controller and possibly the view.
Tell whatever view to add your view as a subview.
Add any layout constraints.
( you can build out very generic constraints to add themselves in your view or view controller by overriding viewDidMoveToSuperview or viewDidMoveToWindow when superview or window are not nil. Use the same to remove your constraints. )
Oddly you remove a view by telling it to remove itself from its superview.
I'd advise just doing it programmatically:
Add a View to your main xib/storyboard and set the custom class to your custom view's class
In your xib for your custom view, set the File's Owner class to your custom view's class
Hook up any IBOutlets, etc. as needed
Make a __strong property/ivar for holding a reference to the top level NSView of the xib
Implement initFromFrame in your custom view's class roughly as follows:
#interface CustomView ()
{
__strong NSView *nibView;
}
#end
#implementation CustomView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
NSArray *nibObjects;
[[NSBundle mainBundle] loadNibNamed:#"CustomView" owner:self topLevelObjects:&nibObjects];
nibView = nibObjects[1];
[self addSubview:nibView];
}
return self;
}
The IBOutlet are connected up immediately after the loadNibNamed call, so you can do further initialization from there.
Another option is to do things purely programmatically:
1. In your custom xib, set the root View's class to your custom class
2. Implement awakeFromNib in your custom class to perform initialization
3. Call loadNibNamed: on your custom xib and programmatically add it to the user interface without interface builder.
I have MainMenu.Xib which has a status menu element. The MainMenu File Owner is mapped to AppDelegate.
I also have another Xib which is a Window and it's File Owner is mapped to a ViewController with the same name.
So what I have tried and it kind of works is I have created an action in the appDelegate and have mapped the menu item in the status menu to the action in the appDelete using the First Responder.
In the action I have put:
SubscriptionsViewController *vc = [[SubscriptionsViewController alloc] initWithNibName:#"Subscriptions" bundle:nil];
[vc view];
If I step through the code it the window shows up but then goes away. So I have two questions
1) I thought there was a way to load the xib with NSMenuItem without the need of the above code.
2) How do I keep the window from closing right away? Do I need to save the view pointer or something?
edit: format code.
1) I thought there was a way to load the xib with NSMenuItem without the need of the above code.
Since NSWindow is not inherited from NSView like in iOS (UIWindow:UIView), it makes no sense to use NSViewController to load window from a xib. Use subclass of NSObject instead.
#interface SubscriptionsViewController : NSObject
#property (assign, nonatomic) IBOutlet NSWindow *window;
#end
#implementation SubscriptionsViewController
- (id)init
{
self = [super init];
if (self) {
[NSBundle loadNibNamed:#"Subscriptions" owner:self];
}
return self;
}
#end
2) How do I keep the window from closing right away? Do I need to save the view pointer or something?
It depends on the context to retain the instance of subscriptionsViewController or not. You can use below code to display a window, where the instance of window is in nib -
self.subscriptionsViewController = [[SubscriptionsViewController alloc] init];
[self.subscriptionsViewController.window makeKeyAndOrderFront:self];
Remember if "Visible At Launch" is set in nib, then the window is visible when you instantiate subscriptionsViewController.
You may add your window into the MainMenu.xib instead of using an addition xib file and create an outlet in the AppDelegare.h as
#property (assign) IBOutlet NSWindow *window;
Then all you need to do is
window.isVisible = !window.isVisible;
in the necessary action method...
I have one base view controller "contentViewController" with one button
the action on button is
(IBAction) goBack:(id)sender
.h file
#interface ContentView : UIViewController {
}
#property(nonatomic,retain) UIViewController *display;
-(IBAction) goBack:(id) sender;
.m file
#synthesize display;
-(IBAction) goBack:(id)sender{
UIViewController *view= display;
[display release];
[self presentModalViewController:view animated:YES];
}
and there are some other view controllers already exist each view controller contain on button to show content on the contentViewController.. here is one class example:
.h file
#interface Info : UIViewController {
}
-(IBAction) viewHealthInfoContent:(id) sender;
.m file
-(IBAction) viewHealthInfoContent:(id)sender{
ContentView *cv=[ContentView alloc];
[cv setDisplay:self];
[self presentModalViewController:cv animated:YES];
[cv release];
}
the case is, each time i show content from one view controller i need to go back to it. using that one goBack button on the contentViewController but when i click the go back button it doesn't do any think !!! any help
When you want to go back your previous viewController, use dismissModalViewControllerAnimated:: method.
According source :
Dismisses the modal view controller that was presented by the receiver.
In iOS, you can display views modally by presenting the controller for the modal view from your current view controller. When you present a view modally using the presentModalViewController:animated: method, the view controller animates the appearance of the view using the technique you specify. (You can specify the desired technique by setting the modalTransitionStyle property.) At the same time, the method creates a parent-child relationship between the current view controller and the modal view controller.
Source
Hey guys I'm a newbie developer with cocoa and I'm Trying to create a simple app. which display 4 differents pages that you can select via a tab bar.
My Problem: I made a UIButton on the First/Home page (==>FirstView.xib) with IB and i tried to link it to my second page (==>SecondView.xib) with some code found on the net.
The thing is that i can build my code but nothing happens when I try to click on my button, can you help me ?
Code for FirstView.h:
#import <UIKit/UIKit.h>
#interface FirstViewController : UIViewController {
UIButton *button;}
#property (nonatomic, retain) IBOutlet UIButton *button;
- (IBAction)goToViewTwo;
#end
Code for FirstView.m:
#implementation FirstViewController
#synthesize button;
- (IBAction)goToViewTwo {
SecondViewController *SecondView= [[SecondViewController alloc] initWithNibName: #"SecondView" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:SecondView animated:YES];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)dealloc {
[super dealloc];
[button release];
}
#end
About the outlets in FIrstVIew.xib:
I linked "Touch Up Inside" to "goToViewTwo" and "b^utton" to "File's Owner'
I'm under the impression this is in a tab bar where each tab bar button displays a .xib file. If this is the case, the solution is fairly simple. Your tabBarController manages your views in an array of view controllers. To switch between them, your IBAction method should be the following:
- (IBAction)goToViewTwo {
self.tabBarController.selectedIndex = 1;
}
This tells your tab bar controller to switch from your current view controller (at index 0) to the second (at index 1)