I want to programatically create an NSTextView. How can I determine the correct frame height so that the view displays one line of text in the current default font?
The NSFont class has a method that can give you the size of a rectangle that would enclose a specific attributed string. Get the font used by your text view, create a string that serves as a reasonable example of what will be in the text view, and use that to inform your frame height. (The frame height will need to be some number of points larger than the actual rectangle the string would be displayed in.)
Alternately, you can get the various metrics from the font and attempt to calculate a reasonable frame from that. That might or might not work; for example, a font like Apple Chancery has a huge amount of variation depending on the glyphs that are being rendered, where they are in a word, and so on; I don't know that you can calculate what the needed size would be in advance without knowing exactly what you were going to render.
It would be more normal to be using an NSTextField than an NSTextView for a single line of text.
With NSTextField, just do the following:
[textField setFont:myFont];
[textField sizeToFit];
Oh, and there is no built-in 'current default font'. If an application has such a concept, it needs to track it itself. The font panel doesn't read or write to anything global, it's used to operate on specific text objects.
Related
So I have an NSTextView with text in it. Some of the parts of the text have a NSTextAttachment.Key so I can find their position in the NSTextContainer. I'm adding notes/annotations to the text right now just by hard coding the frame of the annotation to be under the given part of the text. This fails miserably when two annotations are close to each other(they then overlap), or when an annotation's text position is near an edge of the NSTextView(it then gets clipped and cut off).
What I would really like is to set a rule like, this view(the annotation), likes to be close to this given point, but is flexible. This view does not want to overlap other views or go beyond the bounds of the parent view.
The other rule which may be harder is, this view does not want to cover the NSTextView text. Since that text is not a view itself, one solution may be, since I'll know the font size, and the line height, I could calculate that if say the lines of text are at Y positions 100, 200, 300, etc. Then the annotation could only have Y positions of 75, 125, 175, 225, etc.
Some other notes:
This NSTextView is not editable. It is display only and updates based on changes to another NSTextView, so these positions won't need to be dynamic such that a user is adding text in realtime, rather at fixed intervals the editable textview's content is read, the annotations queried from the backend, and then the text is written into the read-only textview's NSTextStorage, and the annotation NSViews are added to the read-only NSTextView.
MacOS/AppKit only. No UIKit
Here is a diagram of the sort of thing I'm aiming to achieve, and was wondering what is the right tool in the AppKit toolbox to try and achieve this? Can something like AutoLayout work with these types of flexible constraints? Or is my only option to rollout something custom that does all of these calculations, and then gives a fixed NSRect to each annotation's NSView?
You can't do it out of the box but there are couple of things you could look into.
Exclusion paths will help you with not covering the actual text view. Whenever you create the note view, just add an exclusion path which could perhaps cover the whole line (meaning a rect of 0, y, fullContainerWidth, lineHeight).
Using NSPopover instead of normal NSViews. They can pop out of the actual window and won't be clipped outside the screen. There is no way to avoid overlapping with them, though.
There are some popover subclasses (such as SFBPopovers) which allow more flexible positioning.
In either case, you will need to do some maths to avoid overlapping. The best way to do this in your case is probably to enumerate the attributes/text attachments. From there, you can figure out which lines will need to have notes displayed, and calculate in advance how much they will take space, and if you'll have to display them both below and above the line.
Note that you will need to do this as you go along, because the layout and
attribute range rects will change as you add exclusions.
If you decide to go with NSViews, you might want to look into creating a specific container NSView subclass for displaying the notes for a single line. It's much easier to handle positioning the notes in local coordinate space, and to figure out how much space they take. You can then display this specific view on top of your NSTextView where needed.
I am implementing an iChat-like app, using NSTextView to display chat records. The problem is how to change the width of NSTextView automatically based on characters in it. For example, the width of NSTextView is only one character width if there's only one character in it. More than 100 characters will automatically increase the height of NSTextView.
You can use the string addition [-NSString boundingRectWithSize:options:attributes:] to get a bounding rectangle. You might also want to look into the Layout Manager.
I've really searched for an answer to this one but not come up with anything solid so here goes.
I'm dynamically adding UILabels to a view, they could be any size.
I would like the second added one to be placed about 2 pixels below the last regardless whether there is letters that go bellow the line.
I've tried using various method the get the labels frame after it has been sized to font but with no look. The frame always seems to be bigger than the actual text vertically.
Have you guys got any ideas?
First, I'd try setting the label's adjustsFontSizeToFitWidth to NO and see if that it will allow you to mess up the normal proportions, as seems to be your goal.
If that doesn't work, you probably need to draw the string to a CALayer. Create a CALayer and add it to your view's root layer, i.e.:
[yourView.layer addSublayer.yourNewLayerForString].
Set the sublayer's delegate to the class which will do the drawing:
[yourNewLayerForString setDelegate:self];
And implement drawLayer in that class (the one you assigned as delegate). The drawLayer code can be kind of ugly, but you can find examples out there for how to draw a string in a layer. This one looks good: addTextToLayer.
You will then have exact control over the font and string size, and can adjust the layer frame and the view frame to suit your aims.
I have an NSTextField where the font used can be changed by the user.
Although the font's point size remains the same, the actual height of the font is much bigger for some fonts. This means if the user changes the font to 'Zapfino' for example, most of the text is cropped. I would like it so the text in the box always looks roughly the same size.
Also the line height seems to change depending on which font is used meaning they don't line up well and sometimes get pushed down and the bottom gets cropped off.
How can I keep the text size and line height looking the same?
You need NSTextField which is using single line. You can do it in Attributes inspector by checking Uses Single Line Mode.
How to do it:
And now Your text will be like this:
Do programatically:
To change NSTextField height programatically by font size or scale the text to fit the bounds example here.
I have some rather simple code drawing some text into a CGContext. Here is an excerpt (slightly edited).
CGContextSelectFont(context, "Helvetica", 1.5, kCGEncodingMacRoman);
CGContextShowTextAtPoint(context, xpos, ypos, "Hello", 5);
The text renders ok. For some unknown reason, however, the font changes to a smaller size after I click in the view containing the context. Also when I resize the window containing the view the font returns to original size. What is the reason for this?
1.5 points is mighty tiny to begin with. Assuming no other scaling is in effect, that will be one whole pixel and a blurry pixel above it on the screen.
You're probably seeing a bug that I ran into myself: On entry into drawRect:, the current context's text matrix was not the identity matrix. In my case, I saw it contain a scale by 13 on both axes, plus a translation. (Possibly left over from drawing the title bar.) I filed this in Radar as #10585106, in case you want to file your own and cite it.
The solution is to set the text matrix back to the identity transform before trying to draw text.
Once you do that, you'll find that your text will be exactly as tiny as you asked for it to be. You should change your font size to something more reasonable; Core Text contains a function to get the system fonts (from which you can get their sizes), and AppKit's NSFont class contains methods for the same purpose.