I'm trying to nest one NSTableView inside another view based tableview in my xib. When I do so, I get the following build error (when trying to compile the xib):
An instance of NSTableColumn with object ID jRp-dg-jOe did not archive its child (NSTableCellView) with an object of ID y8a-qz-ChK
Has anybody seen this, or know how to fix it?
I assume I could just create another xib for the NSTableCellView, and hook it up to the parent NSTableView using
NSNib *cellNib = [[NSNib alloc] initWithNibNamed:#"MyCellContainingAnNSTableView" bundle:nil];
[parentTable registerNib:cellNib forIdentifier:#"SomeIdentifier"];
but that's a little more annoying...
OK so it seems like nobody has any solutions for this...
After a long time looking, it seems that Interface Builder is just incapable of archiving nested NSTableViews. My solution was to create a new .xib containing an NSTableCellView and referencing that from the original NSTableView. Here's how to do it:
Create a new NSTableCellView in a separate .xib. Call it something like SchemeCell.xib
Add an NSTableViewDelegate to your code, and set it as the delegate for your main (parent) tableview.
Add in the method:
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSNib *cellNib = [[NSNib alloc] initWithNibNamed:#"SchemeCell" bundle:nil];
[self.schemeTableView registerNib:cellNib forIdentifier:#"SchemeCell"];
});
return [self.schemeTableView makeViewWithIdentifier:#"SchemeCell" owner:self];
}
And away you go! Now the main disadvantage to this is that you can no longer use Interface Builder to hook up items from the SchemeCell to the parent NSTableView (since they are now both in separate .xibs, but it's not the end of the world, because you can do all of this within the code of course.
Related
i try to get my head around view-based NSTableViews on OS X.
My Problem is that in the loaded view cell (NSView subclass) the subviews are not initialized when i try to assign the values in my delegate.
At the moment the correct count and the correct view is displayed, but i cannot access the subviews to assign the proper values.
What i have done so far:
Created the xib with the custom view cell in Interface Builder.
Created the custom class for the cell and assigned it in IB.
This work fine and i can see the properties in the Debugger. Correct class
and the properties are IBOulets that are wired to the correct fields.
I can see the call to:
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
In my NSViewController:awakeFromNib i do:
// Make my view controller the delegate
_applicationTableView.delegate = self;
// set the correct datasource
_applicationTableView.dataSource = [NWDataHolder sharedInstance];
// register the nib with the custom cell
[_applicationTableView registerNib:cellNib forIdentifier:#"ApplicationListViewCell"];
In my - (NSView *)tableView:viewForTableColumn:row: i do:
NWApplicationListViewCell *cell = [_applicationTableView makeViewWithIdentifier:#"ApplicationListViewCell" owner:self];
The correct class and cell is returned and i have access to the properties.
The problem is, that subview is not initialized, the property is nil and the new value could not be set:
Log(#"Application Name Label %#", cell.applicationNameLabel); => nil
I`ve seen some hints that the subviews are initialized lazyly, but i cannot find a way to make the eager initialize.
Any suggestions what i'm doing wrong ?
Thanks,
Oliver
Fixed the problem. But i do not understand why this really happens.
I wired the fields in IB to the File Owner, but not to my ApplicationViewCell.
After wiring the property to both everything works fine.
Just getting into NSOutlineViews and see them a useful control.
Is it possible to show a Xib as the root item??
Fritzables.
Yes, when you use a view based NSOutlineView. First register the nib you want to display for a cell using registerNib:forIdentifier: (windowDidLoad in a window controller would be a good place, awakeFromNib is also a possibility).
NSNib *cellNib = [[NSNib alloc] initWithNibNamed:#"MyCell" bundle:nil];
[self.outlineView registerNib:cellNib forIdentifier:#"MyIdentifier"];
Next in your outlineView:viewForTableColumn:item: you get a (possibly recycled) instance of your nib by using the earlier specified identifier:
- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
NSView *cellView = [outlineView makeViewWithIdentifier:#"MyIdentifier" owner:self];
// optional configuration here
return cellView;
}
I've got a blog post + mac app sample code that demonstrates this.
Yes. You just have to switch it to use view based cells.
In your delegate, implement outlineView:viewForTableColumn:item: to provide the correct XIB.
Yes it is Possible, NSOutlineView is another great visual control available in Mac OS X. Being descendant of the NSTableView It represents the hierarchical data. You can collapse and expand nodes, see parents and their children. In this article we will describe how to use NSOutlineView to show DASchema object which by its nature is a good example of the tree-like data.
For more info check Here
Is there a way to get a reference to the view controller of my superview?
There were several instances that I needed this on the past couple of months, but didn't know how to do it. I mean, if I have a custom button on a custom cell, and I wish to get a reference of the table view controller that controls the cell I`m currently in, is there a code snippet for that? Or is it something that I should just solve it by using better design patterns?
Thanks!
Your button should preferably not know about its superviews view controller.
However, if your button really needs to message objects that it shouldn't know the details about, you can use delegation to send the messages you want to the buttons delegate.
Create a MyButtonDelegate protocol and define the methods that everyone that conforms to that protocol need to implement (the callback). You can have optional methods as well.
Then add a property on the button #property (weak) id<MyButtonDelegate> so that any class of any kind can be set as the delegate as long as it conforms to your protocol.
Now the view controller can implement the MyButtonDelegate protocol and set itself as the delegate. The parts of the code that require knowledge about the view controller should be implemented in the delegate method (or methods).
The view can now send the protocol messages to its delegate (without knowing who or what it is) and the delegate can to the appropriate thing for that button. This way the same button could be reused because it doesn't depend on where it is used.
When I asked this question I was thinking of, in a situation where I have custom cells with buttons on them, how can the TableViewController know which cell's button was tapped.
More recently, reading the book "iOS Recipes", I got the solution:
-(IBAction)cellButtonTapped:(id)sender
{
NSLog(#"%s", __FUNCTION__);
UIButton *button = sender;
//Convert the tapped point to the tableView coordinate system
CGPoint correctedPoint = [button convertPoint:button.bounds.origin toView:self.tableView];
//Get the cell at that point
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:correctedPoint];
NSLog(#"Button tapped in row %d", indexPath.row);
}
Another solution, a bit more fragile (though simpler) would be:
- (IBAction)cellButtonTapped:(id)sender
{
// Go get the enclosing cell manually
UITableViewCell *parentCell = [[sender superview] superview];
NSIndexPath *pathForButton = [self.tableView indexPathForCell:parentCell];
}
And the most reusable one would be to add this method to a category of UITableView
- (NSIndexPath *)prp_indexPathForRowContainingView:(UIView *)view
{
CGPoint correctedPoint = [view convertPoint:view.bounds.origin toView:self];
return [self indexPathForRowAtPoint:correctedPoint];
}
And then, on your UITableViewController class, just use this:
- (IBAction)cellButtonTapped:(id)sender
{
NSIndexPath *pathForButton = [self.tableView indexPathForRowContainingView:sender];
}
If you know which class is the superview of your view controller, you can just iterate through the subviews array and typecheck for your superclass.
eg.
UIView *view;
for(tempView in self.subviews) {
if([tempView isKindOfClass:[SuperViewController class] ])
{
// you got the reference, do waht you want
}
}
I was using initWithNibName to push the cells to detail views but I want to use single storyboard rather than having numerous nib files. The below code is supposed to push the view to subcategoriesViewController when the cell is clicked. the cells are dynamic from json data.
Its not giving any errors. Do you think its an issue with navigation control? Just to test, I added a button on this view and it works; when I click it, it does push to next view.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *dict = [rows objectAtIndex: indexPath.row];
subcategoriesViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"subcategoriesViewController"];
controller.CATNAME = [dict objectForKey:#"C_NAME"];
controller.CATNUMBER = [dict objectForKey:#"CAT_ID"];
[self.navigationController pushViewController:controller animated:YES];
// [controller release];
}
Okay I think I have found the cause; but don't know how to fix it.
The tableview in this controller is actually a subview of a viewController. I duplicated these files and created a new view which is a TableViewController. This code, when used in the TableViewController does the job, but it doesn't do it when used in a controller where the tableview is a subview of a ViewController.
I want the table to be a subview because I want to put an image above the table. So, I think the problem here is this line:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
It does not reference the table in the view. Can anyone tell me how to modify this so it would reference the table within the view?
If didSelectRow... is not being called then your view controller is probably not set as the delegate of your table view.
This is set up for you automatically when using a UITableViewController but you have to explicitly do it when using a standard view controller.
An answer to your problem is often that self.navigationController is in fact nil because there is no navigation controller for your view. You perhaps have to get a pointer to the navigation controller of your projet and push your view on it.
I have a view controller MainVC which contains a view where I want to switch between various tables. I have an "abstract" class called InfoVC which extends UITableViewController and contains several methods that must be overridden or else they throw an exception. Finally, I have several classes which extend InfoVC and implement the "abstract" methods in InfoVC, along with methods that override tableView's numberOfRowsInSection and cellForRowAtIndexPath.
This is how I load a table in MainVC. In this case, TemperatureInfoVC extends InfoVC, self.subVC is of type InfoVC, and switchView is a view in MainVC:
TemperatureInfoVC *sVC = [[TemperatureInfoVC alloc] initWithStyle:UITableViewStylePlain];
self.subVC = sVC;
[sVC release];
[switchView insertSubview:self.subVC.view atIndex:0];
My problem is that the table always loads empty. From setting breakpoints, I can see that TemperatureInfoVC's numberOfRowsInSection is being called, but neither TemperatureInfoVC's or InfoVC's cellForRowAtIndexPath are being called. However, if I change the first line in the code above to:
InfoVC *sVC = [[InfoVC alloc] initWithStyle:UITableViewStylePlain];
then InfoVC's numberOfRowsInSection and cellForRowAtIndexPath are called properly. Does anyone know why TemperatureInfoVC's cellForRowAtIndexPath is not being called?
I assume, that you are not setting the delegate (UITableViewDelegate) and the datasource (UITableViewDatasource). Usually the delegate and the datasource implementation are with-in the custom UITableViewController, so (if not wiring up in IB), there need to be this somewhere:
tableView.delegate = self;
tableView.dataSource = self;
often within the -loadView or -viewDidLoad:
Oops, figured it out. I was loading data from an NSMutableArray in TemperatureInfoVC which was set in MainVC, but I was setting the data before I initialized subVC, so of course the table had no data in it.