Replace subview of NSSplitview with custom view - macos

I still have a lot to learn with cocoa so I may have missed something obvious here. I have a custom view I would like to display in an nssplitview which replaces the current subview there.
I have a MessageView.xib file, and a MessageView .h/.m which subclasses NSView. I created a custom view instance for my main window (the one which contains the nssplitview) through Xcode 4's built in gui builder. I created an outlet to this instance of MessageView in my window's controller.
In my controller for the window when I want to swap out the subview for the splitview it runs this
[splitView replaceSubview:[[splitView subviews] objectAtIndex:1] with:viewMessage];
viewMessage is the outlet to the MessageView.
When this code is run the display of that subview changes to be blank. I'm not sure if there is something wrong with my custom view or there is some size issue. Is there something I need to do to fit the view into the split screen view or is my custom view just not displaying correctly? I have had a difficult time finding a tutorial on creating custom subviews with Xcode 4 so I'm not sure if something could be wrong with that. The custom view just has a label and a textfield in it.

Generally, you shouldn't need to replace NSSplitView's subviews with your own. Rather, you add your own custom view(s) as child views of the default subviews on each side of the divider. You can do this in code with addSubview:, but it's probably easier to just use Interface Builder in Xcode. Drag a "Custom View" into the splitview, then in the Identity Inspector, under Custom class, change the class to the name of your custom NSView subclass:
I think (off the top of my head, not tested), if you really do need to replace the default NSSplitView subviews with your own class, you can probably do it in Interface Builder using this same method, but selecting the default subview itself and changing its class in the inspector. This doesn't work for all AppKit classes, but it may work for NSSplitView.

Related

Does Interface Builder's new IBDesignable attribute work with NSButton and other controls in Cocoa?

I have managed to get the IBDesignable/IBInspectable attributes working with direct subclasses of NSView but not with a direct subclass of NSButton. This is causing me to question if in fact the Cocoa implementation is somehow limited to NSView only.
Almost every example on the web (and Apple WWDC 2014 Xcode video) use NSView and then drag a custom view component from the library onto the canvas (and then change its class).
Is it possible to use IBDesignable with subclasses of NSControl and NSButton etc...? I have seen many examples on the web using UIButton.
If it is possible, then what are you supposed to drag from the library onto the canvas? It doesn't make sense for it to be a "custom view". On the other hand, there is no "custom control" available.
To be clear, I can get the IBInspectable attribute to show up at design time; but any changes don't seem to live render at design time.
The workaround is to wrap any custom NSButton I want to create within an NSView (via composition) but this seems like a bit of a hack...
I started playing around with a custom NSButton and NSButtonCell.
Dragging a button from the library onto the canvas and changing its class and the cell class doesn't live render. I think this is because Interface Builder still does a lot of custom things to setup NSButtonCell.
What works fine for me is dragging a custom view from the library onto the canvas and set its class. For this to work you need to setup the cell inside NSButtons -initWithCoder:.
Also I found a sample from Apple with a layer-backed custom Checkbox.
You need to drag an NSButton onto the view, then set the Custom Class to your specific NSButton descendant. Not sure why it doesn't work when you start with an NSView.
What can give you a hint is that the NSButton specific attributes aren't in the "Attributes Inspector". Hence there must be some setup at the time you drag the control onto the view.

NSView added as subview doesn't show

I have a puzzling problem. Working on a cocoa app in mac os x 10.7.
My app main window contains a split view. In a certain use context in one of the subviews of the split view is loaded a custom view with some labels (nstextfield) and a split view (instantiating a view controller that loads a nib and getting view from that controller). Frame of the custom view is set to split view subview bounds and everything works fine.
Problem is that one of the subviews of the second split view should be loaded (same method: view controller-nib-view-frame/bounds) with a custom view containing a table view and a button, but in this case nothing shows. Everything is done the same way but last custom view is not visible. Any idea?
Thanks
(edit)
this is the code I use to instantiate controller for the view to be added, get the view, and add it as subview to a subview of the split view
- (void)loadSubview {
self.subviewToAddController = [[viewController alloc] initWithNibName:nil bundle:nil];
//nib name is coded in the controller class definition
[[self.subviewToAddController view] setFrame:[self.splitViewContainerSubView bounds]];
//container subView is an outlet
[self.splitViewContainerSubView addSubview:[self.subviewToAddController view]];
}
However I don't think the problem is in this code because if I ask the container subview for its own subviews I can see the new subview is present in the list. It just doesn't show. If I add it as a subview of the split view (a test a just made) or as subview of the subview of the most external split view it is correctly showed too (sorry for the confused explanation, I would need a diagram but in this moment I can't make it)
To elaborate more my doubt (I didn't want to misled so I didn't mention before) can't it be a problem of coordinates, so view is correctly loaded and added as subview but is not visible because hidden by something or showed out of visible area?
(update)
Sorry it took so long to post an update.
After more testing I found out the problem is related to autolayout. No idea what the exact problem is and how to solve it. I ended up turning it off for the nibs the were in troubles and use the old way to set interface objects position and size/resize. Not the best way but for now I can go on.
My best guess is that you didn't set the autoresizing masks of the view properly.

Create a custom view using a nib and use it in a window

I have been struggling quite a bit with this problem and I can't seem to figure it out by myself.
This is the situation:
I want to have a custom Nib-based view, with its own ViewController. I then want to add this view to a window. Therefore the window should contain my custom view.
So I go about and create a very simple example like this:
In Xcode 4 I create a new blank document-based Cocoa application
I create a new Cocoa Class which should extend from NSViewController (which causes the nib to be created along with the .h and .m file. The name of the created .h, .m und .xib file is FaceViewController for testing purposes it should only display text for now.
I open the NSViewController.xib in InterfaceBuilder and add a multi-line text component and place it in the custom view, which is already in the xib file. I make the text span the whole view.
In MyDocument.xib I also add a Custom View place holder, which should be replaced with my custom FaceView.
From this starting point on, everything I tried to include the created View + ViewController on my MyDocument.xib failed and the area where my text should be shown remains empty (just like the background of the underlying window.
So my question really is what I need to do, so that my FaceView gets loaded into the Custom View which I placed on MyDocument.xib. Here are some things which are unclear to me:
The custom View extends from NSView so I wanted to change this to my FaceView but the view does not exist as a class but is defined in InterfaceBuilder in the xib, do I need to set this to anything other than NSView?
Do I have to alloc, init the FaceViewController in code or is it enough to drag it into either of my two .xibs?
Can I use InterfaceBuilder to place my FaceView or do I have to do this programmatically?
I really thought that creating a custom view like this would be a piece of cake but it turned out quite the opposite so far, so any help would be greatly appreciated.
You can create your FaceViewController either by adding one to the MyDocument.xib or by creating it with alloc, init.
To place your FaceView you'll have to do it programmatically, you can use
[customView addSubview:[FaceViewController view]];
of if you want to replace the view
[customView replaceSubview:oldView with:[FaceViewController view]];

Switch XIB Views - Cocoa Mac

I am pretty new to coding and would like to know how to switch XIB views on a button click. IS there also anyway to add animation when switching?
Thanks,
Kevin
this is totally possible, but there are a few things you'll need to do. I imagine you are already familiar with connecting outlets to objects in your XIB, so the first thing you need to do is create the custom views in your XIB and connect them to outlets in your appDelegate. I suggest that one of the views be dragged into the window and one one be outside the window. That way, when the window loads, it already has one of your custom views as a subview. This just makes it easier to get started.
Then you're going to write an IBAction in the appDelegate and connect it to your button. Assuming that one of the custom views is already being hosted by the window, the IBAction should send a replaceSubviewWith message to the window's contentView animator like this [[window.contentView animator] replaceSubview:firstView with:secondView]; where firstView and secondView are the pointer/outlets that you declared and connected to the views in your XIB.
This is sending the animator proxy of the window's content view a message which tells it to replace the old subview with the new one. The reason for sending the message to the view's animator proxy (and not the view itself) is that the transition will be carried out with the deafult CATransitionAnimation. Because you want it to be animated, right?
The reason why you shouldn't remove one subview and then add another is because animating the removal of a subview is actually quite tricky and requires the implementation of the delegate method animationDidEnd. This is because executing an animation on a view that has been removed from the view heirarchy does not make sense. I don't know why Apple hasn't changed this, but for now it will be one of the enduring quirks of CoreAnimation.
Let me know if that helps. I am happy to clarify! And welcome to Cocoa!
An easy way to do this is to use a tabless NSTabView- you can lay everything out in IB so the pain is minimal.

NSView high-level overview?

I am trying to create a custom view and load it into a window.
In the end, I know there will be a few steps to follow, reflecting the key relationships that I am missing. I am hoping someone can explain, in a series of steps, how one creates a view object, view controller, associated .xib, and then loads it into a window (after clearing the current view).
And I mean the real nitty gritty of where to declare and initialize, what needs to be imported, everything. Because I am looking through every book I have and it is just not clear to my puny brain.
Thanks!
how one creates a view object, view controller, associated .xib, and then loads it into a window …
These are several things, and some of them conflict.
If you create a view in code, you don't need to (and shouldn't) also create it in a nib, and vice versa.
If you create a view controller to load the nib, you will be creating the view in a nib, so you definitely should not create the same view in code.
You do not need to create a view controller for most views. It is more common to have each controller own the entirety of exactly one window. The only time you need view controllers is when you manage a complex view hierarchy in a single window (most likely if you make your application single-window).
… (after clearing the current view).
There is no “current view” in Cocoa. You can have multiple windows, and each has a deep view hierarchy that you usually don't edit at run time. Swapping one view for another outside of some sort of tabbed UI is very unusual.
Creating a view object in code
Send the desired view class an alloc message and the returned view an initWithFrame: message (unless otherwise prescribed by the class's documentation). You will, of course, need to release or autorelease this view.
Creating a view object in a nib
Giving it its own nib (especially for view controllers)
Use the view-nib template in IB (New) or Xcode (Add File). If you create it in Xcode, don't forget to get info on it and make it localizable. If you create it in IB, you should save it into one of your .lproj folders; then it will already be localizable.
A nib created from those templates will contain one empty NSView. You can change its class and/or add subviews as described below.
Making it in an existing nib
Drag “Custom View” from the Library palette into the nib window, then set the view's class on the ⌘6 inspector.
You only do this for the top-level view in the nib. For its subviews, see below.
Putting the view into a window's view hierarchy
If the view should be the root of the window's view hierarchy (the window's content view)
Set the window's content view.
In IB, you can't change the window's content view. Instead, you change things about it—its class, subviews, etc. There is no reason to try to replace the window's content view with another view in IB.
If the view should be a subview of an existing view
The way to do this in code is to send the superview an addSubview: message.
If both views are in the same nib, create the subview and add it to the superview in the same act. Drag “Custom View” from the Library into the superview, not the nib window, then set the subview's class on the ⌘6 inspector.
(If you're customizing one of the standard Apple views, rather than making a completely original custom view, drag the standard Apple view you based yours on from the Library, then change its class to your customized subclass.)

Resources