Xcode close view controller - xcode

I have two viewControllers parent and child, from parent I'm opening child viewController like this:
ClildVC *modal = [[ClildVC alloc] initWithNibName:nil bundle:nil];
modal.modalPresentationStyle = UIModalPresentationPageSheet;
[self presentModalViewController:modal animated:YES];
and when I return from child View to parent, I use this:
[self dismissModalViewControllerAnimated:YES];
I want, when returning to parent viewController it be refreshed (reloaded), like I open it first time.

in your parentViewController in .h and .m add method
- (void)refreshData
{
//refresh your data
}
in your childViewController type this
- (IBAction)backToParent
{
YourParentController *parent = (YourParentController *)self.parentViewController;
[parent refreshData];
[self dismissModalViewControllerAnimated:YES];
}

dismissModalViewControllerAnimated: is deprecated as of iOS6
You should use dismissViewControllerAnimated:completion: which was introduced in iOS5 in the child view controller after calling a data update on its parent view controller

You are initiating with no nib file and no bundle identifier.
So its looking for a non existent nib in a bundle that isn't there
either design the nib in IB (xcode 4) or storyboard (4.2 +) or programatically by using the designated initialiser for the modal view controller.

Related

NSSplitViewController/NSSplitViewItem support in XIBs

Is there support for NSSplitViewController/NSSplitViewItem for XIBs? I see only NSSplitView
Can I just drag&drop NSViewController and subclass it as NSSplitViewController? How do I add NSSplitViewItem that it mostly works out of the box?
I can easily see support for them in storyboards.
The split view controller is not part of the object library for xib files. The easiest way to use split view controllers is to use storyboards.
If you are unwilling to use storyboards, your best option is to create a subclass of NSSplitViewController and select the checkbox to also create a xib file.
Add a split view to the split view controller xib file. Write code to load the xib file to set up the split view controller.
UPDATE
Look at the NSNib class reference for information on loading a xib file. The File's Owner of the xib file is your NSSplitViewController subclass. You may be able to use that information to set the split view controller. The worst case scenario is that you have to write code to load the split view from the xib file, set the split view controller's split view to the split view you loaded, and add the split view items to the split view controller. See the NSSplitViewController class reference for more information.
Yes it's possible. But it needs some wiring.
First add a custom subclass of NSSplitViewItem and expose viewController property as IBOutlet. Compiler will throw a warning so don't forget to mark property as dynamic.
#interface MySplitViewItem : NSSplitViewItem
#property IBOutlet NSViewController *viewController;
#end
#implementation MySplitViewItem
#dynamic viewController;
#end
In your XIB add 3 NSViewController objects. One of them change to custom class NSSplitViewController. It is important to note that one should NOT add NSSplitView. Wire NSViewControllers to it's views. Also add 2 objects and add custom class of MySplitViewItem which has exposed the viewController and wire it.
Last step. It is important to set property splitItems of NSSplitViewController before the views are loaded! Otherwise you are caught with NSAssert macro.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSNib *nib = [[NSNib alloc] initWithNibNamed:#"Empty" bundle:nil];
NSMutableArray *test = [NSMutableArray new];
NSMutableArray *splitItems = [NSMutableArray new];
NSSplitViewController *controller;
[nib instantiateWithOwner:self topLevelObjects:&test];
for (id object in test) {
if ([object isKindOfClass:[NSSplitViewController class]]) {
controller = object;
}
if ([object isKindOfClass:[NSSplitViewItem class]]) {
[splitItems addObject:object];
}
}
[controller setValue:splitItems forKey:#"splitViewItems"];
[[self window] setContentViewController:controller];
}
Here is a proof that everything is wired correctly. Note that I did not touch delegate in XIB and it is wired. Magic, I know.
PS: XIB has to be set to prefer Coder + auto layout.
Why do I prefer XIB? Because we can create larger XIB which doesn't suffer from data isolation (Easily can do bindings across NSViewControllers).
I have also experimented to add splitViewItems in viewDidLoad or setView or awakeFromNib: in custom subclass of NSSplitViewController (with exposed NSSplitViewItem properties). If someone finds solution here it will be greatly appreciated.
Solution that requires code only:
- (NSSplitViewController *)profilesSVC
{
if (!_profilesSVC) {
NSSplitViewController *splitVC = [[NSSplitViewController alloc] init];
ProfilesViewController *profilesVC = [[ProfilesViewController alloc] initWithNibName:#"Profiles" bundle:nil];
NSSplitViewItem *leftItem = [NSSplitViewItem splitViewItemWithViewController:profilesVC];
[splitVC addSplitViewItem:leftItem];
ProfileViewController *profileVC = [[ProfileViewController alloc] initWithNibName:#"Profile" bundle:nil];
NSSplitViewItem *rightItem = [NSSplitViewItem splitViewItemWithViewController:profileVC];
[splitVC addSplitViewItem:rightItem];
_profilesSVC = splitVC;
}
return _profilesSVC;
}
I too wanted to add a splitView controller to my projet (macOS app) that doesn't use storyboards.
As it turned out, this was rather easy (in XCode 12.4).
As suggested, one has to to add NSViewController objects to the xib and wire each view property to the corresponding 'pane' (subview of the split view) in interface builder.
Then create a subclass of NSSplitViewController (no need to create a xib file).
Add a third NSViewController object to the xib and change its class to your subclass. Then wire both it's view and splitView properties to your splitView. It doesn't load any view if you just wire the splitView property.
Using a subclass of NSSplitViewController may not be required, but it's convenient as you may set the splitViewItems within viewDidLoad (below). Since this object is (automatically) the delegate of the splitView, you can also override delegate methods if you wish.
That object should have outlets leading to the NSViewController objects which you previously wired to the panes in IB.
I set two outlets named leftController and rightController.
My awakeFromNib method looks like this (sorry, I don't use swift):
- (void) viewDidLoad {
self.splitView.wantsLayer = YES; // I think this is required if you use a left sidebar with vibrancy (which I do below). Otherwise appkit complains and forces the use of CA layers anyway
NSSplitViewItem *left =[NSSplitViewItem sidebarWithViewController:leftController];
[self addSplitViewItem:left];
NSSplitViewItem *right =[NSSplitViewItem splitViewItemWithViewController:rightController];
right.minimumThickness = 420;
[self addSplitViewItem:right];
}
VoilĂ !
However, I get crashes if I set thick dividers in IB as appkit calls splitView:shouldHideDividerAtIndex too early, when there is apparently no divider yet. Worse, it may pass a negative divider index (!!). But you may override the method and act accordingly and I have no issue with thin dividers.

Trying to make a tabbed browser

I'm having a problem with cocoa, when I run the app the tabs get added as expected, but all the web views take the same string from the url field.
Basically, if I go to google on one tab, it goes to google on all of them.
Is there any way to make only the web view on the selected tab respond, and not on the others?
Here is the code:
- (IBAction)newTab:(id)sender {
NSTabViewItem *item = [NSTabViewItem new];
[item setView:_webView];
[item setLabel:#"New Tab"];
[_tabView addTabViewItem:item];
}
It looks like you're creating a new tab and then moving your WebView to it. To create a new web view for each tab, you have some options but one is to use NSViewController:
If you create the web view in the same xib as the tab view, move it to a separate xib. Change the class of the owner of that xib to NSViewController.
When adding a new tab in your code, load the xib (assuming it is named WebView.xib):
- (IBAction) newTab: (id) sender
{
NSTabViewItem *item = [[NSTabViewItem alloc] init];
NSViewController *viewController =
[[NSViewController alloc] initWithNibName: #"WebView" bundle: nil];
WebView *webView = [viewController view];
[item setView: webView];
[_tabView addTabViewItem: item];
[_webViewControllers addObject: viewController]; // Store the view controller, remove when the user closes the tab.
}
Here's a tutorial on view controllers: http://comelearncocoawithme.blogspot.fi/2011/07/nsviewcontrollers.html

Interface-Builder: "combine" NSView-class with .xib

I'd like to set up a custom NSView in Interface-Builder, but I don't get it to work for OSX.
In my ViewController's .xib, I added a custom view and set the Class to MyCustomView. I created MyCustomView.h, MyCustomView.m and MyCustomView.xib.
In MyCustomView.xib, I set the Class to MyCustomView as well. In MyCustomView.m, - (void)awakeFromNib is called, but - (id)initWithCoder:(NSCoder *)aDecoder and - (id) awakeAfterUsingCoder:(NSCoder*)aDecoder aren't.
What I'd like to achieve is that in my ViewController, the view I added is "filled" with the view I set up in MyCustomView.xib. What's the best way to do that?
EDIT: I don't think I was clear enough...
I've got my ViewController containing a Custom View called MyCustomView.
This view should be of type MyCustomView, where
MyCustomView.h
MyCustomView.m
MyCustomView.xib
exists. I already set the File's Owner of MyCustomView.xib to MyCustomView and I already set the CustomView in my ViewController to MyCustomView - but it doesn't work.
If I do it with
- (void)awakeFromNib {
NSString* nibName = NSStringFromClass([self class]);
NSArray* topLevelObjects;
[[NSBundle mainBundle] loadNibNamed:nibName
owner:nil
topLevelObjects:&topLevelObjects];
NSView* view = topLevelObjects[0];
[view setFrame:[self bounds]];
[self addSubview:view];
}
I only get a view of type NSView, not MyCustomView... Is there no easy way to tell the ViewController.xib that it's a MyCustomView?
EDIT 2: I uploaded a simple project
At https://dl.dropboxusercontent.com/u/119600/Testproject.zip you find a simple project with the MyCustomView (not in a ViewController but in the window.xib) - but it doesn't show the button which is in MyCustomView.xib. I'd like to achieve exactly that - what's the simplest, best way?
EDIT - apologies, my existing answer failed to take into account the need to connect outlets and actions. This way should do it...
Given the files...
MyCustomView.h
MyCustomView.m
MyCustomView.xib
In MyCustomView.h
declare IBOutlets for your interface elements. You need at least one, to hold a pointer to the top-level view in the xib file
#property (nonatomic, strong) IBOutlet NSView *view;
In MyCustomView.xib
ensure that there is only one top-level view
set file's owner class to MyCustomView in the Identity Inspector
ensure that the top-level view is set to the default NSView class.
now you can connect IBOutlets declared in MyCustomView.h to interface objects in the xib file. At very least you need to connect up the top-level view to your view outlet.
In MyCustomView.m:
- (id)initWithFrame:(NSRect)frame
{
NSString* nibName = NSStringFromClass([self class]);
self = [super initWithFrame:frame];
if (self) {
if ([[NSBundle mainBundle] loadNibNamed:nibName
owner:self
topLevelObjects:nil]) {
[self.view setFrame:[self bounds]];
[self addSubview:self.view];
[self.myCustomButton setTitle:#"test success"];
}
}
return self;
}
In your window's xib file, add a custom NSView and change it's class to MyCustomView.
in OSX prior to 10.8 the method loadNibNamed was a class method - use it instead if you need backwards compatibility, but it is deprecated now:
[NSBundle loadNibNamed:#"NibView" owner:self]
Note that MyCustomView.xib's view is NOT MyCustomView's view, but the sole subview of it's view (this is similar to the way a tableViewCell possesses a single contentView).
In the project sample you have posted, you need to make the following changes:
in MyCustomView.h
. add an NSView property
in MyCustomView.xib:
. change the top-level view from MyCustomView custom class to NSView (the default)
. set the File's Owner to MyCustomView.
. connect IBOutlets from File's owner's view and myCustomButton to interface view and button
. for testing make the view a lot smaller and push the button up to the top right (you won't see it in your window as it is here)
in MyCustomView.m:
. replace all of your implementation code with the initWithFrame method here
In order to load a custom subclass of a view or a control (or any other class that can be used in Interface Builder for that matter), you need to add the base version (NSView in your case, and as you have done), then select that object in the window and go to the Identity Inspector (Cmd-Opt 3).
Instead of the pre-defined value for Class (NSView, in your case), type in the name of your custom subclass. VoilĂ !
A small detail related to the IB UX is that you'll probably have to move the focus from the input field in order for that change to be registered when you build and run the app.

Switching of one view to another view in single customViews

I am having of one CustomView in one xib and two different views in two different xib's.
I want to display those two view one after the other in one single CustomeView.
I have an object of NSView which is connected to CustomView in .xib file
#property (retain) IBOutlet NSView *mySubview;
#property (retain) NSViewController *viewController;
Method to open one View is:
-(IBAction)selectBookTicket:(id)sender
{
//setting status label to nil
_viewController=[[NSViewController alloc] initWithNibName:#"BookTicket" bundle:nil];
//loading bookTicket xib in custom view of NormalUserWindow
[_mySubview addSubview:[_viewController view]];
}
Method to open another view in same CustomView is:
-(IBAction)selectTicketCancellation:(id)sender
{
_viewController=[[NSViewController alloc] initWithNibName:#"CancelTicket" bundle:nil];
//loading CancelTicket xib in custom view of NormalUserWindow
[_mySubview addSubview:[_viewController view]];
}
When I am opening any view for first time its displaying properly in CustomView, but when I am trying to open second view or same view for second time then its get overlapping on previous opened view.
I tried
[_mySubview removeFromSuperview]
It's removing 'mySubview' completely, I mean what ever the view is currently loaded it is get removing but it's not allowing to display any views after that '[_mySubview removeFromSuperview]' get executed.
You must remove only the view that you have added from the view controller. Try the following code instead.
-(IBAction)selectTicketCancellation:(id)sender
{
[[_viewController view] removeFromSuperView];
_viewController=[[NSViewController alloc] initWithNibName:#"CancelTicket" bundle:nil];
//loading CancelTicket xib in custom view of NormalUserWindow
[_mySubview addSubview:[_viewController view]];
}
Executing [_mySubview removeFromSuperview] will remove your host view (i.e; the one that is displaying views from other view controllers) from view hierarchy and this explains the "not allowing to display any other subviews part".

Cocoa: Adding a subview to a view from a different class and nib

If i have two nibs with several views, is there a way for me to use the addSubview: method between them? What I would like to do is take a view from one of the nibs and tell it to add a subview that would be a view in the other nib file.
The reason I have them in separate nibs is because the subview from the second nib will be added several times, using the same template but different parameters.
Yes, you can add a view in one nib as a subview to the view in another nib.
You need to create a NSViewController object which will own the child nib. So that as soon as you initialize the view controller the nib associated with it is loaded. Now you can use the view property of the controller and add it as a subview to any other view.
The code below will help you understand better:
YourViewController.m
-(id)init
{
self = [super init];
if(nil != self)
{
[NSBundle loadNibNamed:#"myNibName" owner:self];
}
return self;
}
YourOtherClass.m
-(void)addYourViewControllerViewAsSubview
{
YourViewController *yvc = [[YourViewController alloc] init];
[yourOtherViewOutlet addSubview:yvc.view];
}

Resources