I'm building a Core Data document based application. In the app there's a main view (NSTableView) and an inspector view. Both views are separate nib files. The content of the inspector view should depend on the selected row in the main view. I have three controller files:
DocumentWindowController (master controller)
ItemsViewController (main view)
SchedulesViewController (part of the inspector)
Every controller owns a nib file. How is it possible to change the content of the inspector when the user selects another row in the main view's table?
I tried to make it work as follows:
Every controller has it's own NSArrayController, which is set up in the DocumentWindowController. The DocumentWindowController gets its managedObjectContext from [[self document] managedObjectContext]
When a user clicks a row in the ItemsViewController's tableView, an NSNotification is posted, with [NSTableView selectedRow] as a parameter. Then a fetch request is being made.
My intuition says I have to do this with Cocoa bindings.
I searched on Stackoverflow and google but I just can't make it work. If everything is in one nib file is very easy to accomplish. What is the best/most used way to achieve this?
Thanks in advance!
You could follow the same paradigm as the table view and create a data source for your inspector view.
#protocol InspectorDataSource <NSObject>
- (void) inspectorView:(InspectorView*)inspectorView managedObjectSelected:(NSManagedObject*)managedObject;
#end
Your inspector view controller would then have the data source as a property.
#interface InspectorViewController : UIViewController{
#public
id<InspectorDataSource> dataSource;
}
#property (nonatomic, assign) id<InspectorDataSource> dataSource;
Related
I am experimenting creating views that contain View based TableViews, ie: NSOutlineView and would like to better understand the correct connections that are made within IB.
At the moment, I have created a MainWindow that has the AppDelegate assigned to it.
On top of this, I have introduced a NSOutlineView which ultimately have an array as its data source.
I would like to have a separate Controller to look after this and its sub-views.
The NSOutlineView is made up of:
NSTableColumn
NSTableViewCell
NSTextField
With my NSTableCellView I have added two NSTextFields, the top one will be used as a Title and the second is a mini and will be used for displaying descriptions.
The issue I am facing is what controls need to have an IBOutlet connected to it.
My logic is; the only controls that will exhibit a change are the NSTableView as there needs to be a separate TableView within the TableColumn which will be a result of the item.count in the array.
Secondly, I am assuming that an IBOutlet will need to point to each of the TextField’s in the TableViewCell which will allow them to display the correct information for each item.count in the array.
So, all I need to do is design a Controller that has three IBOutlets.
Have I got my basics right….. or am I way off track?
I don't get everything you are asking for but first of all you don't set outlet to your NSTableView you need to use a NSArrayController, KVC and bindings.
1) in your AppDelegate.h create an IBOutlet to your NSArrayController:
#interface AppDelegate : NSObject <NSApplicationDelegate>
{
IBOutlet NSArrayController *yourArrayController;
}
2) in AppDelegate.m put your input or whatever in the NSArrayController:
yourItem *newItem = [[yourItem alloc] init];
[newItem setValue:[_inputVar stringValue] forKey:#"name"]; //use Key Value Coding
...
[yourArrayController addObject:newItem];
3) now go to your .xib and draw a NSArrayController object (green bubble with 3 blue cubes) in the dock (the area left to the .xib).
4) in your tableview click three times at each row and open the Bindings inspector
5) go to "Value" and activate "Bind to", select yourArrayController
6) set Controller Key to "arrangedObjects"
7) write the appropriate key in Model Key Path
Do that for each tableview column and you should be fine basically. No IBOutlet from the controller to you NSTableView!
The NSArrayController is taking care about user input back an forth to and from your tableview which is pretty convenient. I know not to answer all your questions but it might help you to get started. Good luck!
I've made a view in my storyboard which I've now decided I'd rather display its data via static table cells.
I can't use static table views in a UIViewController (Static table views are only valid when embedded in UITableViewController instances). So, I need to convert my existing UIViewController to a UITableViewController somehow. I've changed the .h file's parent, but that hasn't done it.
Is there another way to get this going? I'd really rather not have to make a new VC in the storyboard and move everything over, it's a big hassle.
I'll add to this, since the question is about how to change a UIViewController into a UITableViewController, and given that this question is over a year old and the original answer, while valid and may or may not have been the only solution at the time, doesn't actually answer the question and is not the only solution.
It IS possible to do this, you just have to set up the table view delegate and datasource outlets in IB, and manually edit the storyboard XML, which sounds scary but is actually very easy.
First, change your class's parent to be a UITableViewController. UITableViewController already adopts the UITableViewDatasource and UITableViewDelegate protocols, so if your class does too you can remove them:
#implementation MyTableViewController : UITableViewController
...
#end
Next, create new referencing outlets on your UITableView for its dataSource and delegate. The easiest way to do this is to control-drag from the UITableView to itself. The popup will give you the dataSource and delegate options.
Lastly, you need to change the storyboard XML. The storyboard file can get pretty big pretty fast. The easiest way to find the scene you are looking for is by setting Storyboard Identifier in the Identity Inspector. To view the XML directly, right click on the storyboard file in the project navigator and select "Open As -> Source Code". Now just search for whatever you set the reuse identifier to earlier. You'll see something similar to this:
<!-- My Table View Controller -->
<scene sceneID="EuE-XX-cCb">
<objects>
<viewController storyboardIdentifier="MY_TABLE_VIEW_IDENTIFIER" ... >
// Lots of other stuff
</viewController>
</objects>
</scene>
All you need to do is change the opening and closing view controller tags
<viewController>
</viewController>
to be tableViewController instead
<tableViewController>
</tableViewController>
That's it! No need to create a new UITableViewController scene or embed a UITableViewController in a container view.
EDIT:
I should also add that the UITableView MUST be the root view. It cannot be embedded inside another UIView.
If you want your static cell table view not to take up the entire screen, then using a container view is the easiest way to go. Start with a regular UIViewController and drag a container view (next to normal UIView in the object list) into its view. Resize it however you want -- the storyboard will automatically provide a view controller connected to this container view with an embed segue. Delete that controller, drag out a table view controller and right-drag from the container view to this table view controller to make a new embed segue. This table view controller can be accessed from the UIViewController with its childViewControllers property (and conversely, you can access the UIViewController from the table view controller with parentViewController if you need to).
What I did, is creating a UITableViewController in IB, open the Storyboard with a text editor, and copy all the nodes inside from the UIViewController to the UITableViewController.
I think that with this way there's less risk of deleting something important.
Before copying the sections objects, make sure that both tableviews (UIViewController and UITableViewController) have the same properties set like: static or dynamic cells, style (plain or grouped), etc.
Say you have a 2 subclass of tableView controller.
They both have the same header and footer view on top of the bottom of the header. They both implement pull to refresh.
They both have some common features.
The only different is one is for displaying the whole businesses, the other is for displaying only businesses you bookmark.
It looks like they both need to have the same parent class and the different is resolved on the child class. The differences are minor anyway.
I suppose the parent has it's own XIB, the children has it's own XIB.
Hmm... How would that work out? With the exception of container UIViewController, each controller should view a fullview of content. So which view should we display? The child or the superClass? Should child view add it's superclass subview?
Anyone have ever tried that?
Any code sample on the web that use this approach?
It sounds like, based on the business logic you explain, that everything is in common, except the list of data you're presenting. You could expose a property on your UITableViewController subclass to set the business objects that your tableview presents:
#interface JTBusinessesTableViewController : UITableViewController
#property (nonatomic, strong) NSArray *businesses;
#end
The code that instantiates this class would set the business objects:
JTBusinessesTableViewController *businessListings; //Instantiate from XIB or Storyboard
businessListings.businesses = [self bookmarkedBusinesses];
[self.navigationController pushViewController:businessListings animated:YES]
The code for displaying all businesses isn't going to be much different:
JTBusinessesTableViewController *businessListings; //Instantiate from XIB or Storyboard
businessListings.businesses = [self allBusinesses]; // Here we assign all of them
[self.navigationController pushViewController:businessListings animated:YES]
You're just selectively giving this view controller, the business objects to display.
Can anyone describe how it is possible to have a TableViewController with its xib file having a at its root and the uitableview as a subview?
I believe the TVController somehow assumes that UITableView will fill the entire area.
Why is that?
I have a really specific need to build a kind of side and bottom tabbed interface with a UITableView as its main area. Pressing tabs changes the predicate for the fetchedresultscontroller etc etc.
The reason I want this is because of the breadth and depth of the data categories in the data model. I rally need to flatten the menu structure a lot...other wise with a table and navbar controller structure, user experience will be akin to sinking to ever deeper depths of a cavern!
My idea is tried and true in other paradigms...in iOS it almost looks like it's Apple's way or the highway. I am OK with APPLE of course no gripe.
But I really would like to create this.
I tried adding a new tableviewcontroller complete with xib and then removing the UITableView in IB and replacing with a UIView and a UITableView as a subview, hooking up (I believe) the delegate to the file's owner.
I created an IV tableView for when I want to reference it and again used IB to hook it up in IB
Try to run it and it whines that...
[UITableViewController loadView] loaded the "TabbedTableController" nib but didn't get a UITableView.'
Really can't seem to get my head around what the issue is here.
There doesn't appear to be anymore I can do to hook the UITableView up!
Any help would be terrific. I'll send you a Christmas card in desperation :^)
Also...why should it be so and how strict is this UITableView fullscreen thing?
Enlighten me if you can. The docs I have read don't want to.
Regards
Keith
A UITableViewController does assume that the root view (i.e. the controller's view property) will be a UITableView, thus the table view fills the screen. When you need a view that combines UITableView with other top level views, you will need to do a little more work but it's not hard:
Your view controller will not subclass UITableView. Instead, do this:
#interface MyViewController : UIViewController
<UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, weak) IBOutlet UITableView* tableView;
In Interface Builder, drop in a UITableView and whatever other controls you need. The table view can be any size and in any location in the view hierarchy. Also in Interface Builder, ctrl-drag from the table view to your VC and set the delegate and dataSource outlets, and ctrl-drag from your VC to the table view to set the tableView outlet.
Your view controller implementation should be the typical table view controller implementation: cellForRowAtIndexPath, etc.
A UITableViewController is more or less just all of the above work packaged up into a single unit for you.
I've created a ChildViewController class, and then a nib that uses that class.
Then I created a BaseView, that includes some buttons, and some text that I'll be changing programmatically.
Then I created two more views (Boy and Girl), that I want to be able to lay behind the baseview so that the background color is different along with some graphics in an ImageView. I've named the views that I created in IB 'Boy' and 'Girl'...
But when I go back to my code where I'm calling ChildViewController, I'm not sure how to access the views I created so I can call insertSubView. Do I need to instantiate them in code? (in ViewDidLoad perhaps?) Does the nib create the instances when it loads?
I'm confused about how to handle multiple views for a single ViewController
edit =================
#Pablo Santa Cruz
Your answer assumes that i have two nibs and two view controllers (one for each view). I want to know if I can use one nib and one controller, and load in UIViews. It seems silly to create another nib and controller, when all want to do is change the background color and some graphics. Can't I programatically load in UIViews into a UIViewController?
Add IBOutlets in your App Controller class in Xcode then link them in IB (ctrl-click or right-click) from the connections tab in the Inspector to the object.
Then you will be able to send method calls to the objects.
The code in Xcode should look like this:
#interface AppController : NSObject
{
IBOutlet Girl girlIvarName1;
IBOutlet Boy boyIvarName2;
}
#end
You can access a UIView programatically by assigning a value to its tag property, which can be set in IB on the first tab of the inspector (Command 1)
The tag value defaults to zero, so if you want to access it specifically, make it non zero and unique. e.g. 100, which I will use in the example code below
Once the tag is set you can access the view using the following code in your UIViewController that was initWithNibName for the NIB containing the tagged view
UIView *aView = [self.view viewWithTag:100];
You can get instances for your IBuilder views with this piece of code:
boyViewController = [[BoyViewController alloc] initWithNibName:#"BoyViewController" bundle:nil];
girlViewController = [[GirlViewController alloc] initWithNibName:#"GirlViewController" bundle:nil];
Assuming your NIB file names are BoyViewController and GirlViewController. With those instances, you can do whatever you need to. I.E., adding them to a parent view (with addSubView message on the parent).