How do I make this code KVO compliant? - cappuccino

I'm trying to get a grip on bindings and have set up a CPWindow with a CPTextView in xcode and bound the window and the textview to their respective outlets.
I'm getting a bit stuck however trying to make the textview display the _xmlContent. I've set the binding in xcode to "delegate" and the key path to xmlContent (also tried self.xmlContent and _xmlContent and all variations thereof that I could think of).
#import <Foundation/Foundation.j>
#import <AppKit/AppKit.j>
#implementation AppController : CPObject
{
#outlet CPWindow theWindow;
#outlet CPTextView codeView;
CPString _xmlContent #accessors(property=xmlContent);
}
// ...
#end
The message I'm getting now is:
CPUndefinedKeyException: <AppController 0x00a177> is not key value coding-compliant for the key xmlContent
How do I make my AppController key value coding compliant for the key "xmlContent"?

Your code looks correct. I recreated it myself and it works fine for me:
#implementation AppController : CPObject
{
#outlet CPWindow theWindow;
CPString _xmlContent #accessors(property=xmlContent);
}
- (void)applicationDidFinishLaunching:(CPNotification)aNotification
{
}
- (void)awakeFromCib
{
[theWindow setFullPlatformWindow:YES];
[self setXmlContent:#"kitten"];
You should verify that you are running the code you think you are running. One common pitfall here is that some web-browsers really like to hang on to cached copies of your code so make sure you have the "Disable Caches" option enabled in whatever browser you are testing with.
If all else fails, change the port number of your development HTTP server and load the page from the new URL. This will force the browser to get fresh copies of everything (because the URL changed).

Related

Can't change NSTextField's string value in other classes than the AppDelegate

I'm working on an Mac OS X app that'll save and load data (sort of notes app). I've added some NSTextFields and I want to change its string value when the load button is pressed. I want to do that in a separate class. I created the class and called it Load. Now I have the Load.h and the Load.m file. I made an IBAction for when the button is pressed:
- (IBAction)loadText:(id)sender
{
[textField setStringValue:[[NSUserDefaults standardUserDefaults] objectForKey:#"MyNote"]];
NSLog(#"This method works!");
}
Xcode (7 beta 5) doesn't see any errors. But when I run it it doesn't do anything with the textfield, but it does log the string. When I was trying to find a way to edit the string value I found if you create an outlet for the NSTextField in the AppDelegate it'll work.
In app Delegate:
- (IBAction)loadTextInAppDelegate:(id)sender
{
[appDelegateTextField setStringValue:[[NSUserDefaults standardUserDefaults] objectForKey:#"MyNote"]];
NSLog(#"This method works!");
}
It's good to know there's a possibility to do it that way, but then my AppDelegate.m file will be a mess because I'll have to put in all the things that I wanted to put in the Load.m file.
So is there a way to change the string value of the NSTextField in a different class than the AppDelegate?
Two things.
1: are you sure textField in your loadText method isn't nil?
2: are you sure this method is executing on the UI (main) thread?

How to use NSVisualEffectView backwards-compatible with OSX < 10.10?

The upcoming OSX 10.10 ("Yosemite") offers a new type of view, NSVisualEffectView, which supports through-the-window or within-the-window translucency. I'm mostly interested in through-the-window translucency, so I'm going to focus on that in this question, but it applies to within-the-window translucency as well.
Using through-the-window translucency in 10.10 is trivial. You just place an NSVisualEffectView somewhere in your view hierarchy and set it's blendingMode to NSVisualEffectBlendingModeBehindWindow. That's all it takes.
Under 10.10 you can define NSVisualEffectViews in IB, set their blending mode property, and you're off and running.
However, if you want to be backwards-compatible with earlier OSX versions, you can't do that. If you try to include an NSVisualEffectView in your XIB, you'll crash as soon as you try to load the XIB.
I want a "set it and forget it" solution that will offer translucency when run under 10.10 and simply degrade to an opaque view when run on earlier OS versions.
What I've done so far is to make the view in question a normal NSView in the XIB, and then add code (called by awakeFromNib) that checks for [NSVisualEffectView class] != nil, and when it's the class is defined, I create an instance of the NSVisualEffectView, move all my current view's subviews to the new view, and install it in place. This works, but it's custom code that I have to write every time I want a translucent view.
I'm thinking this might be possible using an NSProxy object. Here's what I'm thinking:
Define a custom subclass of NSView (let's call it MyTranslucentView). In all the init methods (initWithFrame and initWithCoder) I would throw away the newly created object and instead create a subclass of NSProxy that has a private instance variable (myActualView). At init time it would decide to create the myActualView object as an NSVisualEffectView if OS>=10.10, and a normal NSView under OS<10.10.
The proxy would forward ALL messages to it's myActualView.
This would be a fair amount of fussy, low-level code, but I think it should work.
Has anybody done something like this? If so, can you point me in the right direction or give me any pointers?
Apple is MUCH more open with the Beta agreement with Yosemite a than with previous Betas. I don't think I'm violating my Beta NDA by talking about this in general terms, but actual code using NSVisualEffectView would probably need to be shared under NDA...
There is a really simple, but somewhat hacky solution: Just dynamically create a class named NSVisualEffectView when your app starts. Then you can load nibs containing the class, with graceful fallback on OS X 10.9 and earlier.
Here's an extract of my app delegate to illustrate the idea:
AppDelegate.m
#import "AppDelegate.h"
#import <objc/runtime.h>
#implementation PGEApplicationDelegate
-(void)applicationWillFinishLaunching:(NSNotification *)notification {
if (![NSVisualEffectView class]) {
Class NSVisualEffectViewClass = objc_allocateClassPair([NSView class], "NSVisualEffectView", 0);
objc_registerClassPair(NSVisualEffectViewClass);
}
}
#end
You have to compile this against the OS X 10.10 SDK.
How does it work?
When your app runs on 10.9 and earlier, [NSVisualEffectView class] will be NULL. In that case, the following two lines create a subclass of NSView with no methods and no ivars, with the name NSVisualEffectView.
So when AppKit now unarchives a NSVisualEffectView from a nib file, it will use your newly created class. That subclass will behave identically to an NSView.
But why doesn't everything go up in flames?
When the view is unarchived from the nib file, it uses NSKeyedArchiver. The nice thing about it is that it simply ignores additional keys that correspond to properties / ivars of NSVisualEffectView.
Anything else I need to be careful about?
Before you access any properties of NSVisualEffectView in code (eg material), make sure that the class responds to the selector ([view respondsToSelector:#selector(setMaterial:)])
[[NSVisualEffectView alloc] initWithFrame:] still wont work because the class name is resolved at compile time. Either use [[NSClassFromString(#"NSVisualEffectView") alloc] initWithFrame:], or just allocate an NSView if [NSVisualEffectView class] is NULL.
I just use this category on my top-level view.
If NSVisualEffects view is available, then it inserts a vibrancy view at the back and everything just works.
The only thing to watch out for is that you have an extra subview, so if you're changing views around later, you'll have to take that into account.
#implementation NSView (HS)
-(instancetype)insertVibrancyViewBlendingMode:(NSVisualEffectBlendingMode)mode
{
Class vibrantClass=NSClassFromString(#"NSVisualEffectView");
if (vibrantClass)
{
NSVisualEffectView *vibrant=[[vibrantClass alloc] initWithFrame:self.bounds];
[vibrant setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
[vibrant setBlendingMode:mode];
[self addSubview:vibrant positioned:NSWindowBelow relativeTo:nil];
return vibrant;
}
return nil;
}
#end
I wound up with a variation of #Confused Vorlon's, but moving the child views to the visual effect view, like so:
#implementation NSView (Vibrancy)
- (instancetype) insertVibrancyView
{
Class vibrantClass = NSClassFromString( #"NSVisualEffectView" );
if( vibrantClass ) {
NSVisualEffectView* vibrant = [[vibrantClass alloc] initWithFrame:self.bounds];
[vibrant setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
NSArray* mySubviews = [self.subviews copy];
for( NSView* aView in mySubviews ) {
[aView removeFromSuperview];
[vibrant addSubview:aView];
}
[self addSubview:vibrant];
return vibrant;
}
return nil;
}
#end

Cannot Save in a Document-Based Application

I created new Document-Based-App.
I implemented dataOfType in subclass of NSDocument
- (NSData*) dataOfType:(NSString *)typeName error:(NSError *__autoreleasing *)outError
{
return [NSKeyedArchiver archivedDataWithRootObject:bcmwc.bindingsController.arrangedObjects];
}
in xib http://i.minus.com/iH2Rj9v5oOhTn.png
When I click "Save" from menu nothing's gonna happen, any errors in console.
I set a breakpoint in dataOfType, and when I clicked "Save", application didn't stop.
Any suggestions?
EDIT
I think it may be connected with fact I use custom NSWindowController, and custom xib.
I made a test when I load custom xib, everything is fine dataOfType method is invoked etc..
Should I connect in some way my custom xib (window) with subclass of NSDocument?
It looks like your Save menu item is connected properly, so let's concentrate on the code (+1 for having posted it in the first place).
You do nothing in your code to ensure -archivedDataWithRootObject: returns valid data or set an error if it doesn't. My best guess (because you haven't provided nearly enough detail to do anything but guess) is that you're returning nil because your call to -archivedDataWithRootObject: is doing the same. Check to see if you get valid data and set the outError if not.
Why would you get nil? Perhaps one or more of the objects in the object graph created by archiving your array controller's -arrangedObjects array isn't <NSCoding> compliant. This is likely the case if your array controller holds objects of a custom class you created rather than a standard Property List container.
Read the Archives and Serializations Programming Guide (in particular, the Encoding and Decoding Objects section) to learn how to make your custom class <NSCoding> compliant so that it knows how to serialize itself (write itself into a byte stream suitable for NSKeyedArchiver, etc. and create an instance of itself from such a byte stream).
Also, you really need to learn to use the debugger. You're groping around in a dark cave with lots of pitfalls and no flashlight without it. Try setting a breakpoint in methods you expect to be called. If they're not called, you can check outlets/actions, etc. If they are, you can step through each line and make sure everything is executing as you expected. If you write a little more verbose code, you can more easily inspect the results when paused in the debugger. Two lines in your case will help you more than one:
- (NSData*) dataOfType:(NSString *)typeName error:(NSError *__autoreleasing *)outError
{
NSData * myData = [NSKeyedArchiver archivedDataWithRootObject:bcmwc.bindingsController.arrangedObjects];
// You should be handling nil (setting a descriptive error) here if (!myData)...
return data; // breakpoint here; you should now see myData is likely nil
}

Visibility of methods in class extensions in Xcodes code completion

I saw a similar question addressing differences of the code completion between Xcode 3.2 and Xcode 4. My question addresses a specific behaviour of Xcode (3.2).
When I declare "private" methods in a class extension, these methods are visible in the code completion lists, where they shouldn't be visible.
An example (AClass.m):
#import "AClass.h"
#interface AClass()
- (void)someMethod;
#end
#implementation AClass
//...
- (void)someMethod
{
// do something here
}
//...
#end
When I import AClass.h to some other class and create an AClass-instance...
AClass *test = [[AClass alloc] init];
Xcode's code-completion shows the "private" method "someMethod":
[test som // Shows someMethod, though it shouldn't be visible here
At that point, this method is visible, even if it shouldn't be, because it's unknown here - it's not defined in the header-file. If I send the message [test someMethod] at that point and build the thing, I get a warning, that the object might not respond - as expected.
It actually does respond, but this is confusing behaviour, especially for someone else, who wants to use my class.
That affects #property / #synthesize as well, since they "just substitute methods". I want to access all of my private ivars by properties for a) having homogene code while b) being able to influence the use of ivars (like lazy instantiation). On the other hand all private stuff shouldn't be visible (in code completion) to anyone using my classes, to make it easier to use them.
Is there any way to change this behaviour?
Is the missing validation of context in Xcode 3.2 the reason, why code-completion shows this kind of methods, where they shouldn't be visible?
Is that behaviour different in Xcode 4 (because of context-validation)?
I still use Xcode 3.2, because I wanted to finish a project before switching and adapting myself to Xcode 4.

My DetailViewController isn't recognizing the '-configureView'

I had a problem with the splitView so I decided to make my own version of it using a ViewBasedApplication. The view is set up to be like a splitView, but instead of having the static table when you flip it to landscape it will have the popover control like it normally does in the portrait mode.
I have everything set up, but I'm encountering an error when I select an item from the popover table. It isn't updating the view to go to the appropriate page that was selected in the table...the code for the setDetailItem in the ViewController.m file looks like this:
-(void)setDetailTime:(id)newDetailItem{
if(detailitem != newDetailItem) {
[detailitem release];
detailItem = [newDetailItem retain[;
[self configureView];
...
}
The problem is in the [sef configureView] line. it says that "PDFViewController" may not respond to '-configureView'. Im assuming this is because '-configureView' is specific to only the SplitView. Any ideas of a way to workaround this issue?
Thanks!
Your class PDFViewController does not have a configureView method, which is why you're getting the warning (and why you're not seeing anything happen).
You probably need to instead call [self.view setNeedsDisplay] which basically tells the view to redraw itself. I don't know if this will work for you, because I don't know how the rest of your class is written.

Resources