what I'm basically trying to make is a very simple program that can switch back and forth between 2 views on a single window.
When the program loads there is a window with a custom view that contains a login button. When clicked, the view changes to a second custom view that contains a label and a logout button. I have this much working.
What I can't figure out is how to get the logout button to bring me back to the first view.
Here is the code for the AppDelegate class where i have the button method to switch the view:
header:
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (weak) IBOutlet NSWindow *window;
#property (weak) IBOutlet NSView *loginView;
- (IBAction)loginButtonClicked:(id)sender;
#end
implementation:
#import "AppDelegate.h"
#import "myCustomView.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
- (IBAction)loginButtonClicked:(id)sender {
[_loginView removeFromSuperview];
myCustomView *new = [[myCustomView alloc]initWithNibName:#"myCustomView" bundle:nil];
[[[self window]contentView]addSubview:[new view]];
}
#end
This is the code for my custom class that is a subclass of NSViewController.
header:
#import <Cocoa/Cocoa.h>
#class AppDelegate;
#interface myCustomView : NSViewController
#property (strong) IBOutlet NSView *logoutView;
- (IBAction)logoutButtonClicked:(id)sender;
#end
implementation:
#import "myCustomView.h"
#implementation myCustomView
- (void)viewDidLoad {
[super viewDidLoad];
// Do view setup here.
}
- (IBAction)logoutButtonClicked:(id)sender {
[_logoutView removeFromSuperview];
myCustomView *newController = [[myCustomView alloc]initWithNibName:#"MainMenu" bundle:nil];
[[[self window]contentView]addSubView:[newController view]];
//this does not work. No visible #interface for 'myCustomClass' declares the selector 'window'
}
#end
The button method to go back to the login page is where I'm stuck. Even though I've added the header file for AppDelegate into myCustomClass I cannot use the instance of NSWindow. What am I doing wrong here? Am I at least on the right track? any help here is greatly appreciated.
I also tried using #class instead of #import, but still can't use the instance of NSWindow from AppDelegate.
Here are the pictures of my two xib files:
[][1
UPDATE: The suggestions from Paul Patterson in his comments were very helpful, but haven't solved my problem. For now what I am doing to get my project to work is putting the buttons in the window instead of the views and then hiding them when i don't need them. This works and I can switch back and forth, however I still can't figure out how to use a button on a custom view itself to load a different view onto the same window.
Related
I just like to play with coding for a hobby, so probably a noob question;
I have a simple storyboard for MacOS with 2 views. Both have there own classes (main class and subclass). How can I control a outlet in the subclass from the main class?
for example
I have a button (IBAction) in the mainclass and a textfield (IBOutlet) in the subclass. I want to set the stringvalue for the textfield with a click on the button in main.
I have searched a lot last days but just don't get it. (or just need a push in the right direction)
EDIT after JingJingTao's answer:
I used the control-drag function to open the second window.
I tried the code JingJingTao gives, but the textfield doesn't respond to the action.
My classes look like this now:
ViewController.h
#import <Cocoa/Cocoa.h>
#interface ViewController : NSViewController
- (IBAction)newText:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#import "ViewController2.h"
#interface ViewController ()
#property (nonatomic) ViewController2 *subclass;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)setRepresentedObject:(id)representedObject {
[super setRepresentedObject:representedObject];
}
- (void)newText:(id)sender {
self.subclass.textField.stringValue = #"button pressed";
}
#end
ViewController2.h
#import "ViewController.h"
#interface ViewController2 : ViewController
#property (nonatomic) IBOutlet NSTextField *textField;
#end
ViewController2.m
#import "ViewController2.h"
#interface ViewController2 ()
#end
#implementation ViewController2
- (void)viewDidLoad {
[super viewDidLoad];
}
#end
Update:
I've attached two screenshots of what it looks like in the storyboard for the first suggestion,
1) Add a view to your ViewController, set the class at the top right to 'YourView', 'YourView' is a just an NSView, add a textfield to it and hook it up.
2) Add YourView as a property to your ViewController, i.e. #property (nonatomic) IBOutlet NSView *yourView; and hook it up.
Let me know if there are any issues.
You just need to put the textfield in the public interface of your subclass, so you can access it in your main class, although it does sound like you're using inheritance and I don't think you need to but that's another topic :D.
Example:
In MainClassViewController.m
#interface MainClassViewController ()
#propert (nonatomic) Subclass *subclass;
#end
#implementation MainClassViewController
// I guess you already add your subclass to the main viewcontroller because they display on the same screen.
- (void)yourButtonTapMethod {
self.subclass.textfield.text = #"Your value";
}
In Subclass.h
#interface Subclass : NSObject
#property (nonatomic) IBOutlet UITextfield *textfield;
I use Cocoa Touch instead of Cocoa, so maybe it's NSTextfield for you. Please let me know if this does not answer your question, good luck.
Running into a simple problem and not quite sure what is causing it. Linked a NSButton up to a ViewController xib. The property is referenced and then I linked up the IBAction to the view controllers view. I'm getting a crash whenever I press the button with an unrecognized selector message. I know I'm doing something wrong but on iOS this is pretty standard.
Here is the code:
#import "AppDelegate.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
IPVLocationViewController *mainViewController = [[IPVLocationViewController alloc]initWithNibName:#"IPVLocationViewController" bundle:nil];
self.window.contentView = mainViewController.view;
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
#end
#import "MainViewController.h"
#interface MainViewController ()
#property (weak) IBOutlet NSButton *mainButton;
#end
#implementation MainViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do view setup here.
}
- (IBAction)mainClick:(id)sender {
NSLog(#"THE BUTTON WAS CLICKED");
}
#end
In your Nib , it looks like your view controller instance is of type NSViewController instead of MainViewController.
So select the view controller in your Nib (or storyboard), and change its type to MainViewController.
Of course, if this view controller isn't being loaded from a nib or storyboard, then just check where you create it and make sure you created an instance of the correct class.
Solved it:
The accepted answer in this post helped me:
Needed to hold a reference to the view controller in the AppDelegate.
#interface AppDelegate ()
#property (nonatomic, strong) MainViewController *mainViewController;
#property (weak) IBOutlet NSWindow *window;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
self.mainViewController = [[MainViewController alloc]initWithNibName:#"MainViewController" bundle:nil];
self.window.contentView = self.mainViewController.view;
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
#end
I'm new to objective-c programming and I'm trying to make a status bar application right now.
I only know how to set a dropdown menu to show when click on the status bar item.
However, what I want is to show a panel when left clicked and show the menu when right clicked, just like the way Bartender 2 acts.
I've referred this demo but I could hardly figure out what it does.
I use xib to build my UI. I have three .xib files: MainMenu, Preferences and MainPanel.
AppDelegate.h
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property NSStatusItem *statusItem;
#end
AppDelegate.m
#import "AppDelegate.h"
#import "Menu.h" //Menu is a ViewController for Menu.xib
#interface AppDelegate ()
//#property (weak) IBOutlet NSWindow *window; //I don't know what is this for
#end
#implementation AppDelegate
#synthesize statusItem;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
//I initiate my statusItem here
-(void)awakeFromNib{
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
self.statusItem.title = #"T";
// you can also set an image
//self.statusBar.image =
self.statusItem.highlightMode = YES;
//I tried to use these code to set the left click action
[statusItem setTarget:self];
[statusItem setAction:#selector(showMenu:)];
}
-(void)showMenu{
Menu* menuVC = [[Menu alloc] initWithNibName:#"Menu" bundle:nil];
//Don't know what to do next...
}
#end
I tried to use
[menuVC showWindow];
but it is not right.
See this post for information about drawing an NSMenu at a given NSPoint (use popUpContextMenu:withEvent:forView: instead of showWindow).
I'll also point out that in the example you linked, the author modularizes his project into different components, following the MVC pattern. There is a component for the menu controller and view (it doesn't look like he just displays an NSMenu), as well as the panel controller and view. You may want to think about how you can organize your project to follow the conventions of MVC.
First I'll give you a short overview.
I'm ...
creating a new cocoa project
customizing the AppDelegate (see listing 1)
adding a "Custom View" to my MainMenu.xib
creating a new Cocoa Class (NSViewController + XIB) in the project, calling it MyTableViewController.*
adding a "Table View" to the recently added ViewController, like described in LINK
the code of my MyTableViewController can be seen in listing 2
Now to my problem.
The table and it's content is shown.
But if I select an item of the table and press a button (connected to (IBAction)action:(id)sender) on this subview, <NSIndexSet: 0x60000022c100>(no indexes) is shown in the output (see: selectedColumnIndexes).
After experimenting a while, I found out that there are two instances of the MyTableViewController class.
Can someone please explain me why there are two instances and help me to fix this problem.
Thx
listing 1:
// FILE: AppDelegate.h
#import <Cocoa/Cocoa.h>
#class MyTableViewController;
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (nonatomic, assign) NSViewController * currentViewController;
#property (nonatomic, strong) MyTableViewController * myTableViewController;
#property (weak) IBOutlet NSView *myview;
#end
// FILE: AppDelegate.m
#import "AppDelegate.h"
#import "MyTableViewController.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[self changeViewController];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication {
return YES;
}
- (void)changeViewController
{
if ([self.currentViewController view] != nil) {
[[self.currentViewController view] removeFromSuperview];
}
switch (0) {
case 0:
default:
if (self.myTableViewController == nil) {
_myTableViewController = [[MyTableViewController alloc] initWithNibName:#"MyTableViewController" bundle:nil];
}
self.currentViewController = self.myTableViewController;
NSLog(#"EndView");
break;
}
[self.myview addSubview:[self.currentViewController view]];
[[self.currentViewController view] setFrame:[self.myview bounds]];
[self.currentViewController setRepresentedObject:[NSNumber numberWithUnsignedInteger:[[[self.currentViewController view] subviews] count]]];
[self didChangeValueForKey:#"viewController"];
NSLog(#"ViewController changed");
}
#end
listing 2:
// FILE: MyTableViewController.h
#import <Cocoa/Cocoa.h>
#interface MyTableViewController : NSViewController
#property (weak) IBOutlet NSTableView *tview;
- (IBAction)action:(id)sender;
#end
// FILE: MyTableViewController.m
#import "MyTableViewController.h"
#import "AppDelegate.h"
#interface MyTableViewController ()
#end
#implementation MyTableViewController
- (NSUInteger)numberOfRowsInTableView:(NSTableView *)tableViewObj {
return 2;
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
if ([tableView tableColumns][0] == tableColumn) {
return #"bla";
} else if ([tableView tableColumns][1] == tableColumn) {
return #"blub";
}
NSLog(#"dropped through tableColumn identifiers");
return NULL;
}
- (IBAction)action:(id)sender {
// selectedColumnIndexes
NSLog(#"%#", [self.tview selectedColumnIndexes]);
}
#end
Two instances of a class, when you only expect/want one, can be caused by failing to appreciate that objects in your xib files are themselves actual automatically-generated instances of that class.
Have you dragged a blue cube into either of your xib files and set it's class to your view controller subclass? If you have, then this will account for one of the objects that you're seeing - Apple's machinery creates it on your behalf. The second object is the one created by you, in code, in your changeViewController method.
I get the impression that you're simply trying to create a window, which contains an NSTableView, which in turn gets its data from your own NSViewController subclass. Is this correct? If it is then you should do away with the second xib file, and instead just use the xib created for you when you created the project.
In Brief
Drag a blue object cube into the Interface Builder dock and set it's subclass to MyTableViewController using the Identity Inspector.
With your view controller blue-cube selected go to the Connections Inspector and drag from the view option to your table view - you must make sure you're dragging to the table view, not the scroll view or clip view that encloses it.
Select the table view (again, make sure it really is the table view you've selected), and go to it's Connections Inspector. Drag from the data source and delegate options to your blue view-controller cube.
Implement the relevant data-soruce methods
Hint: If you aren't sure which inspector is which, open the right sidebar in Xcode and select a xib file from the left file-viewer sidebar. Sit the cursor over each of the icons at the top of the right sidebar and the tool tip will tell you which is which.
Update
A good way of identifying specific objects, and something that can assist with debugging, is to set their identifier. In the attributes inspector, this is the restoration ID. Do this for your NSTableView instance, and for your NSTableColumn instances. Then, in your data-source methods do some logging with them - for instance does the table view passed as the first argument for this methods have the expected identifier, what about the table columns?
I have a project that has a switch from an xib separate from the main ViewController. I am trying to make it so that when you switch the UISwitch to OFF a button in the ViewController is hidden. I have tried to declare the AppDelegate in the xib's .m file but still no luck. My code is:
- (void)viewDidAppear:(BOOL)animated {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
}
What am I doing wrong? I am trying to do
if (switch.on) {
myButton.hidden = YES;
} else {
myButton.hidden = NO;
}
I have also done this in the .m file of the second xib (the one that does not have the main ViewController)
#import "AppDelegate.h"
#import "ViewController.h"
But still NOTHING! So basically, I'm just trying to declare a button in one xib from another. THAT'S IT. PLEASE HELP Thanks!
First, see this about passing data between view controllers and this about UISwitch.
Once you understand that, set up your code something like this--
FirstViewController.h:
#interface FirstViewController : UIViewController
{
...
}
#property IBOutlet UISwitch *theSwitch;
- (IBAction)switchToggled;
#end
SecondViewController.h:
#interface SecondViewController : UIViewController
{
...
}
#property IBOutlet UIButton *button;
#property UIViewController *theFirstViewController;
#end
Be sure that theFirstViewController gets set somehow -- again, see passing data between view controllers. Then in switchToggled you can do this:
- (IBAction)switchToggled
{
theFirstViewController.button.hidden = YES;
}
The important thing to remember is that your view controllers don't magically know about each other. Even if you do something with the AppDelegate like you were trying. SecondViewController needs a reference to FirstViewController.