Tooltip for view based NSTableView - cocoa

I have a tooltip:
cellView.textField.toolTip = cellView.textField.stringValue;
It shows always, but I need to show tooltip only if text clipped... How I can achieve it?

Set allowsExpansionToolTips property to YES for Table View Cell in xib.

I think you can do this by implementing the text field delegate method, controlTextDidEndEditing, and checking the size of the text. I found that the size returned by sizeWithAttributes: didn't match the size I would have expected when I filled the text field, so I just determined the value I needed for my if statement empirically (in this example I had the text field's value bound to a property, theText).
-(void)controlTextDidEndEditing:(NSNotification *)obj {
NSLog(#"%#",NSStringFromRect([obj.object frame]));
NSDictionary *dict = [NSDictionary dictionaryWithObject:[NSFont systemFontOfSize:13] forKey:#"NSFontAttributeName"];
NSSize size = [theText sizeWithAttributes:dict];
NSLog(#"%#",NSStringFromSize(size));
if (size.width >69) {
[obj.object setToolTip:theText];
}
}

Try: allowsExpansionToolTips (in Swift, Obj-C)
Expansion tooltips are shown when the cell cannot show the full
content and the user hovers the pointer over the control.

Related

Calculating width of checkboxes in NSOutlineView

I'm programmatically setting up a multi-column NSOutlineView. The first column contains only checkboxes whereas the other columns contain text. I'm creating the checkboxes like this:
cell = [[NSButtonCell alloc] init];
[cell setButtonType:NSSwitchButton];
[cell setImagePosition:NSImageOnly];
Now I want to adjust the width of the first NSTableColumn to the exact width required by a checkbox. To calculate the dimensions of a checkbox cell, I do the following:
NSCell *cell = [m_view preparedCellAtColumn:0 row:0];
unsigned cellWidth = [cell cellSize].width;
This returns 18 in cellWidth. This is probably the right value but it isn't sufficient because NSOutlineView always seems to insert some blank space before the cells of the very first column. Here's what it looks like:
As you can see the checkboxes are currently cut off because the column width is too small because of the blank space in front of the checkbox.
Therefore, here is my question: How can I calculate the width of this blank space for my first NSTableColumn so that I can calculate the full column width required to show the checkbox and blank space? Is there maybe also a way to get rid of this blank space?

NSTextView changing Insertion Point (Caret) Cursor size when there is no string

I am creating a NSTextView, and I want to change the size of the insertion point when the NSTextView is empty.
It appears that the caret cursor (I-Beam / IBeam cursor) size is based off of the current font size attribute on the attributedString property of the NSTextView.
The problem is that because the NSTextView has an empty attributedString, there can be no attributes on the string. This means that I can't apply a font size.
However, I can change the typingAttributes to have the fontSize be correct-- but the Insertion Point Cursor does not update size until I start typing
See these screenshots:
Notice here the textview on the right has a small insertion point cursor:
I start typing, and it updates in size:
So my question is, is there a property that I need to set on the NSTextView when I initialize it, in order to get the initial Insertion Point Cursor the correct Size? (I want it to match the typingAttributes font)
EDIT:
#Mark Bessey brought up a good question-- what is the order that I'm setting the typing attributes:
[_textEditor setTypingAttributes:typingAttributes];
[_textEditor setDelegate:self];
[view addSubview:_textEditor];
[self updateInsertionPointColor];
[[view window] makeFirstResponder:_textEditor];
[view setNeedsDisplay:YES];
I just ran into the same problem and – after not finding a good solution anywhere – managed to force the NSTextView to update its pointer by inserting a character and then replacing that character with an empty string in the same cycle.
I'm doing this in textView(_:shouldChangeTextIn:replacementString:), but I guess it works elsewhere just as fine.
let attributedString = NSAttributedString(string: " ", attributes: attributes)
let range = someRange
let insertLocation = range.location + range.length
textView.textStorage?.insert(attributedString, at: insertLocation)
textView.typingAttributes = attributes
textView.replaceCharacters(in: range, with: "")
Doing this works in a simple test app, in my applicationDidFinishLaunching method:
NSFont *font = [NSFont fontWithName:#"Marker Felt" size:24.0];
NSDictionary *attr = #{
NSFontAttributeName: font
};
[self.textView setTypingAttributes:attr];
I get the large I-beam cursor that I'd expect.
This is basically the same as what TextEdit does (sample code here), so it doesn't look like you're doing anything wrong, as far as setting the typingAttributes goes.
I wonder if perhaps you're setting the attributes at the wrong time. When do you set the typingAttributes? Is the view on-screen? Is it the first responder?
To changed the attributes when there is no text, using typingAttributes.
Typing attributes are reset automatically whenever the selection changes. However, if you add any user actions that change text attributes, the action should use this method to apply those attributes afterwards. User actions that change attributes should always set the typing attributes because there might not be a subsequent change in selection before the next typing.
#IBOutlet var targetTextView: NSTextView!{
didSet {
let attributes:[NSAttributedString.Key:Any] = [
.font:NSFont.userFont(ofSize: 16.0) ?? NSFont.systemFont(ofSize: 16.0),
.foregroundColor:NSColor(named: "targetColor")!
]
targetTextView.typingAttributes = attributes
}
}

Separate NSPopUpButton content from label while using bindings

I have an NSPopupButton whose content is bound to an NSArray, let’s say the array is
#[
#"Option 1",
#"Option 2"
];
Its selected object is bound to User Defaults Controller, and is written to a preference file by the user defaults system.
In my code I check whether the preference is set to #"Option 1" or not, and perform actions accordingly.
This all worked well (though I did feel a little uneasy checking for what is essentially a UI value, but whatever...) until I needed to localize.
Because the value is the label, I’m having an issue.
If my user is in France, his preferences file will say #"L’option 1", which is not equal to #"Option 1". I need to abstract the presentation from the meaning and it's proving pretty difficult.
I split up the binding into two arrays, let's call them values and labels.
Let’s say they look like this:
values = #[
#"option_1",
#"option_2"
];
labels = #[
NSLocalizedString(#"Option 1", nil),
NSLocalizedString(#"Option 2", nil)
];
I’ve bound the NSPopUpButton’s Content binding to values and its Content Values binding to labels. However, the popup list is showing option_1 and option_2, it does not seem to want to use the labels array to label the items in the popup button.
How do I get the NSPopUpButton to use values internally and store that in the preferences file, but display labels to the user?
It doesn’t have to be architected this way, if you can think of a better solution. The point is I want to store and check one value, and have that value associated with a label that gets localized appropriately.
Cocoa bindings work very well with value transformers, because you can apply them directly in the bindings window, for example
#implementation LocalizeTransformer
+ (Class)transformedValueClass
{
return [NSArray class];
}
+ (BOOL)allowsReverseTransformation
{
return NO;
}
- (id)transformedValue:(id)value {
if (![value isKindOfClass:[NSArray class]]) return nil;
NSMutableArray *output = [NSMutableArray arrayWithCapacity:[value count]];
for (NSString *string in value) {
[output addObject:NSLocalizedString(string, nil)];
}
return [output copy];
}
#end
you have to register the transformer in awakeFromNib or better in +initialize
NSValueTransformer *localizeTransformer = [[LocalizeTransformer alloc] init];
[NSValueTransformer setValueTransformer:localizeTransformer
forName:#"LocalizeTransformer"];
then it appears in the popup menu of value transformers
Bind Selected Tag to your User Defaults Controller instead of Selected Object.
If the NSPopupButton choices are fixed add the NSMenuItems in Interface Builder and set their Tags. Otherwise bind an array of NSMenuItem, again with proper Tags.
Selected Index would also work but only until you change the order.

How do I change the shape of UITextView in Xcode 5?

Instead of having a normal text view, I want it to be another shape, for instance like the Text Field. The reason to why I am not using Text Field is because I want the text view to be uneditable.
Suggestions on how to change the shape?
Thanks.
If what you really want to do is use a UITextField you have a few options:
textField.enabled = NO; // create an outlet to your text field and put this in ViewDidLoad to prevent editing but also selecting and copying
implement the delegate method: // this will allow copy/paste, etc. but no writing
(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
return NO; }
If you want to use a UITextView, the only shape items you can change are the width and height (you can't make it star shaped or anything). If you want rounded corners you could use QuartzCore as follows:
[textView.layer setBorderWidth:2.0]; // not needed but put here in case
//Round the corners via a radius value`enter code here` (play with number to get what you want)
textView.layer.cornerRadius = 5;
textView.clipsToBounds = YES;
Note: UITextView gives you the added benefit of multi-lines and scrolling.
If you just want to display a value that can be read and copied then use a UILabel

How to let NSTextField grow with the text in auto layout?

Auto layout in Lion should make it fairly simple to let a text field (and hence a label) grow with text it holds.
The text field is set to wrap in Interface Builder.
What is a simple and reliable way to do this?
The method intrinsicContentSize in NSView returns what the view itself thinks of as its intrinsic content size.
NSTextField calculates this without considering the wraps property of its cell, so it will report the dimensions of the text if laid out in on a single line.
Hence, a custom subclass of NSTextField can override this method to return a better value, such as the one provided by the cell's cellSizeForBounds: method:
-(NSSize)intrinsicContentSize
{
if ( ![self.cell wraps] ) {
return [super intrinsicContentSize];
}
NSRect frame = [self frame];
CGFloat width = frame.size.width;
// Make the frame very high, while keeping the width
frame.size.height = CGFLOAT_MAX;
// Calculate new height within the frame
// with practically infinite height.
CGFloat height = [self.cell cellSizeForBounds: frame].height;
return NSMakeSize(width, height);
}
// you need to invalidate the layout on text change, else it wouldn't grow by changing the text
- (void)textDidChange:(NSNotification *)notification
{
[super textDidChange:notification];
[self invalidateIntrinsicContentSize];
}
Swift 4
Editable Autosizing NSTextField
Based on Peter Lapisu's Objective-C post
Subclass NSTextField, add the code below.
override var intrinsicContentSize: NSSize {
// Guard the cell exists and wraps
guard let cell = self.cell, cell.wraps else {return super.intrinsicContentSize}
// Use intrinsic width to jive with autolayout
let width = super.intrinsicContentSize.width
// Set the frame height to a reasonable number
self.frame.size.height = 750.0
// Calcuate height
let height = cell.cellSize(forBounds: self.frame).height
return NSMakeSize(width, height);
}
override func textDidChange(_ notification: Notification) {
super.textDidChange(notification)
super.invalidateIntrinsicContentSize()
}
Setting self.frame.size.height to 'a reasonable number' avoids some bugs when using FLT_MAX, CGFloat.greatestFiniteMagnitude or large numbers. The bugs occur during operation when the user select highlights the text in the field, they can drag scroll up and down off into infinity. Additionally when the user enters text the NSTextField is blanked out until the user ends editing. Finally if the user has selected the NSTextField and then attempts to resize the window, if the value of self.frame.size.height is too large the window will hang.
The accepted answer is based on manipulating intrinsicContentSize but that may not be necessary in all cases. Autolayout will grow and shrink the height of the text field if (a) you give the text field a preferredMaxLayoutWidth and (b) make the field not editable. These steps enable the text field to determine its intrinsic width and calculate the height needed for autolayout. See this answer and this answer for more details.
Even more obscurely, it follows from the dependency on the text field's editable attribute that autolayout will break if you are using bindings on the field and fail to clear the Conditionally Sets Editable option.

Resources