Cocoa : Load NSViewController with nib - cocoa

I have tried many possibilities I found on this site and read some explanations
from apple developer page, but seems like i couldn't resolve my problem about
loading NSViewController with/form NIB.
Files on Xcode Project look a bit like this :
SecondViewController.h
SecondViewController.m
SecondViewController.xib
AppDelegate.h
AppDelegate.m
MainMenu.xib
The main problem is how could i create the SecondViewController programatically with
the initial nib on SecondViewController.xib
Custom class of FileOwner on MainMenu.xib is NSApplication
Custom class of FileOwner on SecondViewController.xib is SecondViewController
There are some panels and window in MainMenu.xb (about window and preference panel)
This application has no main window (using notification icon on status bar)
SecondViewController.h
#interface SecondViewController : NSViewController {
BOOL fullScreenMode;
NSImageView *fullScreenbg;
NSImageView *imageView1;
NSImageView *imageView2;
NSPanel *imageWindow;
}
#property (assign) IBOutlet NSImageView *fullScreenbg;
#property (assign) IBOutlet NSImageView *imageView1;
#property (assign) IBOutlet NSImageView *imageView2;
#property (assign) IBOutlet NSPanel *imageWindow;
SecondViewController.m
#implementation SecondViewController {
NSImageView *nv1;
NSImageView *nv2;
NSSize curImgSize;
}
#synthesize fullScreenbg;
#synthesize imageView1;
#synthesize imageView2;
#synthesize imageWindow;
......
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Initialization code here.
fullScreenMode = YES;
}
return self;
}
AppDelegate.h
#interface AppDelegate : NSObject <NSApplicationDelegate,NSWindowDelegate> {
NSPanel *aboutWindow;
IBOutlet NSMenu *myStatusMenu;
IBOutlet NSMenuItem *toggleFullScreen;
}
AppDelegate.m
#implementation AppDelegate {
SecondViewController *controller;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
controller = [[SecondViewController alloc]
initWithNibName:#"SecondViewController"
bundle:nil];
//Not work (fullscreenbg, imageView1, imageView2,imageWindow = nil)
//controller = [[SecondViewController alloc] init]; ?? <-- didn't work either
}
Even if using initWithNibName or just init, all the IBOutlet properties seems to be nil
on debug.
i've tried other solustions like "NSBundle loadNibNamed" or using loadView but it didn't work (warning message : "NSObject my not respond to loadView").
The main purpose of the secondViewController is to display notification message including
graphics and web element.
I hope someone could give me a best suggestion. Thanks.

This is normal behavior. IBOutlets are not connected in the constructor.
You can override viewDidLoad, call super and then do any initialization.

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
//added this line :
if (nibBundleOrNil!=nil || ![nibBundleOrNil isEqualtoString:#""]) {
[NSBundle loadNibNamed:#"SecondViewController" owner:self];
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Initialization code here.
fullScreenMode = YES;
}
return self;
}

Related

NSButton does not respond to MouseDown Events if it is a part of custom NSView

I have an OS X application with a main view (i.e., self.view) that contains an NSScrollView and the NSScrollView contains a custom NSView. The custom NSView (flippedNSView) is just an NSView custom class FlippedNSView with a -(BOOL)isFlipped { return YES; }
I have a function that creates a NSButton that is called in the ViewDidLoad.
If button is added to self.view, button works fine
If button is added to flippedNSView, button does not respond.
NSTextViews work fine and respond to MouseDown events, although they do not respond to text selection. Only the buttons do not respond.
Any ideas?
AppDelegate.h
#import "CodeObjViewController.h"
#interface AppDelegate : NSObject <NSApplicationDelegate> {
FlippedView *flippedNSView;
}
// Windows are defined in MainMenu.xib with the following hierarchy
// Window
// |_ View
// |_ flippedNSView
// |_ scrollView
// |_ myCustomView
#property (assign) IBOutlet NSWindow *window;
#property (strong) IBOutlet NSViewController *myViewController;
#property (weak) IBOutlet NSView *myCustomView;
#property(readwrite, strong, nonatomic) IBOutlet NSScrollView* scrollView;
#property(readwrite, strong, nonatomic) IBOutlet FlippedView * flippedNSView;
#end
AppDelegate.m
#import "AppDelegate.h"
#interface AppDelegate ()
#end
#implementation AppDelegate
#synthesize scrollView;
#synthesize flippedNSView;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
_myViewController = [[CodeObjViewController alloc] initWithNibName:#"customViewController" bundle:nil];
[_myCustomView addSubview:[_myViewController view]];
[[_myViewController view] setFrame:[_myCustomView bounds]];
[scrollView setDocumentView: flippedNSView];
}
customViewController.h
#interface FlippedView : NSView
#end
//----------------------------------------------------------
#interface CodeObjViewController : NSViewController {
FlippedView *flippedNSView;
NSScrollView *scrollView;
}
customViewController.m
#implementation FlippedView
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
return self;
}
//----------------------------------------------------------
- (BOOL)isFlipped {
return YES;
}
#end
//----------------------------------------------------------
#implementation customViewController
// Custom View Controller
-(void) viewDidLoad {
// The scroll and flipped views are defined on menu.xib and declared on the AppDelegate.
AppDelegate *theAppDelegate = (AppDelegate*) [NSApplication sharedApplication].delegate;
flippedNSView = theAppDelegate.flippedNSView;
scrollView = theAppDelegate.scrollView;
NSButton *button = [[NSButton alloc] initWithFrame:NSMakeRect(0,0,10,10)];
[self.view addSubview:helpButton]; // THIS RESPONDS TO MOUSE DOWN
[flippedNSView addSubview:helpButton]; // THIS DOES NOT RESPOND TO MOUSE DOWN
[button setAction:#selector(buttonClicked:)];
[button setTarget:self];
}
-(void) buttonClicked : (id) sender {
NSLog(#"Button clicked");
}

Setting dataSource for NSTableView across scenes in a storyboard

I am using XCode 6 on OS X 10.10 and have a storyboard containing a window with a split view controller, as shown in the following image.
The split view controller (highlighted in the image) is an instance of MyViewController, which has the following code:
MyViewController.h
#import <Cocoa/Cocoa.h>
#interface MyViewController : NSSplitViewController <NSTableViewDataSource>
#end
MyViewController.m
#import "MyViewController.h"
#implementation MyViewController
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return 7;
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
return [NSString stringWithFormat:#"%ld", (long)row];
}
#end
I would like to make the view controller the dataSource of the NSTableView in my storyboard, however I am unable to connect them. Is there a reason for this?
In your NSSplitViewController-subclass viewDidLoad-method set the data source programmatically. You need to implement the child view controller class as well (with the tableView outlet connected to the control).
MySplitViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
for (NSSplitViewItem *item in self.splitViewItems)
{
NSViewController *controller = item.viewController;
if ([controller isKindOfClass:[MyChildController class]])
{
MyChildController *myController = (MyChildController *)controller;
myController.tableView.dataSource = self;
[myController.tableView reloadData];
}
}
}
But to tell you the truth I don't like this approach. It's more better when the table view's data source methods are in the native view controller class.
Another way to do that. MyChildController.h file:
#class MyChildViewController;
#protocol MyChildControllerDelegate <NSObject>
- (void)childController:(MyChildViewController *)controller didSelectRowAtIndex:(NSUInteger)index;
#end
#interface MyChildViewController : NSViewController <NSTableViewDataSource, NSTableViewDelegate>
#property (nonatomic, weak) id<MyChildControllerDelegate> delegate;
#property (nonatomic, retain) NSArray *items;
#property (nonatomic, weak) IBOutlet NSTableView *tableView;
#end
Don't forget to implement all the table view dataSource and delegate methods you need. MySplitViewController.m file:
- (void)viewDidLoad
{
[super viewDidLoad];
for (NSSplitViewItem *item in self.splitViewItems)
{
NSViewController *controller = item.viewController;
if ([controller isKindOfClass:[MyChildController class]])
{
MyChildController *myController = (MyChildController *)controller;
myController.delegate = self;
[myController setItems:_items];
}
}
}

NSViewController mouseDown: not called

I have a NSWindowController where I'm adding my corresponding ViewControllers. I would like to handle touch events in one of the ViewController. My class is
Window.h
#interface WindowController : NSWindowController<NSToolbarDelegate>
#property (nonatomic, strong) NSViewController *currentViewController;
#property (assign) IBOutlet NSView *targetView;
#end
Window.m
-(void)addViewController
{
NSViewController *currentController = [[currentControllerClass alloc]initWithNibName:controllerIdentifier bundle:nil];
self.currentViewController = currentController;
[self.targetView addSubview:self.currentViewController.view];
[self.currentViewController.view setFrame: [self.targetView bounds]];
}
-(void) awakeFromNib{
[super awakeFromNib];
[self.window setIgnoresMouseEvents:NO];
[self setToolbarToPracticeView];
}
-(void)mouseDown:(NSEvent *)theEvent
{
NSLog(#"Window > Mouse down");
}
I'm able to print the mousedown event here. Now I have a viewController, which is
ViewController.h
#interface ViewController : NSViewController
#end
ViewController.m
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Initialization code here.
}
return self;
}
-(void)awakeFromNib
{
[self.view setAcceptsTouchEvents:YES];
}
-(BOOL)acceptsFirstResponder
{
return YES;
}
-(void)mouseDown:(NSEvent *)theEvent
{
NSLog(#"ViewController > mouse down");
}
I would like to get the control of the mouseDown in the ViewController. Am I missing something here?
Thanks.
Create a Custom NSView and then delegate a mouseDown method in it's protocol. Import and use the customView in the ViewController and delegate its methods. When you click on the view a mouseDown event triggers.
Not sure of my answer, but if you don't call [super mouseDown:theEvent] in the Window.m (which should be called WindowController.m by the way), you are breaking the responder chain. Hence your view controller can't see it. Am I right?

Mac OS X Cocoa multiview application navigation

I've already spent 2 full days trying to figure out how to use NSViewControllers in order to create a multiview application.
Here is what I do.
I have 2 View Controllers and the MainMenu.xib's Window.
I also have an AppController that is the delegate for both View Controllers.
When I launch the app, I'm first greeted with the MainMenu.xib's Window's view which holds a button. On clicking this button, an IBAction is sent to the appController and asks for the SecondViewController to display it's nib. So far, everything's fine and the nib file is displayed correctly.
On the secondViewController, there's another button that sends another IBAction to the appController and asks for the FirstViewController to be displayed but nothing happens,
no crash, no warning... Any help would be much appreciated...
Thanks in advance for your patience...
Here is the code for the AppController.h :
#import <Foundation/Foundation.h>
#import "SecondViewController.h"
#import "FirstViewController.h"
#interface AppController : NSObject
#property (strong) IBOutlet NSWindow *mainWindow;
#property (strong) IBOutlet SecondViewController *secondViewController;
#property (strong) IBOutlet FirstViewController *firstViewController;
- (IBAction)secondButtonfromsecondViewControllerClicked:(id)sender;
- (IBAction)buttonClicked:(id)sender;
#end
and here is the code for the AppController.m :
#import "AppController.h"
#implementation AppController
#synthesize mainWindow = mainwindow;
#synthesize secondViewController;
#synthesize firstViewController;
- (IBAction)buttonClicked:(id)sender {
NSLog(#"button from second View Controller clicked");
self.secondViewController = [[SecondViewController
alloc]initWithNibName:#"SecondViewController" bundle:nil];
self.mainWindow.contentView = self.secondViewController.view;
[self.secondViewController.view setAutoresizingMask:NSViewWidthSizable |
NSViewHeightSizable];
}
- (IBAction)secondButtonfromsecondViewControllerClicked:(id)sender {
NSLog(#"button from first ViewController clicked");
self.firstViewController = [[FirstViewController
alloc]initWithNibName:#"FirstViewController" bundle:nil];
self.mainWindow.contentView = [self.firstViewController view];
}
#end
Well, anyone can help me, I just need a single view application that displays a first ViewController with a button on the first viewController that takes me to a second view controller with a second button that takes me back to my first viewcontroller... I've already spent more than a week on that... in vain... PS : I don't want any button on the mainMenu.xib window nor tabs.
here is the solution to my question then.
Here's the code for the AppDelegate.h:
// AppDelegate.h
#import <Cocoa/Cocoa.h>
#import "FirstViewController.h"
#import "SecondViewController.h"
//We need to declare the AppDelegate class as being the delegate for both
//FirstViewController and SecondViewController
#interface AppDelegate : NSObject <NSApplicationDelegate,
FirstViewControllerDelegate, SecondViewControllerDelegate>
#property (strong, nonatomic) NSWindow *window;
#property (strong) FirstViewController *firstViewController;
#property (strong) SecondViewController *secondViewController;
-(void) goToSecondView;
-(void) goToFirstView;
#end
Now, here's the AppDelegate.m:
// AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize window = _window;
#synthesize firstViewController;
#synthesize secondViewController;
-(void) awakeFromNib {
[self goToFirstView];
self.firstViewController.delegate = self;
}
-(void) goToSecondView {
if (self.secondViewController ==nil) {
self.secondViewController =[[SecondViewController alloc]
initWithNibName:#"SecondViewController" bundle:nil];
}
self.window.contentView = [self.secondViewController view];
}
-(void) goToFirstView {
if (self.firstViewController ==nil) {
self.firstViewController =[[FirstViewController alloc]
initWithNibName:#"FirstViewController" bundle:nil];
}
self.window.contentView = [self.firstViewController view];
}
#end
Next we need to set delegates in the FirstViewController and the SecondViewController
// FirstViewController.h
#import <Cocoa/Cocoa.h>
#import "SecondViewController.h"
//We declare the delegation protocole:
#protocol FirstViewControllerDelegate <NSObject>
-(void)goToSecondView;
#end
#interface FirstViewController : NSViewController
- (IBAction)firstViewControllerButtonClicked:(id)sender;
#property (nonatomic, strong) id <FirstViewControllerDelegate> delegate;
#end
And here is the FirstViewController.m:
// FirstViewController.m
#import "FirstViewController.h"
#implementation FirstViewController
#synthesize delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.delegate = [NSApp delegate];
}
return self;
}
- (IBAction)firstViewControllerButtonClicked:(id)sender {
NSLog(#"button from first View Controller clicked");
if ([self.delegate respondsToSelector:#selector(goToSecondView)]) {
[self.delegate goToSecondView];
}
}
#end
Now, same thing for the SecondViewController:
// SecondViewController.h
#import <Cocoa/Cocoa.h>
#protocol SecondViewControllerDelegate <NSObject>
-(void)goToFirstView;
#end
#interface SecondViewController : NSViewController
#property (nonatomic, strong) id <SecondViewControllerDelegate> delegate;
- (IBAction)goToFirstViewControllerButtonClicked:(id)sender;
#end
And here's the SecondViewController.m:
// SecondViewController.m
#import "SecondViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.delegate = [NSApp delegate];
}
return self;
}
- (IBAction)goToFirstViewControllerButtonClicked:(id)sender {
NSLog(#"button from Second View Controller clicked");
if ([self.delegate respondsToSelector:#selector(goToFirstView)]) {
[self.delegate goToFirstView];
}
}
#end
Well, I guess this code may be improved and if you have any suggestion, feel free to let me know. Hope it will help others.
THE PROBLEM: When the user presses a button in View2, you want View1 to appear. It's not.
STEP 1: You say that the button should be invoking an action on your AppController. Set a breakpoint (or add a diagnostic log) in that action, just to verify that it is, in fact, being invoked.
STEP 2: Think about what you want that action to do, precisely. My guess is that you want to hide View2 and show View1. Perhaps
[view2 setHidden: YES];
[view1 setHidden: NO];
(I'm not using your names here, of course.) Or you might animate the transitions, either cross-fading the views or moving them.
STEP 3: My guess is that STEP 2 will solve your problem. If it doesn't, use the debugger again to verify that view1 and view2 are not null. (If they're null, you probably have weak variables where you need them to be strong.)
STEP 4: In the unlikely event that you're still stuck, check the frames of view1 and view2. Perhaps view1 isn't where you think it is.
STEP 5: If you're still stuck, check the alphaValue of view1. Maybe you set it to be transparent, and it's being drawn transparently in the right place.
STEP 6: I bet there is no step 6!
This isn't much of an answer at the moment, however I have some concerns about your code that I wanted to work through with you.
Are you sure you have connected the outlets and actions in Interface Builder. Please verify this.
You don't need mainWindow as there is already a window property that points to the main window (verify this in Interface Builder). Also this looks wrong:
#synthesize mainWindow = mainwindow;
^
W
So dump that and just use the existing window outlet provided by Xcode.
Don't re-create the view controllers if they already exist:
if (self.secondViewController == nil)
{
self.secondViewController = [[SecondViewController alloc] initWithNibName:#"SecondViewController"
bundle:nil];
}
self.window.contentView = self.secondViewController.view;

Why doesn't my NSView delegate method gets called?

I'm working on my first Mac app and I'm having some trouble with getting my NSView and it's delegate methods to work. I have a NSViewController that should response to mouse events, i.e mouseDown. This didn't work, instead I created a custom NSView subclass that looks like this:
// Delegate method
#protocol CanvasViewDelegate
- (void)mouseDown:(NSEvent *)event;
#end
#interface CanvasView : NSView
{
id delegate;
}
#property (nonatomic, strong) id delegate;
#end
#implementation CanvasView
#synthesize delegate;
- (void)mouseDown:(NSEvent *)event
{
[delegate mouseDown:event];
}
The NSViewController relevant parts that should act as the NSView's delegate looks like this:
#import "CanvasView.h"
#interface PaintViewController : NSViewController <CanvasViewDelegate>
{
CanvasView *canvasView;
}
#property (strong) IBOutlet CanvasView *canvasView;
#end
#synthesize canvasView;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Initialization code here.
canvasView = [[CanvasView alloc] init];
canvasView.delegate = self;
}
return self;
}
- (void)mouseDown:(NSEvent *)event
{
NSLog(#"Down");
}
Now, the method mouseDown doesn't get called, what am I doing wrong?
If you want an NSView subclass to accept events, you will have to implement:
- (BOOL)acceptsFirstResponder {
return YES;
}
as documented here.
Creating the view programmatically instead did work. Like this:
NSRect canvasRect = self.view.frame;
canvasView = [[CanvasView alloc] initWithFrame:canvasRect];
[self.view addSubview:canvasView];
canvasView.delegate = self;

Resources