NSView high-level overview? - cocoa

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

Related

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

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.

Issues when reloading views in an NSBox

I have a problem when reloading views. OSX is not as simple as IOS.
In MainMenu.xib I have an NSBox. As per Hillegrath, several views are stored in an array and when a segmented button is pressed the views are exposed. This works properly. My frustration involves revisiting these views when changes have been made that should cause them to redisplay new values through [self someAction]. Actions in two of the views alter (global)values that should propagate changes in the other views. I am using a window controller w/xib (MainMenu) to hold the box which contains the views. I also title the views in code.
The global ivar values change properly when required and the log shows that. However dependent operations do not occur when the view are revisited, ie, update view specifics.
What appears to happen is that loadView is not called when the various views are displayed, ** **. awakeFromNib and loadView operate correctly when each view is first displayed but not ever on redisplays. This implies that that the view may be hidden but viewDidUnhide has no effect.
The view changing code (from an SO MVC answer and Hillegrath) is
NSViewController *activeVC =
(NSViewController *) self.viewControllers[index];
// [_box setContentView:nil];
[_box setContentView:activeVC.view];
[_box setNeedsDisplay:YES];
From the copied code it can be seen that I have also attempted setting the active content to nil before setting a new view but, to no avail.
Any ideas, recommendations, notifications fail to work either but may not set up correctly.
Thanks
It looks like you’re confusing view loading with display. -awakeFromNib is received by a nib file object only when the nib file is loaded. Similarly, -loadView is received by the view controller only when its corresponding nib file is loaded. Since you are keeping your view controllers in an array, those view controllers do not get deallocated, hence their corresponding nib files are never unloaded whilst the array is alive. This is reasonable behaviour, but you must bear in mind that -awakeFromNib and -loadView are only executed once in this case because nib loading is executed only once.
Since it seems that you are manually populating the view in -loadView, you’ll also have to do that whenever you set that view as the box’s content view (assuming you’re not always updating the view whenever the model changes). For instance, you could have a -reloadData method in your view controller and both -loadView and your box’s content view swapping method would use -reloadData.
Alternatively, you could set your model object as the represented object of your view controller and bind the controls in the corresponding view to properties of that represented object. NSViewController exposes a representedObject property that’s convenient for bindings.
For the record, -[NSBox setContentView:] marks the box for redisplay, so you don’t need to send it -setNeedsDisplay:YES.
(and make sure _box actually points to the NSBox instance)

Replace subview of NSSplitview with custom view

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.

How to set size of UITableView inside Interface Builder?

Is it possible to change the frame of UITableViewController's UITableView frame inside Interface Builder? I want to make my table, which I instantiate in IB, a little bit narrower.
When you have create your tableViewController in IB, if you then go to the section on the right of the screen where all the options are set size to "freeform" then drag it to whatever you like. When you init it it will then get this frame. Unless it is within a container controller.
The best way to do what you are after is to create a viewController and add a table view to its view. Make the table view narrower that the main controller view and wire it up to an IBOutlet on your controller. If you then make your controller implement the and methods you will be able to use your table view exactly as you want to.
Hope this is of help to you, if this is unclear or you would like any extra help, let me know :)
You can change it in IB but unless you customize your view controller somehow, it will set it be full screen (module enclosing controllers like nav, tab bar, split view, etc.) That's just the way view controllers work.
From the Apple docs:
When a view controller is displayed on screen, its root view is
typically resized to fit the available space, which can vary depending
on the window’s current orientation and the presence of other
interface elements such as the status bar.
You can create another UIView to hold your table view and then have more control over the table view sizing, But then you won't be a table view controller and will have to implement some of the things that table view controller provides you, e.g., deselectRowAtIndexPath on viewDidAppear.

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

Resources