Selecting rows in NSTableView inside a container view - objective-c

In my interface I need to provide two NSTableViews in the same window that are structurally the same, but present different data to the user. I'm using storyboards and to do this, I've placed two Container Views in the view controller of the window. I then added a single view controller to the storyboard (outside of the window's view controller) and into that controller placed an NSTableView.
A couple of storyboard screenshots to illustrate:
The NSTableView is inside the Lineup View Controller, which has its own interface and implementation. The two Lineup View Controllers are binded to the container views within viewDidLoad of the Game Board View Controller like so (showing the code for only one of the attachments):
homeLineupView = [self.storyboard instantiateControllerWithIdentifier:#"Lineup View"];
[self addChildViewController: homeLineupView];
[HomeLineupContainerView addSubview:homeLineupView.view];
I'm able to successfully populate the two table views using notifications to provide the data that is unique to each table. The table views are functioning independently, as expected, with no problems.
Where I'm kind of stuck is how I now go about talking to the two table views from the view controller that manages everything else in the window (Game Board View Controller in the second figure). For example, how would I go about telling the table view inside Home Lineup Container View to select row 3? I have references to the Lineup View Controllers, but not the NSTableViews — unless there is a straightforward way to get those so I can call the methods directly. Or... do I need to set up notifications (or some other mechanism) for doing things like selecting a row, detecting a double click, etc?
Thanks!!
Adding a little more information about where I am confused
When I step through my code in the debugger I can "see" the table view inside the NSViewController that references the enclosing container view (see below).
You can also see my very incorrect attempt at using tableView directly to selectRowIndexes. 'visitorLineupView' is defined like so:
#interface GameBoardViewController : NSViewController <NSOpenSavePanelDelegate> {
:
:
NSViewController *visitorLineupView;
NSViewController *homeLineupView;
:
:
}
And the tableview itself is defined like so:
#interface LineupViewController : NSViewController <NSTableViewDataSource> {
:
:
IBOutlet NSTableView *tableView;
:
:
}
So, the procedural C programmer in me sees all this and wants to grab that 'tableView' pointer and use it directly to send it 'selectRowIndexes' message. But I know that conceptual rules of MVC are throwing up roadblocks I'm having a difficult time scaling.

Related

StoryBoard handling inherited view controller

I am new to storyboard. I used to make my view using code. I have a question.
I created a view controller
#interface FunctionViewController : UIViewController
And I have use my code to add
a full screen button (alpha : 0.5)
a popup-like view in the middle (not full screen)
Then I created another Viewcontroller
#interface PlayFunctionViewController : FunctionViewController
and add some views on the popup-like view
I want to recreate these stuff by using storyboard.
How can I build these using interface builder and storyboard so that
I don't need to layout the full screen button and the popup-like view in every subclass of FunctionViewController?
You can't inherit the layout of a superclass in a subclass in a storyboard, i.e. if you visually lay out elements in a view controller in a storyboard and connect them to code, subclasses of that view controller, and even other instances of the same class, will have to be laid out individually, and will not automatically be populated or updated.
In other words, in a storyboard, you will have to manually lay out and connect all your interface elements in every individual instance and subclass that you add to the storyboard. This gives you flexibility in that you can reuse multiple instances of the same class throughout your app and lay them out differently, but it does not give you the ability to inherit layouts.
If you want to inherit your layout in subclasses, do your layout programmatically in the viewDidLoad of your superclass, and then all of your subclasses will have those interface elements, even if you design and lay them out in your storyboard (they will not be visible in the storyboard, but they will appear when you build and run your app).
Basically, if you want to have interface elements that are the same in a class and all its subclasses, create them programmatically, and they will exist in all instances and subclass instances, even if you create and design the instances themselves in your storyboard.
You can mix code and storyboard, so you can create some elements in your storyboard, but others that need to be present in all instances and subclasses, in code.
I ran into a similar issue, and though it was time consuming but creating a delegate and reusing it in multiple view controllers was a much better solution. Although that beats the whole point of 'Inheritance'.

Change a UIViewController to a UITableViewController inside a storyboard?

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.

Can we create an IBOutlet to a View Controller

I have a viewController. That viewController have a containerView that contain another controller. List and map.
I want users to be able to switch between list and map via a button.
So that means the parent view controller (or somebody) need to be able to refer to both the mapViewController and ListViewController.
How can I achieve that?
One way to do so is by using instantiateViewControllerWithIdentifier. However, I do not like identifier. Identifier is prone to mistyping and conflicts. I like Outlet
I read that one possible way to do so is to set a storyboardid. What about if I like the good old outlet?
As I understand it, you have to sub view controllers. What I would do is create a master controller class, which imports all of the other view controllers (think of it as the super class of the entire project) which will then control a empty view, less a button. The background of the empty view will be fed by an "active" view, which will be supplied by either listViewController.h or mapViewController.h This will let the users use both the list/map VC's, and be able to switch between the two.

How do you hook up IBOutlets across scenes in the Interface Builder storyboard?

I have a custom UITableViewController with 2 properties:
#property (strong, nonatomic) IBOutlet VenueHeaderViewController *header;
#property (strong, nonatomic) IBOutlet VenueFooterViewController *footer;
The UIViews of my VenueHeaderViewController and VenueFooterViewController were designed in IB. Each of these controllers has IBOutlets to its subviews as set up in the storyboard. This is my storyboard:
My question is, how do I now connect my header and footer IBOutlets inside my UITableViewController to instances of my VenueHeaderViewController and VenueFooterViewController while retaining the layout and IBOutlets which I have set up in the storyboard?
You can't hook up IBOutlets across different scenes in a storyboard. Scenes are each entirely self-contained, and only connected via segues (or relationships in the case of special container view controllers like tab, navigation, split view and popover controllers). If you've done any IB work without storyboards, think of each scene (view controller) as being in its own nib (in fact, that's what storyboards do behind the scenes).
Beyond that, it's generally the case (on iPhone at least) that there's a one-to-one mapping between a view controller and a "screen" of UI -- you generally don't have different view controllers managing different parts of the screen, as appears to be the case with the header, table, and footer you have. (Thus, you'll pretty much never have an IBOutlet to any UIViewController subclass.)
So what can you do? Here's a few different approaches, from easier to harder:
UITableView header & footer
If you're meaning to assign those header & footer views to the UITableView's tableHeaderView and tableFooterView properties, you can do that in the storyboard in IB -- just drag a UIView of some sort from the library toward the top or the bottom of the table until you see a blue line above or below the prototype cell(s):
The table header & footer views might or might not be what you're after, though: they're part of the scrolling content of the table (that is, they appear above the first row and below the last, respectively, so they can be scrolled off the screen).
Views above & below
If your intended use of these views is as "header" and "footer" in that they always appear above or below the table regardless of where it's scrolled, then what you really need is to have the header, footer and the table view all be subviews of a root view.
For that, you'll need a regular UIViewController subclass rather than a UITableViewController subclass. You can put a UITableView in as a subview of that view controller's root view, and arrange whatever other views you want to be visible above/below it. (Just make sure to set the autoresizing sensibly if you plan to support rotation.) Here's an example:
(If you're wondering where the prototype cells are when you first drag out the table view, check the inspector -- you set the number of them in the upper right of that screenshot.)
Top level views
With regular nibs (not storyboards), you can set up views in IB which aren't the view controller's root view. Then you can still connect outlets to them, and programmatically insert them into the view hierarchy as needed.
You can sort of do this in a storyboard, too -- drag views to the black bar beneath the scene -- but due to a bug in the current version of Xcode, you can't see these views for editing. Oops.
Multiple view controllers, really
If none of the above suit your needs, you might still have a use for multiple view controllers -- say, if you wanted to switch out different different header views at various times, and save memory by dynamically instantiating these view hierarchies as needed. Cases like this are pretty rare, though... you should make sure you've exhausted other options first.
In effect, you're still using one of the previous two approaches, but the views you want are located in other scenes (again, it's like they're in other nibs). Here, the view controllers holding the other views don't even need to be custom subclasses of UIViewController, since they're just temporary containers for views you want to insert into a different view controller's view hierarchy.
In that case, the storyboard you have is what you want... it's just a matter of getting those view controllers appropriately loaded, which you can do thusly:
SpecialViewController *svc = [storyboard instantiateViewControllerWithIdentifier:#"SpecialViewController"];
Then, pull the view property from each view controller, insert it into your "main" view controller's view hierarchy, and dispose of the the view controller you pulled it from.

Accessing Views created in Interface Builder

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