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.
Related
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.
I just started a new Cocoa project after a long time... And I don't know why, but I always get an error when calling a xib by a NSWindowController. What I do is really very simple: I have a new project as a starting point and then I don't wantz to call the xib from Appdelegate, but from a subclass of NSWindowController. Then the output tells me that:
2014-11-12 09:58:18.519 SimpleTest[8554:378690] ApplePersistence=NO
2014-11-12 09:58:18.671 SimpleTest[8554:378690] Failed to connect (window) outlet from (NSApplication) to (NSWindow): missing setter or instance variable
Okay, how does it look in code? My Appdelegate looks like this:
#import "AppDelegate.h"
#import "MainWindowController.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#property (strong) MainWindowController *mainWindowController;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
_mainWindowController = [[MainWindowController alloc] initWithWindowNibName:#"MainMenu"];
[self.mainWindowController showWindow:self];
}
#end
Nothing special so far. The MainWindowController looks like this:
#import "MainWindowController.h"
#interface MainWindowController ()
#property (weak) IBOutlet NSWindow *window;
#end
#implementation MainWindowController
- (id)initWithWindow:(NSWindow *)window
{
self = [super initWithWindow:window];
if (self != nil)
{
//do something
}
return self;
}
#end
And again very simple... Additiponally I have some modifications in IB: File'Owner of MainMenu.xib becomes MainWindowController. Its 'window' outlet is connected to the Window of the application. The delegate of Window is connected to File's Owner. Well, that's it! But why do I receive this error? What am I doing wrong?
---EDIT---
this shows the connections in IB
The most important is using right selector to create new instance and having all wired up correctly.
Steps:
1. Add new xib with window or an empty one and add window to it
2.Select File owner and set it to NSWindowController or its subclass.
Select window and connect it to File owner
Make new NSWindowController instance
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSWindowController *windowController = [[NSWindowController alloc] initWithWindowNibName:#"Window"];
[NSApp runModalForWindow:[windowController window]];
}
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?
For some reason, i'm not able to change or add text in my UITextView from code.
I have made Outlet and connected (All in IB) - nothing happens.
Even tried to add -setNeedsDisplay
Do i need to set some property ? - This i driving me nuts....
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UITextViewDelegate> {
UITextView *textInfoView;
}
#property (nonatomic, retain) IBOutlet UITextView *textInfoView;
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
textInfoView.text = #"Test";
}
-(void)viewDidAppear:(BOOL)animated{
textInfoView.text = #"Test";
textInfoView.text = [textInfoView.text stringByAppendingString:#"Line2"];
[textInfoView setNeedsDisplay];
It seems, really strange that this code doesn't update the text inside the UITextView. I have tried and it works for me ... I think that you don't have correctly linked the Outlet in interface builder, or you don't have associated your view controller to your view ...
UPS - Got it...
Forgot to #synthesize...
For some reason i did not get warning or "setter error" in Xcode 4.4, maybe it's a version bug ?
I'm having a lot of trouble with what seems like a very simple thing. I cannot update a UILabel programmatically from a Navigation-based iOS App. I don't want to use a button as this label is designed to report the status of an external system, and should update on launch. There is no need to make the user go though the extra step on touching the button if I don't have to.
The following is a somewhat exhaustive list of the steps I've taken. I'm sorry if some of this seems unnecessary, but in my experience even the smallest forgotten step can be the cause of the issue.
From a fresh Navigation-based App in Xcode here are the steps I'm taking:
Replace UITableView with a generic UIView class
Re-wire File's Owner's view outlet to the new UIView
Add a UILabel to the center of the UIView, make the text centered, and leave the default text.
Save and Exit Interface Builder
RootViewController.h
#import <UIKit>
#interface RootViewController : UIViewController {
UILabel *myLabel;
}
#property (nonatomic, retain) IBOutlet UILabel *myLabel;
#end
RootViewController.m
#import "RootViewController.h"
#implementation RootViewController
#synthesize myLabel;
...
Removed TableView stuff from RootViewController.m
Wire IBOutlet myLabel to the Label in RootViewController.xib
Save and Exit Interface Builder
tempNavAppAppDelegate.m
...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Add the navigation controller's view to the window and display.
[self.window addSubview:navigationController.view];
[self.window makeKeyAndVisible];
RootViewController *rootViewCont = navigationController.visibleViewController;
rootViewCont.myLabel.text = #"test";
NSLog(#"Label Text: %#", rootViewCont.myLabel.text);
return YES;
}
...
Build/Run
The Label shows as "Label" not "test". And the log reports:tempNavApp[94186:207] Label Text: (null)
I've tried a number of different ways to get this done, but any help would be appreciated.
The Journey
After discovering that my rootViewCont.myLabel was also nil, thanks to the help of mprudhom, I decided to test and see if I could assign myLabel.text a value in RootViewController.m's - (void)viewDidLoad method.
It worked, I was able to change the text directly from the RootViewController. But while this proved my View Controller wasn't broken, it did not solve my initial desire to change the UILabel from tempNavAppAppDelegate.m.
Elliot H. then suggested that navigationController.visibleViewController wasn't actually returning a view controller. I had tested for the value of rootViewCont and it came back as a RootViewController, but Elliot's suggestion got me thinking about the app's lifecycle and when the different parts of my code was actually loaded up.
So I started printing an NSLog at each step of the launch process (application:didFinishLaunchingWithOptions:, applicationDidBecomeActive:, viewDidLoad, viewDidAppear:), and discovered to my surprise that [self.window makeKeyAndVisible]; does not mean that the view will load before application:didFinishLaunchingWithOptions: is complete.
With that knowledge in hand I knew where the problem was. The solution (or at least my solution) seems to be NSNotificationCenter. I have now registered for notifications in tempNavAppAppDelegate and I am broadcasting a notification in RootViewController's viewDidAppear: method.
The Pertinent Code
RootViewController.h:
#interface RootViewController : UIViewController {
IBOutlet UILabel *myLabel;
}
#property (nonatomic, retain) UILabel *myLabel;
#end
RootViewController.m:
#implementation RootViewController
#synthesize myLabel;
- (void)viewDidLoad {
[super viewDidLoad];
NSParameterAssert(self.myLabel);
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[NSNotificationCenter defaultCenter] postNotificationName:#"viewDidAppear" object:self];
}
tempNavAppAppDelegate.h:
#interface tempNavAppAppDelegate : NSObject {
UIWindow *window;
UINavigationController *navigationController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
- (void)viewDidAppearNotification:(id)notification;
#end
tempNavAppAppDelegate.m:
#implementation tempNavAppAppDelegate
#synthesize window;
#synthesize navigationController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self.window addSubview:navigationController.view];
[self.window makeKeyAndVisible];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(viewDidAppearNotification:) name:#"viewDidAppear" object:nil];
return YES;
}
- (void)viewDidAppearNotification:(id)notification
{
NSString *noteClass = [NSString stringWithFormat:#"%#", [[notification object] class]];
if ([noteClass isEqualToString:#"RootViewController"]) {
RootViewController *noteObject = [notification object];
noteObject.myLabel.text = #"Success!";
}
}
If this code is printing nil:
rootViewCont.myLabel.text = #"test";
NSLog(#"Label Text: %#", rootViewCont.myLabel.text);
Then almost certainly it is because rootViewCont.myLabel itself is nil. Try logging the value of rootViewCont.myLabel as well and you'll see.
Are you sure you wired up the label to your UILabel IBOutput declaration in Interface Builder? That's most commonly the problem.
I personally always assert all my expected outlets in viewDidLoad so that I catch early on when the outlets have been (accidentally or not) been decoupled in Interface Builder. E.g.:
- (void)viewDidLoad {
[super viewDidLoad];
NSParameterAssert(rootViewCont.myLabel);
}
your interface should look like this
#import <UIKit>
#interface RootViewController : UIViewController {
// IBOutlet here...
IBOutlet UILabel *myLabel;
}
#property (nonatomic, retain) UILabel *myLabel;
#end
Is visibleViewController actually returning the view controller? My guess is since application:didFinishLaunchingWithOptions: hasn't returned yet, it's possible UINavigationController hasn't properly configured that property to return yet, even though you've added the navigation controller's subview to the view hierarchy, it's probably that visibleViewController isn't valid until after viewDidAppear: is called on the view controller in question.
Try having an IBOutlet to the RootViewController directly, or create it programmatically, and then assign the label text.
Just a general reminder: If an object is nil (in this case visibleViewController would be returning nil), and you send it a message, you won't crash, because messages to nil are valid and won't do anything. When you call the myLabel accessor on the rootViewCont object, if rootViewCont is nil, myLabel will return nil always.