NSTextView and Core Data

Core Data rocks. If there are good reasons to build data objects manually, I have not yet seen them. I’ll give the NSTextField instructions in brackets because they’re just so close.

 

Context

The problem I – and several other users – ran into is that it’s straightforward to bind an NSTextField to a Core Data entity, but not the more flexible NSTextView.

Abstract Explanation

An NSTextField displays objects of type NSString. If you want to bind it to a Core Data entity, you set that entity to be of type ‘String’. An NSTextView can display different types of data – the very attraction is that you can display images and text mixed together – which means that it contains objects of type NSData and you need to set the type to ‘Binary data’.

That’s all.

Data Model

Anything you want to be displayed in, and bound to, an NSTextView needs to have its type set to ‘Binary data’. (For NSTextField, use ‘String’)

Interface Builder

Create an NSArrayController. In the ‘Attributes’ tab, set ‘Mode’ to ‘Entity‘ and make sure 'automatically prepares content' is checked.
In the ‘Bindings’ tab, bind the managedObjectContext to the App Delegate’s managedObjectContext

Create an NSTextView. (Make sure you select the NSTextView rather than the surrounding scroll view). In the Inspector’s ‘Bindings’ tab, bind its data to your NSArrayController and set the model Key Path to the entity you wish to display. If your entity is set to ‘string’ it will throw an exception, so make sure it’s of type ‘binary data’.

Linkage

(to be implemented)

Antidotes

none

Entanglements

  • Core Data
  • Text Attributes

Areas

  • Core Data
  • Bindings
  • Text handling

Setting Font Attributes

This snippet contains an overview of the method by which font attributes are handled. There are many possible attributes that can be set, and I’ll cover them, and their methods, in another snippet.

 

Context: Text handling

Cocoa gives you a font menu, font management functions, and All That Jazz for free. That’s the easy way for a user to set font attributes. (For a much more detailed Overview see the Text Handling Overview)
If you want to allow users to set a default font, use a font pallette or other ways for the user to style text, this will need to be done in code.

Underlying Principle

You create a font object with the attributes of the font currently in the text field, manipulate that object, and apply it back to the text field. At least, the examples in _David Chisnall: Cocoa Programming Fundamentals_ work on this basis. It’s one of the things that leave me wondering whether this is the only way *to* set font attributes, the best way, or what.
On a further tentacle, this appears to be the same principle that works on creating user-defined styles and default styles: instead of creating a temporary object you manipulate, apply, and release immediately, you store it (I have no idea in which form) so you can _then_ apply it to text.

Prerequisites

Access to an/the? instance of NSFontManager (Apple Developer Library Link), which – if I understand it correctly – is a singleton, so there should be only one.

In _David Chisnall: Cocoa Programming Fundamentals_, this is solved through a class initialisation method, but don’t ask me to explain the exact reasoning behind it or under which circumstances this is a good or bad idea.

NSFontManager *fm;
@implementation FontController
+ (void) initialize
{
fm = [NSFontManager sharedFontManager];
}[/obc]
(I'm recording it because it's important, but I make no claims to actually understanding the reasoning behind this.) 

<h3>Setting Font Attribute</h3>
First, get your font attribute. In this example I'll stick with the font family.
<h4>Interface Builder</h4>
To get all available font families (allegedly), bind the contentKey of an <code>NSComboBox</code> to the 'availableFontFamilies' key of the FontManager. (I say allegedly because when I tried this in Xcode 2.5 I got a menu populated with a selection of my fonts, out of order. I don't have the opportunity to test this in Xcode 3.5 right now.)
<h4>IBAction</h4>
[objc]- (IBAction) takeFontFamilyFrom: (id)sender
{
NSFont = *font [text font];
 font = [fm convertFont: font
		toFamily: [sender stringValue]]; //sender = NSComboBox
[text setFont: font];
}

(_David Chisnall: Cocoa Programming Fundamentals_)
It becomes obvious here that the convertFont: toFamily: method needs a font object to work on.

Caveats

I am not certain about the principle, but I’m writing it down anyway lest I lose sight of it. Create font object-manipulate it – apply to text field makes sense to me.

Linkage

(to be implemented)

Antidotes

  • Font Menu

Entanglements

  • Font Attributes
  • User-defined Styles

Areas

  • Text Processing

Drawing in Custom Views

(This is a draft and will change as my understanding grows)

 

Context: NSView subclass

The action here takes place in an NSView, which needs to be inside an NSWindow. Cocoa offers NSView subclasses as one of its class templates.

Function Logic

When the drawing is initiated by the application (mathematically calculated lines, or the user has interacted with a dialog and clicked ‘ok’),

- (void)drawRect:(NSRect)rect

is called directly. (This seems to be a catch-all drawing functions, others are available, but I’m not certain when to call them, since this one is used for bezier curves as well.)

Anyway, you call the drawing function, and it draws.

The sample in Collecting Mouseclicks uses a different method: it has a function that runs quite happily until a certain condition is fulfilled – it has collected four mouseclicks (but anything could be the trigger), after which

[self setNeedsDisplay: YES];

is called, which in turn calls

- (void)drawRect:(NSRect)rect

This appears to be a good example of encapsulation: instead of calling a drawing function directly, you tell the view ‘do whatever you need to do to draw yourself’ which might, conceivably, include housekeeping, even though I have no idea what that might consist of.

Linkage

Collecting Mouseclicks

Antidotes

none

Entanglements

  • Views in general

Areas

  • Drawing

Collecting mouseclicks

This is a common challenge in drawing-related applications: the user clicks their mouse, and the application does something with those coordinates.
<Assumption> If you have already drawn an object, the code will be different, because now you have an object that knows its own coordinates and that should be able to tell when it’s clicked.</Assumption>

 

Context: NSView subclass

The action here takes place in an NSView, which needs to be inside an NSWindow. Cocoa offers NSView subclasses as one of its class templates.

Declaration in view.h

NSPoint points[4];
	/*array that can store four points*/
NSUInteger pointCount;
	/*unsigned since the number in an array
can never be negative*/

Function Logic

The user clicks the mouse; the application stores the clicks in the points array. When it has four points, it tells itself that it needs to redraw

[self setNeedsDisplay: YES];

which triggers whatever sits inside

- (void)drawRect:(NSRect)rect

Instead of doing that, the points could be stored or manipulated further.

Function

- (void)mouseDown: (NSEvent*)theEvent
{
	NSPoint click = [self convertPoint: [theEvent locationInWindow]
							 from View:nil]; /*from this view
rather than another one in the same window*/
	points[pointCount++ % 4] = click; /*keep going until you have four (see note below)*/
	if (pointCount % 4 ==0)//if you have four, do this
	{
		[self setNeedsDisplay: YES]; /* this triggers whatever
code sits in - (void)drawRect:(NSRect)rect */
	}
}

[copied from David Chisnall: Cocoa Programming Fundamentals]
For a closer explanation of points[pointCount++ % 4] = click see this post

Caveats

The % syntax is, as I ought to have known, the modulo operator.
I wasn’t able yet to verify that this actually runs, since I’ve had trouble linking the view to the Window following the example I was given (wrong version of Xcode, too much hassle right now.)

Linkage

(to be implemented)

Antidotes

none

Entanglements

  • click-and-drag
  • double-click
  • rightclick

Areas

  • User Input
  • Drawing

Test Snippet

Sorry, I just needed a second snippet to display on the snippets page.

Custom posts, it seems, are different from ordinary posts in two important ways:

You cannot use the ‘more’ tag, and the headline does not get turned into a link for the post, thus making it impossible to access a post directly.

I’m pretty sure that I’ve seen this done in examples, but I haven’t found an explanation for it.

And after spending upwards on five hours, stretched over several days, working out how to use custom posts, I have to say that I’m quickly losing interest in investing even more time and effort into this feature. You need to mess with the template (add a custom page for posts) in order to display them at all, which is one more thing to remember when you change your template, and, well, argh.

In hunting for a tutorial I’ve found dozens of explanations of how to create custom post types, and two of how to display them, one of which used smartquotes which made copying and pasting his code not so useful (and he omitted half the explanations); while the other uses css that does not show highlighting of text, so cut-and-paste was adventurous.

It’s not just Cocoa, but it’s full of the same frustrations: I want to do a very simple thing (create a custom post type and display it) and find that a) it takes hours hunting down a working explanation of what I ought to do, and that b) there’s additional information (in this case, what custom posts do and don’t support) that I would have needed from the start.

Opening Windows

Windows probably don’t need much introduction. They’re common interface elements, they can contain all kinds of content (from a elements the application developer provides to input fields to views displaying text or graphics). A lot of window behaviours are things you get for free in Cocoa – you don’t need to track whether a mouseclick is on the scroll bar or which area of the window a user has just selected; these things are tracked for you. (This is OOP, so ‘the object knows it’s being interacted with’).

 

Automatically Opening a Window

You can set the attribute of a window to ‘Visible at launch time’ in IB and it will automatically open. This is useful, but standard windows/panels behave like all Mac windows/panels: they have a close button and respond to

[command] + W

. If you add a window to your application, you need to either disable the options to close it, or provide ways of opening it again.

Opening Windows in IB

You can open windows in IB without writing any further code. Link a control (commonly a button) to the window’s

makeKeyAndOrderFront:

method. The key window is the window that accepts key strokes, it is usually – but not always – the front window. (If you have a find dialogue in a text processing document, the document would be the front window, but the find dialogue would be the one accepting your key input.)

Opening a Window using code

If you want to open a window, you call its

makeKeyAndOrderFront:

method, or alternatively its

orderFront:

method. I’m giving an example for using an IBAction for this, since connecting menu items or buttons is such a common way of interacting with them.

Add the following to your .h file:

@interface AppController : NSObject
{
    IBOutlet id programmedWindow;
}

- (IBAction) showWindow: (id)sender;

@end

The implementation in your .m file looks as follows:

@implementation AppController

- (IBAction) showWindow: (id)sender
{
	[programmedWindow makeKeyAndOrderFront:sender];
}
@end

That’s it.

Linkage

(to be implemented)

Antidotes

  • performClose:
  • Related content

    • Window handling (sizing, positioning etc)
    • Opening documents

    Entanglements

  • All 300-odd or so methods that windows respond to
  • Areas

  • User Interface