I would like to preserve my application's state in XCode 6 and iOS 8. However, all the information I can find refers to storyboards and restoration identifiers, but I am not using storyboards.
I am sure I could make sense of all these would it not be for one major thing: I can't seem to find the field for the restoration ID for View Controllers in Xcode 6. I have found the ones for views, but every tutorial makes it clear that I have to make sure to tag the controller, not the view!
Any help is highly appreciated :)
Thx in advance
restorationIdentifier is a view controller property as well as a view property.
From the View Controller Class Reference documentation:
restorationIdentifier The identifier that determines whether the view controller supports state restoration.
This property indicates whether the view controller and its contents should be preserved and is used to identify the view controller during the restoration process. The value of this property is nil by default, which indicates that the view controller should not be saved. Assigning a string object to the property lets the system know that the view controller should be saved. In addition, the contents of the string are your way to identify the purpose of the view controller.
State restoration is hierarchical in nature. As I'm sure you know, if you don't set the view controller's restorationIdentifier property, its view will not be saved, even if its view's restorationIdentifier property is set.
Update:
You can programmatically set your view controller's restorationIdentifier when you initialize it:
- (instancetype)initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle
{
self = [super initWithNibName:nibName bundle:bundle];
if(self)
{
self.restorationIdentifier = #"MyViewControllerID";
}
}
Since you're not using Storyboards, you can't inspect your view controller's properties. This is where the view controller's restorationIdentifier property is displayed.
Related
I'm using Storyboards for the first time creating a master/detail view for a simple object.
Both view controller items of the split view have their own view & NSObjectControllers. The controller for the table view is a NSArrayController (which shows all instances of the Entity). The controller for the detail view is an NSObjectController, which shows just the selected instance.
When I click an object in the table, I'd like the detail to show up in the detail view. So I believe my goal here is to bind the 'contents' of the detail object controller to the 'selection' of the master array controller. However with storyboards I cannot cross view controller boundaries (can't create the IB 'link'. aka: can't bind cross VC).
Whats the best practice for doing this?
So far all I can think to do is manually set all this up in one of the controllers (either the root controller, window controller or perhaps the master table view controller) - but that seems counterintuitive. That seems to defeat the purpose of being able to lay out this stuff within storyboards in the first place, and my controller of choice would then have to know about the other controller specifically.
I must be missing something!
I've developed an app for Mac OS X Lion using its new view-based NSTableView, but as I want to port the whole app to Snow Leopard I'm trying to figure out the best way to emulate such a tableview. So far I've created a NSCollectionView and everything is fine, except for the fact that I can't get the index of the view from which a button click event is triggered.
In Lion I have the following function:
- (IBAction)buttonClick:(id)sender
so I can get the index of the view inside the tableview using a method (I can't remember its name) like
- (NSInteger)rowForView:(NSView *)aView
with aView being the sender's superview, but I couldn't find something similar for the collection view ... The only "useful" method seems to be
- (NSCollectionViewItem *)itemAtIndex:(NSUInteger)index
(or something like this), but this can't help me as it returns a NSCollectionViewItem and I can't even access it knowing only the corresponding view!
Within buttonClick, try this code:
id collectionViewItem = [sender superview];
NSInteger index = [[collectionView subviews] indexOfObject:collectionViewItem];
return index;
Hope this helps :)
Geesh! Both of those approaches have issues. I can see how the first on may work, but note that the "collectionViewItem" is actually the view, NOT the collectionViewItem, which is a view controller.
The second way will not work, unless you subclass the button and put in a back link to the collectionViewItem. Otherwise, your view does not know what collectionViewItem controls it. You should use a selector binding to the collectionViewItem's representedObject instead, to get the action to the correct object in your array.
How about something like:
id obj = [collectonViewItem representedObject];
NSInteger index = [[collectionView contents] indexOfObject:obj];
As I suggested here: How to handle a button click from NSCollectionView
I would do it like this (because the button you want to press should be coupled with the corresponding model, therefore the represented object):
Add a method to the model of your collectionViewItem (e.g. buttonClicked)
Bind the Button Target to Collection View Item
While binding set model key path to: representedObject
While binding set selectorname to: methodname you chose earlier (e.g. buttonClicked)
Add protocol to your model, if you must tell delegate or establish observer-pattern
use NSArrayController for binding to NSCollectionView,
use collectonViewItem.representedObject to get a Custom Model defined by yourself.
save and get index in your custom model.
That's works for me.
I'm developing a Mac Application. The application has a common source view on the left and a detail view on the right which is the main part of the whole window.
It's like a Master-Detail relationship, but each element in the source view require another detail view. In fact, I have designed a specific NSViewController for each element in the source view.
If I'm switching between these NSViewControllers, that means If I select another element in the source view, I remove the current view and add the view of the newly selected NSViewController. Everytime I change the NSViewController, its state will be lost. When the user comes back to that NSViewController, he has to start over.
My question now is: How can I save the state of the NSViewController, so that I can switch between these without losing its states and can continue where I have left?
Two considerations about your problem:
Keep model data in model classes. This means that you can always recreate a view controller and set its represented object provided the model classes have kept the changes made via the view controller. When you need to instantiate a view controller, set its represented object to (a representation of) a model class.
When removing a view from its superview, you do not necessarily need to release its corresponding view controller. Instead, you can keep strong references to all view controllers in your window controller/application delegate, so no state is actually lost.
Use NSArchiver. Implement archiving/unarchiving in your dealloc/init methods and store each view controller's state in a file named after the class (if you have one item per view controller policy). Otherwise think of some simple naming convention and use it.
After I drag a controller object to the document window, how do I give it a Boolean property?
Assuming you want the ability to expose and edit the property values of your custom controller as attributes showing up in IB's inspector, you'll need to write your own plugin which tells IB what the inspector should look like. This really can't be answered briefly.
Here's Apple's reference on IB Plugins:
http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/IBPlugInGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40004323-CH1-SW1
And here's the inspector portion:
http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/IBPlugInGuide/CreatingInspectors/CreatingInspectors.html#//apple_ref/doc/uid/TP40004323-CH6-SW1
If you truly just want to add a property to the controller's interface, you do this in Xcode (modifying the interface and implementation accordingly) and IB will pick up the changes automagically.
In the case of a checkbox, the checkbox itself is either checked or not. That state stores the boolean value instead of the controller (unless you wish it.)
To have the app undertake an action upon clicking it, think of it as a button instead of a data display and link it to an action method in the view controller. That's the simplest and old school way of doing it.
To use binding, you need to bind the checkbox's value attribute to a controller. Usually for binary values its an object controller. So, in IB, drag an Object controller to your nib window and bind it to your data source. Then, drag a checkmark button to the interface. In the checkmarks binding inspector. Set "value" binding to the name of the object controller, controller key to selection, keypath to the name of the data source attribute and provide a value transformer if needed.
(Abstract: bindings work in code, but not in IB)
I have a window managed by a NSWindowController. To the left of the window is a source view. To the right is a table view showing the elements of the currently selected source.
I have set up a NSTreeController within my window XIB. I want its contents to be used for the source view. It's selection will drive the table view.
I am trying to split this up using NSViewControllers. One view controller will load a NIB containing the source view. Another view controller will load the table view.
Seeing that I need access to the NSTreeController within the source view controller, I have set it to be the view controller's representedObject. (Actually for this setup to be done by the time awakeFromNib is called on the view controller, I have turned representedObject into an IBOutlet).
All works fine when I wire my source view up in code:
[outlineView bind:#"content"
toObject:sources
withKeyPath:#"arrangedObjects"
options:nil];
[outlineView bind:#"selectionIndexPaths"
toObject:sources
withKeyPath:#"selectionIndexPaths"
options:nil];
[[outlineView tableColumnWithIdentifier:#"Title"] bind:#"value"
toObject:sources
withKeyPath:#"arrangedObjects.title"
options:nil];
I am however unable to reproduce this using Interface Builder. Thing is, here the "controller key" textfield is grayed out. Thus I bind column's "value" to the file owner using a model keyPath of "representedObject.arrangedObjects.title". This does not show the desired behavior. Actually an exception is thrown: -[NSProxy doesNotRecognizeSelector:_mutatingNodes] called!
How can I use representedObject in IB?
Can I create a controller in IB which acts as proxy to representedObject?
Could I set-up a tree controller in the source view XIB which during NIB loading gets swapped out for the representedObject?
I moved away from using representedObject. It appears that is meant only for model objects.
I now pass in my tree controller using a custom outlet. I continued setting up and tearing down the bindings in code.
I’ve similar issues when I try to pass a reference to an object controller (NSTreeController in my case). I don’t think this is how Apple wants you to use their KVO-compatible controllers. The exceptions look like they’re XIB-unarchiving & timing-related.
The trick is not to pass the controllers, but to pass the underlying data and keep the selection in sync.
This way you can set up your bindings in a storyboard and won’t get any exceptions. You’ll have to set up a new instance of an object controller for every child view controller (copy & paste in Storyboard once you configured the first one works). For a detailed example take a look at another answer that gets much more into detail.