I have a project with 3 .xib files, MainMenu, FileUploadView, FileBrowseView.
MainMenu has a NSPanel, it's owner is AppDelegate, and AppDelegate has an outlet to NSPanel called FilePanel. The NSView below the NSPanel is called filePanelView and also has an outlet in AppDelegate.
FileUploadView is an NSView, it's owner is FileUploadViewController. It has an outlet called uploadView in the controller.
FileBrowseView is similar, owner is FileBrowseViewController, has an outlet called browseView.
So in App delegate I have the following code:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
fileBrowseViewController = [[FileBrowseViewController alloc]
initWithNibName:#"FileBrowseView" bundle:nil];
}
- (IBAction)importHandsClicked:(id)sender {
[NSApp activateIgnoringOtherApps:YES];
[filePanel setIsVisible:YES];
[filePanelView addSubview:[fileBrowseViewController browseView]];
}
The action does make filePanel visible, but it doesn't add the browseView to it. Am I doing something wrong?
Check that [fileBrowseViewController browserView] is not nil.
This is highly probably, especially if you forgot or failed to link your browseView IBOutlet to the actual UIView that represents your FileBrowseView instance in InterfaceBuilder.
[EDIT]
For more info about connecting outlets, see Apple's InterfaceBuilder Help here (including tutorial video).
Related
im on OSX, XCode9, Objective C.
I have a viewController layouted in IB. The view contains a button connected to the corresponding viewController
SHTourViewController.h
#property (weak) IBOutlet SHStandardButton *closeButton;
// SHStandardButton is a subclass from NSBUtton.
The view controller gets instantinated by code in another class (i need to instantinate this viewController from other classes cause i need it more than once).
// Get instance of viewController
SHTourViewController* tourViewController = [storyBoard instantiateControllerWithIdentifier:#"tourViewController"];
Now in viewDidLoad method of my viewController, i like to connect the buttons action and target:
SHTourViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
[self.closeButton setAction:#selector(closeButtonClicked:)];
[self.closeButton setTarget:self];
}
- (void)closeButtonClicked:(id)sender {
NSLog(#"CLOSE!");
}
}
When i click the button, the app crashes (Thread 1: EXC_BAD_ACCESS). I can't find the mistake i am doing here.
Any help appreciated.
You are not supplying enough information about what you're doing. But, as it stands, the fact that fact that you are getting a Bad Access would suggest that some important object has vanished prematurely in a puff of smoke. My guess is that that object is self, and that the problem has to do with code after this line:
SHTourViewController* tourViewController =
[storyBoard instantiateControllerWithIdentifier:#"tourViewController"];
You are obtaining a completely new instance of this view controller but then you are not getting its view correctly into the view hierarchy and the view controller itself into the view controller hierarchy, so the view controller (I'm guessing) is released.
But you didn't show us the relevant code, so that's just a guess.
I have been stuck with this warning for several hours now. I've looked around SO for answers, attempted all the ones I found and couldn't find the solution. Here's the run-down of the code I have, which Xcode generated by default.
This is in my AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
return YES;
}
I have this on main.m (according to this answer)
int main(int argc, char *argv[])
{
#autoreleasepool {
int retVal = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
return retVal;
}
}
I also have all the connections in my MainWindow.xib connected correctly. So I'm at a loss right now. Anything that I could be missing? Thanks in advance!
It's odd to be setting your window's rootViewController in application:didFinishLaunchingWithOptions: if you have a MainWindow.xib. Usually a project follows one of three templates:
Some projects have a MainWindow.xib. The target's “Main Interface” is set to “MainWindow” in the target's Summary tab (or in its Info.plist). This xib's File's Owner is UIApplication. The xib contains an instance of AppDelegate, connected to the File's Owner's delegate outlet. The xib also contains a UIWindow, whose rootViewController outlet is connected to a UIViewController (or subclass, such as UINavigationController), which is also in the xib. By the time the application delegate receives the application:didFinishLaunchingWithOptions: message, the xib is entirely loaded, so the window and its root view controller are already set up.
Other projects don't have a MainWindow.xib. The target's “Main Interface” is empty. Instead, the UIApplicationMain function creates an instance of AppDelegate, sets it as the UIApplication's delegate, and sends it the application:didFinishLaunchingWithOptions: message. The app delegate handles that message by creating a UIWindow, creating a view controller (or several), and setting the window's rootViewController property. The default version looks like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
Some projects have a MainStoryboard.storyboard. I'm not going to describe this in detail because it doesn't seem relevant to your problem.
The problem you're describing makes it sound like you're using half of the first template, and half of the second template. That won't work. You need to decide which approach you're taking, and go all-in.
You can open xib file and right-click "File's Owner" in Placeholders. If view didn't connect to View outlet then hold "Ctrl" key and drag right mouse click to design, then run again ^^ (do not drag to particular control, drag to background design when appear border View).
I have this message because I had in my RootViewController #property(weak, nonatomic) IBOutlet UIView* loadView;
and viewDidLoad was called twice... Rename it to something else...
I have solve issue in my project, Follow the below steps to solve it..
1) Open the main view controller nib file, that file you have reference in appDelegate
eg.ViewCotroller.xib
2) On nib file check the view connection, if not connected to file owner then connect it.
3) Now run the project.
I am trying to open a ViewController from within another ViewController if certain conditions are met. The code seems to run without error but the view is never shown. I am new to xcode 4 /ios 5 so I must be missing something.
Here is the code responsible for opening the second viewcontroller:
CreateUserViewController *createUserController = [[CreateUserViewController alloc] initWithNibName:#"CreateUserView" bundle:[NSBundle mainBundle] keyWrapper:keyChainWrapper];
[self presentViewController:createUserController animated:YES completion:nil];
In my project I have a xib called, "CreateUserView". I have added a view controller to this xib and assigned it to, "CreateUserViewController".
Also I noticed in the apple documentation that is shows setting the delegate of the viewcontroller to be presented. But it seems that no property called, "delegate" is on the viewcontroller object. Is this documentation old? This is the document I am trying to use (section 9-1):
View Controller Programming
Can someone give me a hint? Thanks..
edit Adding Custom Constructor
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil keyWrapper:(KeychainItemWrapper *)keyWrapper
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if(self){
[self setKeyChainWrapper:keyWrapper];
}
return self;
}
Regarding CreateUserView.xib: you don't want to put a CreateUserViewController object in the nib. You want to set the custom class of the File's Owner placeholder to CreateUserViewController. Then you need to connect the view outlet of File's Owner to the top-level view in the nib.
Regarding the delegate property: The UIViewController class doesn't have its own delegate property. The idea is that you add a delegate property to your subclass of UIViewController. The delegate provides a way for your presented view controller to pass custom information back to the presenting view controller.
Why would you want to do that? Let's consider the code you posted. I'll assume you have a UserListViewController that shows a list of User objects, and has a "Create new user" button. When the user touches the "Create new user" button, you create a CreateUserViewController and present it.
The user interacts with the CreateUserViewController to set the attributes of the new User object - name, rank, hairstyle, etc. Then he touches a "Done" button. Your CreateUserViewController creates the new User object and puts it in the database. Then it needs to dismiss itself, so the UserListViewController's list of User objects will appear again.
But you want the User list to include the newly created User object and you want to scroll the list so that the new User is on the screen. So you need a way to have your CreateUserViewController tell the UserListViewController about the newly created User object. This is where the delegate comes in.
You define a protocol like this:
#protocol CreateUserViewControllerDelegate
- (void)didCreateUser:(User *)user;
#end
and you give your CreateUserViewController a delegate property:
#interface CreateUserViewController
#property (weak, nonatomic) id<CreateUserViewControllerDelegate> delegate;
// ...
When your CreateUserViewController's "Done" button is touched, you notify your delegate of the new User:
- (IBAction)doneButtonWasTouched:(id)sender {
User *user = [self createUser];
[self.delegate didCreateUser:user];
[self dismissViewControllerAnimated:YES completion:nil];
}
In your UserListViewController, you adopt and implement the protocol:
#interface UserListViewController <CreateUserViewControllerDelegate, UITableViewDelegate, UITableViewDataSource>
// ...
#end
#implementation UserListViewController
- (void)didCreateUser:(User *)user {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[self.users count] inSection:0];
[self.users addObject:user];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation: UITableViewRowAnimationAutomatic];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition: UITableViewScrollPositionNone animated:YES];
}
and when you need to present a CreateUserViewController, you set the new controller's delegate to the UserListViewController:
- (IBAction)createUserButtonWasTouched:(id)sender {
CreateUserViewController *createUserController = [[CreateUserViewController alloc] initWithNibName:#"CreateUserView" bundle:[NSBundle mainBundle] keyWrapper:keyChainWrapper];
createUserController.delegate = self;
[self presentViewController:createUserController animated:YES completion:nil];
}
In iOS5 the method for pushing new view controllers was really changed around quite a bit from iOS4 and Xcode 3. In summary, storyboards are now used to create your application view controller flow. Even though you may use standalone .xib files to build an application it is much less common in iOS5.
Anyway, the main method for pushing new view controllers onto the screen is done using segues. Check out this tutorial for an introduction: http://www.raywenderlich.com/5138/beginning-storyboards-in-ios-5-part-1
It does a good job on explaining how to create a storyboard and use segues. You can still present view controllers in code "the old way" but it is much much less common now with the introduction of these new technologies. There are also some absolutely awesome tutorials on iTunes U - search for CS193P. It's the Stanford Introductory class to Objective-C and programming for iOS. This should get you started and maybe help you think of a way to push your createUserController in a way more up to speed with iOS5.
UPDATE
I just wanted to add. If you configure your program to use storyboards and segues you can use the method performSegueWithIdentifier:sender: to perform the segue to your createUserController view if the proper conditions are met. See the Apple API for UIViewController for information on how to use this method.
I'm trying to create custom UIViewController with a UITableView, load the UIViewController using a xib file and add the view as a subview to another UIView.
The hierarchy is like this:
UIViewController
UIView << add custom UIViewController's view
UIView
UIView
Here's my xib view hierarchy and settings:
UIView
UITableView
Connection in IB:
File's Owner:CustomTableViewController
Outlets:
view connected to UIView
tableView connected to File's Owner
delegate connected to File's Owner
datasource connected to File's Owner
I have both UITableDataSource and UITableDelegate implemented.
When i tried to add the view as a subview, it crashed ...
- (void)viewDidLoad
{
[super viewDidLoad];
CustomTableViewController* controller = [[CustomTableViewController alloc] initWithNibName:#"CustomTableView" bundle:[NSBundle mainBundle]];
[self.viewContainer addSubview:controller.view];
}
What am i missing?
Sounds Like something is not retained that should be. Set up an exception breakpoint and turn on zombies to find it. See askers results above.
In my code below, CustomWindow is a subclass of NSWindow.
CustomWindow *window = [[CustomWindow alloc] init];
if (![NSBundle loadNibNamed:#"NibName" owner:window])
[window center]; // doesn't work
How do you get a pointer to control your XIB after you load it so you can do things such as centering the NSWindow (I mean the serialised one that resides inside the XIB)?
What am i doing wrong here?
You should be using an NSWindowController subclass. NSWindowController is specifically designed to do exactly what you want to achieve and solves several problems that you will run into if you load the nib directly using the methods of NSBundle. You generally should always use an NSWindowController subclass to manage windows.
Create a subclass of NSWindowController:
#interface MyWindowController : NSWindowController {}
#end
#implementation MyWindowController
- (id)init
{
self = [super initWithWindowNibName:#"MyWindow"];
if(self)
{
//initialize stuff
}
return self;
}
//this is a simple override of -showWindow: to ensure the window is always centered
-(IBAction)showWindow:(id)sender
{
[super showWindow:sender];
[[self window] center];
}
#end
In Interface Builder, set the class of File's Owner to be MyWindowController and connect the window outlet of File's Owner to the window object in your nib.
You can then display the window by doing this:
MyWindowController* controller = [[MyWindowController alloc] init];
[controller showWindow:self];
In my code below, CustomWindow is a subclass of NSWindow.
CustomWindow *window = [[CustomWindow alloc] init];
if (![NSBundle loadNibNamed:#"NibName" owner:window])
[window center]; // doesn't work
How do you get a pointer to control your XIB after you load it so you can do things such as centering the NSWindow inside the XIB?
“centering the NSWindow inside the XIB” makes no sense (you would center it on the screen), unless you mean centering the NSWindow object that is inside the xib, in which case, why are you creating another NSWindow (CustomWindow) object outside of the xib?
Remember that a nib (or xib) is an archive of objects. If you want to use a window that you have in your nib, you need to create an outlet to point to that window, set the class of the File's Owner to be the class where you've added the outlet, hook up the outlet in IB, and appoint the object with the outlet as the File's Owner by passing it to the owner: argument. That object, as the owner, will then be responsible for working with the window. It may be (usually is, in my code) the same object that loads the nib.
Also, init doesn't work on NSWindow; you must use initWithContentRect:styleMask:backing:defer: or initWithContentRect:styleMask:backing:defer:screen:. Using init would only be valid if you've implemented init yourself in CustomWindow, and used one of those two selectors for the [super init…] message.
You probably don't want to make your window the File's Owner. Normally you would pass self or some controller object there. Then if self or that controller object has a CustomWindow IBOutlet, it will get hooked up when you call loadNibNamed:. Check out this post for example code.
A XIB is a container for objects, it's not equal to a window. You can't center a XIB, you can only center a window contained in a XIB.
Also, the objects in the XIB are created when you load it. You don't pass an object as owner that then stands in for one of the objects in the XIB, you instead use IBOutlets to get references to the new objects created when loading the XIB and then you can interact with them.
The File's Owner object is a special object in XIBs, as it's the only object that is not created and that you can specify by passing it to loadNibNamed:owner:. It's your gateway between the XIB-created objects and your application.
Usually, the owner object is some kind of controller class. Set the File's Owner's class in Interface Builder to your controller class, then define some IBOutlets in the class, they will show up in Interface Builder on the File's Owner and you can connect your objects in the XIB to them.
Finally, when you pass your controller object to loadNibNamed:owner:, Cocoa will connect your IBOutlets to the newly created objects and you can use them to interact with them, e.g. to center a window in your XIB.