Designing UICollectionView cells in nib with Interface Builder (without storyboard) - xcode

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.

Related

Xcode Interface Builder - reusing a UIView

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.

How to make a modular NSViewController based disclosure view where all disclosed views are contained in their own NIB?

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]];

NSTableView makeViewWithIdentifier across nibs

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.

Why do UIViewControllers have xib files and UIViews do not?

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...

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