How to programmatically put an NSObjectController into entity mode? - objective-c

I'm instantiating an NSTreeController programmatically and want to put it into entity mode, but regardless of what I do it sticks to class mode.
The following code fragment doesn't work:
NSTreeController *treeCon = [[NSTreeController alloc] initWithContent:nil];
treeCon.entityName = currentEntityItem.entityDescription.name;
XaDocument *document = self.document;
treeCon.managedObjectContext = document.managedObjectContext;
treeCon.objectClass = nil;
Does anyone know how to programmatically put an NSObjectController into entity mode?
(I do know how to do this using IB, but I need to instantiate the controller programmatically.)

Related

Container View Controllers pre iOS 5

iOS 5 adds a nice feature allowing you to nest UIViewControllers. Using this pattern it was easy for me to create a custom alert view -- I created a semi-transparent view to darken the screen and a custom view with some widgets in it that I could interact with. I added the VC as a child of the VC in which I wanted it to display, then added its views as subviews and did a little animation to bring it on the screen.
Unfortunately, I need to support iOS 4.3. Can something like this be done, or do I have to manage my "alert" directly from the VC in which I want to display it?
MORE INFO
So if I create a custom view in a nib whose file owner is "TapView" and who has a child view that is a UIButton. I tie the UIButton action to a IBAction in TapView.
Now in my MainControllerView I simple add the TapView:
TapView *tapView = [[TapView alloc] init];
[[self view] addSubview:tapView];
I see my TapView, but I can't interact with the UIButton on it and can interact with a UIButton on the MainControllerView hidden behind it. For some reason I am not figuring out what I'm missing...
Not sure if this helps, but, in situations where I've needed more control over potential several controllers, I've implemented a pattern where I have a "master" controller object (doesn't need to be descendent from UIViewController), which implements a delegate protocol (declared separately in it's own file), and then have whatever other controllers I need to hook into declare an object of that type as a delegate, and the master can do whatever it needs to do in response to messages from the controllers with the delegate, at whatever point you need; in your case, that being displaying the alert and acting as it's delegate to handle the button selection. I find this approach to be very effective, simpler and usually cleaner. YMMV ;-)
Regd. your second query, where you are trying to create a custom view using nib. Don't change the FileOwner type, instead set "TapView" for the class property of the top level view object.
Once you have done this, you might experience difficulty when making connections. For that just manually choose the TapView file for making connections.
Also, to load the view you need to load its nib file. For which you can create a class level helper method in TapView class like below
+(TapView *) getInstance
{
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:#"TapView" owner:self options:nil];
TapView *view;
for (id object in bundle) {
if ([object isKindOfClass:[TapView class]]) {
view = (TapView *) object;
break;
}
}
return view;
}
Now, you get a refrence to you view object like this
TapView *tapView = [TapView getInstance];

Loading a Custom ViewController with data

I have a UITableView which loads through it's navigationController a new viewcontroller.
This code goes in the tableView:didSelectRowAtIndexPath method:
ConcertDetailViewController *detailVC = [[ConcertDetailViewController alloc] initWithNibName:#"ConcertDetailViewController" bundle:nil];
The UITableView has a model, I want to sent an element of this model to the newly created ViewController.
detailVC.aProd = [_prod objectAtIndex:indexPath.row];
When the value is set I want the detailVC to draw the data on the screen. I thought a custom setter, overwriting the one generated by #synthesize would work.
-(void)setaProd:(NSMutableDictionary *)aProd {
_aProd = aProd;
[self displayAProd];
}
displayAProd just takes the values in aProd and put's them on the screen, or rather I'm setting some value of an outlet , created in my nib file.
self.prodNameLbl.text = [_aProd objectForKey:#"name"];
Nothing special about this. But it just doesn't work. I figured out why, I think.
It's because the setter executes way faster then, loading the whole view into memory.
If I put self.prodNameLbl.text = #"something"; in the viewDidLoad method it does display the correct value in the label.
A quick workaround would be the see if _concerts has been set and from there call displayAProd. Here I'm doubting myself if it's a good way to load a view. What if the custom setter takes longer to execute the loading the view. The test to see if _concerts has been set will be false and nothing will be displayed. Or is that just impossible to happen ?
Or maybe there's a better pattern for loading views and passing data to them to be displayed.
Thanks in advanced, Jonas.
The problem is that when you load the view controller from the NIB, the IBOutlets will not be connected to your UILabel and other similar properties during the initWithNibName call.
You need to wait for viewDidLoad to be called on detailVC and call [self displayAProd] from there. At this point, the connections will have been made.
Do a quick test. Put a break point in your didSelectRowAtIndexPath method and, after initialising detailVC, check to see if prodNameLbl is null or not.

Programmatically defining NSSegmentedCell

I am a little stumped as to how to instruct a programmatically created NSSegmentedControl to use a subclass instance of an NSSegmentedCell.
If I want to use a subclasses NSSegmentedCell on an NSSegmentedControl built using IB it would be as simple as doing the following:
Drag an NSSegmentedControl into the NSView
Click through to the NSSegmentedCell
In the inspector assign the class definition to the subclass (e.g. myCustomCell)
Job done.
However, when programmatically creating an NSSegmentedControl as in the following simplified example, I don't see how to subclass the cell...
-(void)creatSegmentControl {
if (!mySegmentControl)
mySegmentControl = [[NSSegmentedControl alloc]
initWithFrame:NSMakeRect(0,0 400,20)];
[mySegmentControl setSegmentCount:2];
[mySegmentControl setLabel:#"First" forSegment:0];
[mySegmentControl setLabel:#"Second" forSegment:0];
[mySegmentControl setTarget:self];
[mySegmentControl setAction:#selector(segmentClicked:)];
}
NSSegmentedControl does not appear to have a method for defining the class to use for it's segment cell instances.
As usual, any and all help appreciated.
Update
Tried implementing [mySegmentControl setCellClass:[myCustomCell class] but that didn't work either. I was thinking that maybe it inherited the ability to set it's cell class like other AppKit controls. :-(
This must be possible though... somehow...
Kinda late, but wouldn't overwrite cellClass work?
+ (Class)cellClass
{
return [YourCustomCellClass class];
}
The property cellClass is in the deprecated category.
You need to make an instance of your custom class and set NSControl's cell property, before anything else (yes NSSegmentedControl inherits from NSControl)
NSSegmentedControl* oSegment = [[NSSegmentedControl alloc] init];
QPDFSegmentedCell* csell = [[QPDFSegmentedCell alloc] init];
oSegment.cell = csell;

Reusable bits of interface, designed in IB

I'm making an app that includes the same group of buttons in many different contexts. The buttons send their actions to a different object in each context. I'd like to be able to design a single NSView in IB containing the buttons, and then be able to put copies of that view in many places in my nibs, while maintaining the link, so changes propagate. I'd like to connect each of those instances to different objects, and have the buttons send their actions to whatever object their parent view is connected to.
I thought of creating a subclass of NSView which, when loaded, replaces itself with another view which it loads from a nib file, setting the connected object as File's Owner, but I'm not convinced this is the cleanest method. Here's my implementation of that idea (which -does- work):
#implementation AVNViewFromNib
- (void)awakeFromNib
{
//Load the nib whose name is specified by the "nibFile" key
NSNib* viewNib = [[NSNib alloc] initWithNibNamed:[self valueForKey:#"nibFile"] bundle:[NSBundle mainBundle]];
NSMutableArray* topLevelObjects = [NSMutableArray new];
[viewNib instantiateNibWithOwner:relatedObject topLevelObjects:&topLevelObjects];
//Find our replacement view in that nib
for (id currentObject in topLevelObjects)
{
if ([currentObject isKindOfClass:NSClassFromString(#"AVNReplacementView")])
{
representedView = currentObject;
break;
}
}
//Copy appropriate properties from us to our representedView
[representedView setAutoresizingMask:[self autoresizingMask]];
[representedView setFrame:[self frame]];
[[self superview] addSubview:representedView];
//We were never here. :)
[self removeFromSuperview];
[viewNib autorelease];
}
#end
#implementation AVNReplacementView
#end
Is that the cleanest method? Is there a standard way of going about this?
You can create the view with the buttons in it in IB, then drag that view into the Library window and save it. The catch is, there's no “link” between them; editing one won't change anything about the others.
If you want that, you'll need to make a subclass of NSView instead.
I thought of creating a subclass of NSView which, when loaded, replaces itself with another view which it loads from a nib file, setting the connected object as File's Owner, but I'm not convinced this is the cleanest method.
That could work. I don't think that's really all that dirty; the reason init methods return an object is that they explicitly can return a different object. However, I'm not sure how you'd handle views of different frames, since the loaded view will have whatever frame it has in the nib.
Another way would be to load the buttons from a nib, but you'd have to adjust their frames before adding them as subviews.

Design an NSView subclass in Interface Builder and then instantiate it?

So I have an NSTabView that I'm dynamically resizing and populating with NSView subclasses. I would like to design the pages in IB and then instantiate them and add them to the NSTabView. I got the programmatic adding of NSView subclasses down, but I'm not sure how to design them in IB and then instantiate them.
I think I got it. Let me know if this is not a good thing to do.
I made a new xib file, set its File's Owner to be an NSViewController and set its "view" to the custom view I designed in the xib.
Then you just need:
NSViewController *viewController = [[NSViewController alloc] initWithNibName:#"MyViewXib" bundle:nil];
NSView *myView = [viewController view];
#toastie had a really good answer. Mine is similar, but requires a bit more explanation.
Let's say you've already got a controller object and you don't want to instantiate a new controller object just to get at a view, and let's say that you're going to need multiple copies of this view (for example, you've designed a custom UITableViewCell in IB and you want to instantiate it again and again from your UITableViewController). Here's how you would do that:
Add a new IBOutlet to your existing class called "specialView" (or something like that). It may also be helpful to declare it as a (nonatomic, retain) property.
Create a new view xib called "SpecialView", and build the view however you like.
Set the File's Owner of the view to be your controller object.
Set the specialView outlet of File's Owner to be the new view.
Whenever you need a new copy of the view in your code, you can simply do the following.
(gratuitous text to get formatting working properly)
NSNib * viewNib = [[NSNib alloc] initWithNibNamed:#"SpecialView" bundle:nil];
[viewNib instantiateNibWithOwner:self topLevelObjects:nil];
[viewNib release];
NSView * myInstantiatedSpecialView = [[[self specialView] retain] autorelease];
[self setSpecialView:nil];
Yes, it's a bit more code than other ways, but I prefer this method simply because the view shows up in the designated IBOutlet. I retain and autorelease the view, because I like to reset the outlet to nil once I have the view, so it can be immediately ready to load a new copy of the view. I'll also point out that the code for this is even shorter on the iPhone, which requires one line to load the view, and not 3 (as it does on the Mac). That line is simply:
[[NSBundle mainBundle] loadNibNamed:#"SpecialView" owner:self options:nil];
HTH!