When I create a new UIViewController in xcode, it offers to make me an associated nib for the interface. However, when I create a UIView, it does not. If my understanding of MVC is correct, views should really be the parts that contain the interface elements (i.e. the nib) while view controllers are the parts that hook the functionality to the views they control.
I'm sure I'll be able to get it working together either way, so this is more of an exploratory question.
This seems like a case where I'm missing some fundamental understanding of how these two parts should be used. What am I missing?
Because UIView is usually not used in such way.
However How do I associate a nib (.xib) file with a UIView?
The answer I eventually got that satisfied my interest was roughly this:
The job of a view controller is to manage a view hierarchy. Interface Builder is an excellent tool for creating view hierarchies. Xcode offers to create a .xib when you create a new view controller because chances are high that you'll want to load the controllers' views from a .xib.
.xib files aren't necessarily meant to hold every UIView (or subclass) that goes into the view, just a general outline of views that don't change during the life of the view. The other UIViews are much easier to create programmatically since they change often.
I had a similar confusion. Basically (according to the MVC) the View is contained inside the Controller. In the iPhone this means that UIViewController has a property called 'view' which is a UIView instance.
A XIB file (and this is not mentioned often) is a serialised UIView instance. It is roughly an XML sub format which represents a UIView with all its subsequent views. So when you create a UIViewController, a UIView is created in the form of a XIB and bounded to that controller.
A new UIView therefore does not have a XIB because they are essentially the same thing...
Related
Is it not possible to reuse a UIView with constraints etc, from one view to another. I have tried:
Note: I am not in storyboard, but using xib file in a framework that I am creating - therefore I am using only UIView from xib files.
Tried to reference view in the same xib file.
#IBDesignable incl all custom classe setup.
I am only looking for reusing a UIView incl. constraints.
Regards
You can't do this through the storyboard because constraints are not made to be generalized through viewcontrollers. If you copy and paste constraints, they'll look for the exact views they used to constrain which won't be there anymore.
However if you have multiple views that look similar, then my suggestion would be to make a base class where you layout the view once on the storyboard and then subclass it. Or, your other option would be programmatic.
I am trying to design a custom UICollectionViewCell prototype (in Xcode 5.0.2), however Interface Builder doesn't let me add a cell to my UICollectionView while designing a nib. I can set the number of items (cells) and Interface Builder creates and displays cells perfectly if I'm using storyboard, but I can't add a cell to my collection view in a nib. I've tried:
Drag and dropping collection view cell into collection view manually from the object library. (fails: doesn't let me drop the cell anywhere in my view)
Creating my collection view with cells in storyboard and copy-pasting the whole view into nib. (fails: collection view is copied but the cell is gone)
Creating my collection view with cells in storyboard, opening the storyboard as source code, finding my collection view cells, copying the relevant XML, opening my nib as source code, pasting it inside my collection view in XML. (fails: unable to open the nib in Interface Builder, it gives errors. When I remove the cell from source code, it opens again. Do not try this if you don't know what you are doing.)
I've also seen several questions about the same issue:
Is it possible to create prototype cells in Interface Builder without story boards?
Custom Header in UICollectionView with Interface Builder without Storyboard
Prototype Cells in a nib instead of a storyboard
They all point out to doing them programatically and/or using another nib for the cell. I know how to do them, but is there any way to design the collection view cell, inside a collection view inside the same view in a nib, just as in storyboard? Why doesn't Interface Builder let me do that in nib where it allows (and even encourages) perfectly using storyboard?
The simple answer is no, this cannot be done. Think of a storyboard as a collection of XIBs. A XIB simply defines the facets of a particular view so that it can be constructed at runtime.
Regarding collection views and their storyboard implementations, it's important to understand that a storyboard allows for nesting of viewcontrollers and defining collection views with their XIBs because that keeps the fundamental paradigm of storyboards coherent. Since a storyboard is the means of defining the "story" or scene of an application it is only natural that it allows for the declaration of the reusable views for use inside a collection view.
The same cannot be said for XIBs because the fundamental idea behind XIBs is in reusability. This will allow a collection view defined in a XIB to have any cells used with it as long as the controller registers these classes with the collection view. This way you get the benefit of reusability as another controller can use the same XIB and register different cells etc.
So I think it would be far more confusing to allow for the declaration of the supported cells of a collection view inside a XIB since that breaks the single responsibility principle(if it can be called that) that XIBs aspire to.
Your best solution would be to define a custom collection view subclass that registers the relevant cells on instantiation, and then use this class in your XIB.
In real apps the view hierarchy can be complex at it really helps to be able to put different views in different nibs. I am following InfoBarStackView example project they give a really nice example of how to use the new NSStackView class which hosts different views. They make a DisclosureViewController which is responsible for hosting a content view
changing the it's size so that is can go from a open to closed state.
Here is a simplified example. What we have are two separate nibs:
DisclosureViewController
ContentViewController
What is the simplest way to load the content view inside the placeholder view of the disclosure view? Is it possible to do this only in IB only?
Currently my AppDelegate has a lot of redundancy because it need to hold references to both view controllers. I wondering if there is a way of simplifying the situation? For this simple example, the AppDelegate would load from the two different nibs using code like this,
// In AppDelegate.m
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[_disclosureView1.view replaceSubview:_disclosureView1.placeholder with:_contentView1.view];
[(NSView*)_window.contentView addSubview:_disclosureView1.view];
}
You can have NSViewControllers in your main XIB corresponding the the views you want in your NSStackView — in XIB’s inspector you can set the name of the other XIBs they should load to get their ‘view’s.
Assuming you had IBOutlets onto these viewControllers in your main XIB, it’d be as easy as calling:
/* load the views into the stack view */
_stackView = [NSStackView stackViewWithViews:#[_viewControllerOutlet1.view, _viewControllerOutlet2.view, _viewControllerOutlet3.view]];
I have a similar question to Cocoa - View-Based NSTableView, using one cell in multiple tables, amplified by
Apple's own docs for makeViewWithIdentifier:owner:
"Typically identifier is associated with an external NIB in Interface Builder and the table view will automatically instantiate the NIB with the provided owner."
This seems to imply that you should be able to store the NSTableCellView in a separate nib from the nib containing the NSTableView. However, in my experimenting, I have only ever been able to obtain cells which are contained within the tableview I'm calling this on. I.e., if I cut and paste my cell into a new .xib file, the tableview can no longer find it. What am I doing wrong, or is this actually impossible and I am somehow misreading Apple's docs?
Use - (void)registerNib:(NSNib *)nib forIdentifier:(NSString *)identifier to register a nib to be used with a cell identifier.
If it doesn't work you're probably registering the nib after the tableView data has been loaded. Use [tableView reloadData] afterwords to be sure it's not a timing issue.
I just ran into this problem and I think you cannot use makeViewWithIdentifier:owner: when you're using a dedicated Nib to populate View-Based Tables.
The problem has to do with file owners (ie. view controllers). makeViewWithIdentifier:owner: seems intended to be used with "self" as the owner for simple custom views.
Generally if you have a separate nib for the custom view with outlets, you're going to want a separate view controller too. Otherwise, if your custom view has an outlet and the table displays many custom views, which outlet are you referring to from the "self" table view owner?
So in my test, I've got the AppDelegate as the delegate/datasource of the Table View. I have a CellView.xib, and CellViewController.h/.m with outlets to the interface. Then in my tableView:viewForTableColumn:row: delegate method I have this code:
SSCellViewController *vc = [[SSCellViewController alloc] initWithNibName:#"CellView" bundle:nil];
return vc.view;
What you lose is the cell re-use that happens automatically with makeViewWithIdentifier:owner:. To implement that yourself, you'll also likely have to deal with managing the many view controllers you've created.
I might still be missing something, as I'm coming to OS X development after years of only doing iOS work.
First, some background: I am trying to implement a master-detail interface in Cocoa (for OS X). That is, I have a window with two NSTableViews that display two different types of objects. For this question, let's say they are warehouses and packages (to pick an example that is analogous to my actual problem.) Selecting a row in the first table view (on a warehouse) will display a list of packages which belong to that warehouse in the second table view. For the model part, I currently have an NSMutableArray called warehouses of warehouse objects, and each warehouse object has an NSArray of package objects. One thing to note is that the warehouses variable is modified after the NIB files are loaded, so the NSArrayController has to be notified.
Now, I've tried to organize it so that the "master" is in its own view object, and the "detail" is in its own view object. This means that there are three NIBs: a WarehousesView NIB, WarehouseDetailView NIB, and a MainWindow NIB.
The WarehousesView NIB contains an instance of a WarehousesViewController (subclassed from NSViewController) and the view itself.
The WarehouseDetailView NIB contains an instance of WarehouseDetailViewController and the view itself.
The MainWindow NIB contains the main window, an instance of MainWindowController, and an instance of both WarehousesView and WarehouseDetailView. The window itself contains an NSSplitView, and the views of the split view are connected to the corresponding view instances in the NIB file.
That brings me to the first half of my question:
1) Is this a good way of splitting up the application views for a Cocoa application? To me it makes sense, because at a later point more details about the warehouse besides a list of its package inventory might be added to the WarehouseDetailView.
It's an important question, because everything works just fine if I skip creating views, putting all controls in the window directly and put everything else, including NSArrayController instances corresponding to Warehouses and Packages, into the same NIB file. I don't need to ask the second half of the question if I shouldn't even be doing it this way.
The second half of the question is basically:
2) Where should I place the NSArrayControllers corresponding to Warehouses and Packages if I split it up as described above so that the master-detail interface still works? Currently I am using Cocoa bindings, so somehow the content array of the Warehouse NSArrayController needs to bind to my warehouses array, and the content array of the Packages NSArrayController needs to bind to the selection of the Warehouse NSArrayController
I've tried a few things, and I couldn't get anything to work completely. Specifically, I've tried putting the NSArrayController for Warehouses into the WarehousesView NIB and the NSArrayController for Packages into the WarehouseDetailView NIB. The problem with this approach is that I cannot figure out a way to bind the Package NSArrayController to the selection of the Warehouse NSArrayController. The other thing I've tried is (1) putting both NSArrayControllers into the MainWindow NIB, (2) connecting those NSArrayControllers to IBOutlets in the MainWindowController, then (3) passing those variables to their respective view controllers via their constructors, (4) exposing them as properties in the view controllers via KVC, and (5) binding the necessary table columns in a view to the array controller through the File's Owner. The result was that nothing appeared, but there were no errors either. If one of these approaches is the preferred way to do it, I can give more details to help see if I am doing it incorrectly.
Thanks in advance!
Edit: I did look at this related question, and they seemed to be using separate instances of NSArrayControllers for each NIB file if I understood it correctly, and that didn't seem to make sense from a design point of view, but perhaps I am wrong?
Part 1: You can certainly do this. I'd say it's a matter of preference. Personally, then, if the views were going to be displayed simultaneously in a window, I would keep them in the same nib.* Modularity is also a good thing, though.
Part 2: You can put the array controllers wherever you like, really. The only thing you need to worry about is getting each object the references that it needs to the information you want it to have. If you want my 2¢, I'd say put each in the nib with the view its contents will be displayed in. That'll make your detail view setup more difficult, but it continues the modularity you seem to be going for.
You have to remember that every object in the nib is a real instance. The nib allocates and inits them for you; if you put a MyClass object in one nib, and a MyClass object in another nib, those are two different objects. This is sometimes a tricky thing about nibs: it's really convenient to have instances automatically created for you, but it also means some fiddling around with references when you want to do things across nibs.
It sounds like you put instances of WarehouseView and WarehouseDetailView into both your individual nibs and MainMenu.nib and expected them to be the same objects. It won't be so. You have to link objects in the nibs to objects that they already know about. You'll have to work this out for your particular situation.
I don't know where your model is stored, or how you're getting the nib loaded. Whichever object it doing that loading, though, will likely be your link between the individual nib and the rest of the app. This is what the File's Owner proxy object in nibs is for -- it gives you a place to hook up objects in the nib to code that they wouldn't otherwise know about.
*: If you find it easier to layout the views if they are not enclosed in the split view in IB, you could set them up on their own: put the custom view objects in the MainMenu.xib window and you can open each view in its own IB window (though it will not be in a window in the app). Then set the split view's subviews in something's awakeFromNib.