I am just learning and trying to put forth some of the knowledge with objective c and cocoa. I have a little program that has a login screen which then displays a main menu. On the main menu view, there is a button that should load a view to be able to add some data to the app. When I click on the button, it doesn't fire the IBAction but throws an error
EXC_BAD_ACCESS [StartMenuViewController performSelector:withObject:]: message sent to deallocated instance 0x6080195e9f90
I know that it is because there is an object that is not instantiated when the message is being sent, but I cannot find out why. Sorry for the code and the novice level here is the main menu view controller StartMenuViewController.m file:
#import "StartMenuViewController.h"
#import "AppDelegate.h"
#import "MasterViewController.h"
#interface StartMenuViewController ()
#end
#implementation StartMenuViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do view setup here.
}
-(IBAction)showStrainView:(id)sender{
AppDelegate *delegate = nil;
delegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
MasterViewController *addStrainView = [[MasterViewController alloc] initWithNibName:#"MasterViewController" bundle:nil];
[delegate swapView:self.view toView:addStrainView.view];
}
#end
here is the StartMenuViewController.h
#import <Cocoa/Cocoa.h>
#interface StartMenuViewController : NSViewController
#property (weak) IBOutlet NSButton *showStrainViewButton;
#end
Here is the AppDelegate.h
#import <Cocoa/Cocoa.h>
#class loginView, MasterViewController,StartMenuViewController;
#interface AppDelegate : NSObject <NSApplicationDelegate>{
loginView *loginView;
MasterViewController *masterViewController;
}
#property (assign) IBOutlet NSWindow *window;
#property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
#property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
#property (strong) loginView *loginView;
#property (strong) MasterViewController *masterViewController;
#property (strong) StartMenuViewController *starMenuViewController;
-(void)swapView:(NSView *)view1 toView:(NSView *)view2;
#end
Here is the AppDelegate.m
#import "AppDelegate.h"
#include "MasterViewController.h"
#import "ScaryBugDoc.h"
#import "Strains.h"
#import "loginView.h"
#interface AppDelegate()
#end
#implementation AppDelegate
#synthesize managedObjectContext = _managedObjectContext;
#synthesize managedObjectModel = _managedObjectModel;
#synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
#synthesize loginView;
#synthesize masterViewController;
#synthesize starMenuViewController;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// 1. Create the master View Controller
self.loginView = [[loginView alloc] initWithNibName:#"loginView" bundle:nil];
// 2. Add the view controller to the Window's content view
[self.window.contentView addSubview:self.loginView.view];
self.loginView.view.frame = ((NSView*)self.window.contentView).bounds;
}
-(void)swapView:(NSView *)view1 toView:(NSView *)view2{
//[view1 removeFromSuperview];
[self.window.contentView addSubview:view2];
view2.frame = ((NSView*)self.window.contentView).bounds;
}
#end
If you need the loginView.h and loginView.m files I can add them as well. I would appreciate any and all help. Thanks in advance
The problem is here:
MasterViewController *addStrainView = [[MasterViewController alloc]initWithNibName:#"MasterViewController" bundle:nil];
[delegate swapView:self.view toView:addStrainView.view];
You don't keep a reference to the MasterViewController so it gets deallocated even though its view is kept around. Now anytime the view tries to access methods from the view controller, things fail.
You should make addStrainView a child view controller of self in the above code so the view controller sticks around as long as its view.
Related
I am working on this couple of days and I can't figure out what I am doing wrong.
First, I don't know if the KVO is the best way to pass the value of the clicked row info from one view controller to another view controller.
I have Main view, with 2 buttons only. Clicking on one of the buttons, I open MasterTableViewController, where I have the NSTableView with some records (one column only) from core data.
On click on the NSTableView, I am opening the DetailViewController, where I have only one label.
What I am trying is to change the label value, with the value of the clicked row in the NSTableView.
While I can print the value of the clicked row in the DetailViewController, I can't change the label value. I assume that notification comes before the viewDidLoad get called.
I have the following code:
In my MasterTableViewController.h file:
#import <Cocoa/Cocoa.h>
#import "AppDelegate.h"
#interface MasterTableViewController : NSViewController
#property (nonatomic,strong) NSManagedObjectContext *mObjContext;
#property (weak) IBOutlet NSTableView *websitesTableView;
- (IBAction)tableViewDoubleClick:(id)sender;
#property (nonatomic,strong) NSString *cellValue;
#end
In my MasterTableViewController.m file:
#import "MasterTableViewController.h"
#import "DetailViewController.h"
#interface MasterTableViewController ()
#end
#implementation MasterTableViewController
-(void)awakeFromNib {
[_websitesTableView setTarget:self];
[_websitesTableView setDoubleAction:#selector(tableViewDoubleClick:)];
}
- (void)viewDidLoad {
[super viewDidLoad];
// Get the object managed context
AppDelegate *appDelegate = (AppDelegate*)[[NSApplication sharedApplication] delegate];
self.mObjContext = appDelegate.managedObjectContext;
}
- (IBAction)tableViewDoubleClick:(id)sender {
NSInteger rowNumber = [_websitesTableView clickedRow];
NSTableColumn *column = [_websitesTableView tableColumnWithIdentifier:#"websiteUrl"];
NSCell *cell = [column dataCellForRow:rowNumber];
_cellValue = [cell stringValue];
MasterTableViewController *mtvc = [[MasterTableViewController alloc]initWithNibName:#"MasterTableViewController" bundle:nil];
DetailViewController *dvc = [[DetailViewController alloc]initWithNibName:#"DetailViewController" bundle:nil];
[mtvc addObserver:dvc
forKeyPath:#"cellValue"
options:NSKeyValueObservingOptionNew
context:NULL];
[mtvc setCellValue:_cellValue];
[mtvc removeObserver:dvc forKeyPath:#"cellValue"];
AppDelegate *appDelegate = (AppDelegate*)[[NSApplication sharedApplication] delegate];
[appDelegate changeViewController:2];
}
#end
Code for the DetailViewController.h is:
#import <Cocoa/Cocoa.h>
#import "AppDelegate.h"
#interface DetailViewController : NSViewController
#property (nonatomic, weak) AppDelegate *appDelegate;
#property (weak) IBOutlet NSTextField *detailLabel;
#property (nonatomic,strong) NSString *transferedLabelValue;
#property (nonatomic,strong) NSString *cellValue;
#end
Code for the DetailViewController.m is:
#import "DetailViewController.h"
#import "MasterTableViewController.h"
#interface DetailViewController ()
#end
#implementation DetailViewController
- (void)viewDidLoad {
[super viewDidLoad];
// if I uncomment this line, program crashes, because the value of the _transferedLabelValue is null here.
//[_detailLabel setStringValue:_transferedLabelValue];
}
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if ([keyPath isEqualToString:#"cellValue"]) {
_transferedLabelValue = [change objectForKey:NSKeyValueChangeNewKey];
// Here I get the value of the clicked cell, it is printed in the console. However if I try to change the label value here, doesn't work, since seems that label does not exist yet at this point....
NSLog(#"Value in the observerValueForKeyPath is:%#",_transferedLabelValue);
}
}
#end
Code from the AppDelegate.h :
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
#property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
#property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (weak) IBOutlet NSView *mainAppView;
#property (nonatomic,strong) NSViewController *mainAppViewController;
- (IBAction)showMasterViewButtonHasBeenClicked:(id)sender;
- (IBAction)showDetailViewButtonHasBeenClicked:(id)sender;
- (void)changeViewController:(NSInteger)tag;
#end
Here is only the relevant code from the AppDelegatge.m, the boiler plate code is omited.
#import "AppDelegate.h"
#import "MasterTableViewController.h"
#import "DetailViewController.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
- (IBAction)saveAction:(id)sender;
#end
#implementation AppDelegate
- (IBAction)showMasterViewButtonHasBeenClicked:(id)sender {
NSInteger tag = [sender tag];
[self changeViewController:tag];
}
- (IBAction)showDetailViewButtonHasBeenClicked:(id)sender {
NSInteger tag = [sender tag];
[self changeViewController:tag];
}
- (void)changeViewController:(NSInteger)tag {
[[_mainAppViewController view]removeFromSuperview];
switch (tag) {
case 1:
self.mainAppViewController = [[MasterTableViewController alloc]initWithNibName:#"MasterTableViewController" bundle:nil];
[_mainAppView addSubview:[_mainAppViewController view]];
[[_mainAppViewController view] setFrame:[_mainAppView bounds]];
[[_mainAppViewController view] setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
break;
case 2:
self.mainAppViewController = [[DetailViewController alloc]initWithNibName:#"DetailViewController" bundle:nil];
[_mainAppView addSubview:[_mainAppViewController view]];
[[_mainAppViewController view] setFrame:[_mainAppView bounds]];
[[_mainAppViewController view] setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
break;
}
}
Again, if the KVO and notification is not the best way to go, please let me know how I should "inform" the other view which row has been clicked and which is his value.
Regards, John
I manage to solve my problem with the KVO...My problem was that I was creating new instances of the view controllers in the MasterTableViewController...What I did was I created properties for the ViewControllers in the appDelegatge.h like this:
#property MasterTableViewController *mtvc;
#property DetailViewController *dvc;
and then in the appDelegate.m :
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
_mtvc = [[MasterTableViewController alloc]initWithNibName:#"MasterTableViewController" bundle:nil];
_dvc = [[DetailViewController alloc]initWithNibName:#"DetailViewController" bundle:nil];
}
Rest of the changes in the code are just replacing the MasterTableViewController and DetailViewController with _mtvc and _dvc...
However...I had been warned that this solution is error prone, and that warning shows true. It happens from time to time that I get wrong value, not the clicked one. Once in 4-5 clicks, I get wrong value. What makes me confuse is that I make the function to work on double click, but it works on single click as well...But those things are for some other question I guess.
hoepfully a simple fix. :)
I'm using a collection view with a push segue to a detailed view controller. When the cell is clicked in simulator the app crashes with a break point indicating at this part of the code:
#import "DetailViewController.h"
#interface DetailViewController ()
#property (nonatomic, weak) IBOutlet UIImageView *imageView;
#end
#implementation DetailViewController
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated]; //<--------- break point
self.imageView.image = self.image;
}
#end
segue id etc is correct. So not at all sure why it shouldn't be working.
Any help on this as always is appreciated. If you need further info let me know.
A.
edit:
.h file as follows:
#import <UIKit/UIKit.h>
#interface DetailViewController : UIViewController
#property (nonatomic, strong) UIImage *image;
#end
How can I access to ViewController property from another class ?
(the ViewController was created by XCode)
this is code of ViewController.h:
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#interface ViewController : UIViewController{
AppDelegate *appDelegate; //il delegate disponibile per tutta la classe
int numPag; //indice array (sulle pagine html da visualizzare)
NSString *path;
NSURL *baseURL;
NSString *file;
NSString *contentFile;
}
- (IBAction)sottrai:(id)sender;
- (IBAction)aggiungi:(id)sender;
#property (strong, nonatomic) IBOutlet UILabel *valore;
#property (strong, nonatomic) IBOutlet UIWebView *web;
#end
thanks!
[EDIT]
I use Xcode 4.3.2 and the code of AppDelegate.m not find something like:
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
Now, I put in the method didFinishLaunchingWithOptions:(into AppDelegate.m) this code:
viewController = [[UIViewController alloc] initWithNibName:#"ViewController" bundle:nil];
[viewController.web loadHTMLString:contentFile baseURL:baseURL];
adding in the file AppDelegate.h the variable viewController:
#import <UIKit/UIKit.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate>{
NSArray *pageInItalian;
NSArray *pageInEnglish;
UIViewController *viewController;
}
#property (strong, nonatomic) NSArray *pageInLanguage;
#property (strong, nonatomic) UIWindow *window;
#end
I did it correctly? the error is: Property 'web' not found on object of type 'UIViewController *'
I think this post might answer your question
How can I access variables from another class?
You need to make your variable accessible from foreign classes (with #property and #synthesize) and then your foreign class need to know your instance of ViewController
[EDIT]
In your AppDelegate.m, there must be a line like this one :
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
This is where your ViewController is instantiate. From there, you can access every property of your ViewController.
[EDIT]
You need to add the following at the beginning of your implementation
#implementation ViewController
#synthesize web;
I would like the view controller to be able to access dog in the app delegate.
I would like the app delegate to be able to access mouse in the view controller.
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
{
int mouse; // <----------------
}
#end
- (void)viewDidLoad
{
[super viewDidLoad];
mouse = 12; // <-------------------
NSLog(#"viewDidLoad %d", dog); // <---------------
}
#import <UIKit/UIKit.h>
#class ViewController;
#interface AppDelegate : UIResponder <UIApplicationDelegate>
{
int dog; // <---------------
}
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) ViewController *viewController;
#end
- (void)applicationWillResignActive:(UIApplication *)application
{
NSLog(#"applicationWillResignActive %d", mouse); // <--------------
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
dog = 77; // <---------------------
NSLog(#"applicationDidBecomeActive");
}
Part 1:
In the ViewController.h:
-(int)mouse; //add this before the #end
In the ViewController.m, add this method:
-(int)mouse
{
return mouse;
}
To access mouse from AppDelegate, use self.viewController.mouse
For example;
NSLog(#"ViewController mouse: %i", self.viewController.mouse);
Part2:
In the AppDelegate.h:
-(int)dog; //add this before the #end
In the AppDelegate.m, add this method:
-(int)dog
{
return dog;
}
In the ViewController.m:
#import "AppDelegate.h"
To access dog from ViewController, use this:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSLog(#"dog from AppDelegate: %i", [appDelegate dog]); //etc.
In your view controller header file add mouse as a property:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
{
NSInteger mouse; // <----------------
}
#property (nonatomic, assign) NSInteger mouse;
#end
Synthesize the property in your view controller implementation just below the #implementation line:
#synthesize mouse;
In your app delegate add dog as a property:
#interface AppDelegate : UIResponder <UIApplicationDelegate>
{
NSInteger dog; // <---------------
}
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) ViewController *viewController;
#property (nonatomic, assign) NSInteger dog;
#end
Also synthesize dog in your app delegate implementation.
Now in your app delegate, assuming you have a reference to your view controller, you can access mouse like this:
viewController.mouse = 13;
You can do the same thing with your app delegate class, which can be accessed from any view controller using (assuming the name of your app delegate class is AppDelegate):
((AppDelegate *)([UIApplication sharedApplication].delegate)).dog = 13;
I would recommend that you use also use NSInteger instead of int.
I am trying to improve myself in Objective-C and what I am on about is now modifying and changing and existed project. I did everything I wanted expect one thing. Theres an info button in my program and it doesnt related with .xib file. I tried to remove it but always my main.m gave error like SIGABRT (int retval thing.)
Here is the thing what I want to modify
http://i.stack.imgur.com/ZG51s.png // The picture of my program and info button
I am trying to remove the (i) button on the right-down corner.
Here's my code
This is my RootViewController.h
#import <UIKit/UIKit.h>
#class MainViewController;
#interface RootViewController : UIViewController {
IBOutlet UIButton *infoButton; //Silinecek
MainViewController *mainViewController;
}
#property (nonatomic, retain) MainViewController *mainViewController;
#end
This is my RootViewController.m
#import "RootViewController.h"
#import "MainViewController.h"
#implementation RootViewController
#synthesize mainViewController;
- (void)viewDidLoad {
MainViewController *viewController = [[MainViewController alloc] initWithNibName:#"MainView" bundle:nil];
self.mainViewController = viewController;
[viewController release];
[self.view insertSubview:mainViewController.view belowSubview:infoButton];
}
and also I tried to modify my AppDelegate h and m. Here they are ;
#import <UIKit/UIKit.h>
#class RootViewController;
#interface AppDelegate : NSObject <UIApplicationDelegate> {
IBOutlet UIWindow *window;
IBOutlet RootViewController *rootViewController;
}
#property (nonatomic, retain) UIWindow *window;
#property (nonatomic, retain) RootViewController *rootViewController;
#end
#import "AppDelegate.h"
#import "RootViewController.h"
#implementation AppDelegate
#synthesize window;
#synthesize rootViewController;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[window addSubview:[rootViewController view]];
//[window makeKeyAndVisible];
}
/*
- (void)dealloc {
[rootViewController release];
[window release];
[super dealloc];
}
*/
What should I do for removing that info button in my View. Thanks for the any tips
And if you are interested in this is the whole algorithm : http://pastebin.com/hHQkQYS6
Do you still have the control linked somewhere in your xib file? It is declared as an IBOutlet control. Usually when I have these controls I will use the GUI link this control in some way (I can't remember what exactly as I have not done Obj-C in a long time). But I would definitely have a look around you xib file (as well as removing [self.view insertSubview:mainViewController.view belowSubview:infoButton];
The exception mentions:
[<RootViewController 0x4b9ab20> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key infoButton.
The call stack shows that it is thrown by UINib. So this is happening during nib load. So check for all objects present, carefully, in the nib and remove the info button