NSTextField sizeToFit or frame update shift text to left - macos

I have a strange issue with sizeToFit method on a NSTextField.
I have a NSView where I'm creating a CALayer and a NSTextField. I use the sizeToFit method to resize my CALayer according to the field value. It works well, but when I'm inserting a space, the text shifts on the left inside the field's frame (see image below).
Both layer's and text field's frames are positioned well (origin isn't moving).
Any clue on this would be highly appreciated.
EDIT
Frame update has the same behavior, even when not using sizeToFit.
Sometimes it's shifting when inserting a space, or sometimes it's coming back to its wanted position when inserting a space.
The underlying layer doesn't move from its origin.
EDIT
It looks like the container of the NSTextField needs enough space to display.
What I discovered is that it needs actually a lot of space.
We can't access the container of a NSTextField, so I add a large value to the width of the string (depending on the size of the font) before calling a frame update or [self sizeThatFits:stringSize]; and [self sizeToFit];.

I think there's no need in -sizeToFit-method. It's automatically called when layout performs. If you need to adjust size of your NSTextField-instance, subclass it and override -intrinsicContentSize-method.
I used this approach with "fade out"-text mask at the end of the text field. Using autolayout and intrinsic content size my textfields were always with that mask even if on the parent view was enough space for layout.
- (CGSize)intrinsicContentSize
{
CGSize size=[super intrinsicContentSize];
// adjust size somehow (or may be not)
return(size);
}
Each time you need to "update" text field size (while typing?), call the -invalidateIntrinsicContentSize.

Related

Custom NSView in a NSScrollView that takes up the whole visible space

I have a custom NSView subclass, and I put it in a NSScrollView, and the basics are working fine. It always takes up the full width of its available space, so its constraints take up the whole width of the scroll view. How much vertical space it takes up depends on how much data it has to display, so it can scroll vertically, only. That's all great.
The catch is that if my custom view needs less space than the NSScrollView that it's in, it doesn't expand to fill the visible area. In cases where there's less data than fits in the visible area, I want it to expand downwards -- so that space is available as a drag-and-drop target, among other things.
I've tried changing its "hugging priority". I've tried adding a constraint to keep the bottom below the NSClipView's bottom. I can't come up with any constraint-based solution to fix this (though I haven't ruled out the possibility, either).
I've tried catching the notifications when the NSScrollView changes size, and adjusting the custom view's frame if it's too small, but (presumably because I can only change its current frame, while the layout system does all layout later) I can't seem to make this work, either.
Is there a trick for adding a view to an NSScrollView such that it expands to the bottom of the visible area, whenever it would otherwise be too short? It seems like it should be so simple, and I've done it before in cases where I just call -setFrame on everything manually, but once you move to the autolayout world, that approach stops working.
Without knowing what your constraints and content hugging and content compression actually look like...
Content hugging with at least one edge having >= constraint to superview might do it but you might need to adjust priorities.
You might also need to make sure you've implemented intrinsicContentSize in your custom view class this tells the content hugging and compression what they need to knowfirst.

How to stop interface builder resetting user constraints on UIScrollView?

I'm having trouble getting a UIScrollView to respect the constraints I put in interface builder.
All I need to be able to do is set the content size of the scroll view from within IB.
The UIScrollView contains a single UIView.
Constraints on the UIScrollView:
Constraints on the UIView:
I've read through the documentation, and so have set things up as follows:
the UIScrollView has constraints pinning it to its superview, thus defining its size from outside
the UIView (content) has a fixed size (through width and height constraints)
the UIView is pinned to the UIScrollView, thus defining the content size
However, IB won't let me enter these constraints. If I change the 'Bottom Space' constraint between the view and the scroll view, shown in the image as -2196, to 0 (thus pinning the lower edge of the scroll view), then the 'Top Space' constraint resets to a non-zero value. The same happens in reverse. (I haven't yet tried in Xcode 5, which has a far saner approach to invalid constraints in that it doesn't just throw yours away when it feels like it.)
What am I missing?
Every time I've tried to do something even mildly sophisticated with constraints in Xcode 4's Interface Builder, I've eventually given up and either written the constraints in code or switched back to springs'n'struts and layoutSubviews (usually after crashing Xcode a few times).
That said, there is another approach to laying out a scroll view with content in IB. Just make the scroll view as big as its content size, and rely on the view controller (or some containing view controller) to resize the scroll view (or its superview) and let the constraints shrink down the scroll view's frame at runtime. The window's root view controller will always set its view's frame to the screen size, regardless of its size in the nib or storyboard, and that resizing flows down the view hierarchy.
I described this approach in more detail in this answer.
If your scroll view's content size is really supposed to be 2196 points tall, this probably won't work so well. I don't have anything better to suggest in that case.

How to resize NSTextField dynamically?

I want an NSTextField to get bigger as the user types, and smaller as backspace is pressed etc.
The only thing I've found is [textField sizeToFit] but that doesn't run dynamically, it's just a one off resizing. Is there a property or something I need to set?
p.s. I only need it to resize horizontally (single line text).
What Richard said, except you need to know by how much to resize your textfield. To get this value, use the the layout manager of the NSTextField and call something like boundingRectForGlyphRange:inTextContainer: on it with a range of all glyphs. This should tell you the dimensions of the rectangle bounding all of your text. Take these dimensions and set them to the width and height of your NSTextField content.
Set a delegate on the NSTextField which implements
- (void)controlTextDidChange:(NSNotification *)aNotification
Every time the delegate gets called resize the control per your idea above.

NSTextView's adjustScroll method

I'm trying to implement typewriter scrolling in my Cocoa text editor, keeping the insertion point centered vertically in its scrollview.
Toward this end, I have subclassed NSClipView to provide a scrollToPointWithoutConstraint method, which scrolls the document to a specified point without calling constrainScrollPoint. This is necessary because for short documents the insertion point can't be centered unless we scroll beyond the document's bounds.
This seems reasonably straightforward so far and does what I want. The problem comes in when I try to scroll using the scroll bars. If I'm scrolled to the end of the document, such that part of the scroll view contains an area outside the document's bounds, trying to scroll up by a small increment causes the scroll view to jump, immediately clamping to the document's actual bounds.
I gather that I might need to subclass NSTextView and override the adjustScroll method; this is where my actual question begins. The proposedVisibleRect that is passed to adjustScroll already has its dimensions adjusted so that they lie within the document's actual bounds. Is there a way that I can change the value of proposedVisibleRect before adjustScroll is called? Alternatively, am I going about this entirely wrong? Any suggestions would be greatly appreciated at this point.

Scrolling an NSTextView to an exact rect?

I've got an NSTextView, inside an NSScrollView, and I want to scroll it to exactly where it used to be after repopulated the NSTextView with new data. So far, I've tried variations on the code below, but it never gets it quite right.
What I need is a setDocumentVisibleRect method, but there isn't one.
NSRect oldVisibleRect = [[[self scrollView] contentView] documentVisibleRect];
[Code to repopulate data]
[[[self scrollView] contentView] scrollToPoint:newPoint];
Any ideas?
You'll need to be more specific - what about this isn't "quite right"?
-[NSView scrollRectToVisible:]
Scrolls the receiver’s closest ancestor NSClipView object the minimum distance needed so a specified region of the receiver becomes visible in the clip view.
NSTextView is a subclass of NSView (though you should always consider whether a given view has flipped coordinates...), so you can use -scrollRectToVisible:
Also, unless your text view content changes are pretty much exactly the same as before, the old visible rect isn't going to be quite the same, so I wouldn't expect it to behave perfectly. If you're only applying attributes (like syntax highlighting) to the text, there'd be no need to do anything to the text view (the text storage or container) that should require a complete reload (and re-scroll). I guess it boils down to "what exactly are you trying to do?"

Resources