Strange Behavior With NSTextField and an NSAttributedString Placeholder - objective-c

I'm adding a placeholder to an NSTextField:
NSTextField *textField = [[NSTextField alloc] initWithFrame:NSMakeRect(0.0, 0.0, 100.0, 30.0)];
NSAttributedString *string = [[NSAttributedString alloc] initWithString:#"Hello, World"];
[textField.cell setPlaceholderAttributedString:string];
This works, but when I focus the text field, the placeholder disappears, even when the stringValue is empty.
If I set the placeholder with just the setPlaceholderString: method, the placeholder remains until the stringValue is not empty.
Is this just bad behavior on AppKit's part or am I missing something?
The above is a simple example. I'm using the attributed string so I can set font and color attributes.

Is it possible that you have specified a binding for the control? If yes, your placeholder attributed string would be overwritten by the binding's null placeholder.
It should be possible then to bind the control programmatically and specify the attributed string as the option for the null placeholder like this:
[textField bind:#"value" toObject:self withKeyPath:#"textFieldValue" options: #{NSNullPlaceholderBindingOption:string}];
One more thing. I found out that it will work from the 10.9 SDK.

Related

Image and Text locations in UIButton

Im trying to put a UIImage and text into a UIButton, by using UIEdgeInset to locate them.
The problem is, changing one of the edgeinset setting will cause both the image and the text to change the position.
Which means, I put the image to the correct position, then I change the titleEdgeInset values, suddenly not only the text but also the image changed the position.
Any one can help out please?
According the Apple's documentation this won't happen. Did you check to make sure you have negative insets and not positive?
The insets you specify are applied to the title rectangle after that rectangle has been sized to fit the button’s text. Thus, positive inset values may actually clip the title text.
This property is used only for positioning the title during layout.
The button does not use this property to determine
intrinsicContentSize and sizeThatFits:.
I hope this may helps you
#property (strong, nonatomic) IBOutlet UIButton *Imagetext;
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:#"Click Me"];
NSTextAttachment *ImageAttachment = [[NSTextAttachment alloc] init];
ImageAttachment.image = [UIImage imageNamed:#"add.png"];
NSAttributedString *attributedWithImage = [NSAttributedString attributedStringWithAttachment:ImageAttachment];
[attributedString replaceCharactersInRange:NSMakeRange(6, 0) withAttributedString:attributedWithImage];
// [attributedString appendAttributedString:attributedWithImage];
[self.Imagetext setAttributedTitle:attributedString forState:UIControlStateNormal];

Changing the font of the selected text in Objective-C

I'm trying to create a custom “Change font” NSPopupButton for a Mac App (not an iOS App). I can detect a change in font selection:
long fontItemIndex = [fontPopup indexOfSelectedItem];
NSMenuItem *fontItem = [fontPopup itemAtIndex:(int)selectedFontItemIndex];
NSString *fontName = [selectedFontItem title];
Given this NSString of a font name, I cannot seem to find out how to actually change the selected text in my NSTextView textView to this new font.
I'm simply dazzled by the official documentation: it seems convertFont:toFamily: is what I need. When I do this:
NSFont *font = [NSFont fontWithName:fontName size:12.0];
[textView setFont:font];
It sets all text in the text view, not just the selected text. But when I do this:
NSFontManager *fontManager = [NSFontManager sharedFontManager];
[fontManager convertFont:[fontManager selectedFont] toFamily:fontName];
it doesn't do a thing. What am I missing?
Inside a NSTextView is a NSTextStorage (a subclass of NSAttributedString) and you’ll have to modify the attribute named NSFontAttributeName.
First get the range where you want to change the font attribute:
NSRange selection = textView.selectedRange;
Now add the font attribute to the selection:
NSFont *font = [NSFont fontWithName:fontName size:12.0f];
[self.textView.textStorage addAttributes:#{NSFontAttributeName: font}
range:selection];
Depending on the contents of your NSPopUpButton it should be enough to call fontWithName:size: with title as the font name to get the just selected font. But if the method you already do doesn’t work, you’ll probably have to get a specific font from the font family name. availableMembersOfFontFamily: on NSFontManager will give you a list of all available fonts. You can use one of them to initialize a specific font.
Take a look at the setFont:range: method on NSText, the superclass of NSTextView.
(The ranges, of course, come from the selectedRanges property on NSTextView.)
This was all I needed to change all the text in my textview.
[textview setFont:[NSFont fontWithName:#"Courier" size:14]];

NSParagraphStyle line spacing ignored

A simple test that is failed: Make a new project with just one subview (UITextView) and put the following in:
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineHeightMultiple = 50.f;
paragraphStyle.lineSpacing = 100.f;
paragraphStyle.minimumLineHeight = 200.f;
paragraphStyle.maximumLineHeight = 500.f;
UIFont *font = [UIFont fontWithName:#"AmericanTypewriter" size:24.f];
self.textView.attributedText = [[NSAttributedString alloc] initWithString:
#"This is a test.\n Will I pass?" attributes:
#{NSParagraphStyleAttributeName : paragraphStyle, NSFontAttributeName : font}];
}
Line spacing is the same as if the attribute were not there. Has anything got this to work successfully? I put in ridiculous numbers just to show that it won't change...
This is a bug in NSHTMLWriter which is the private class which UITextView uses to convert attributedText into HTML. Internally it displays this HTML via a UIWebDocumentView. Read more on the inner workings of UITextView in my writeup here: http://www.cocoanetics.com/2012/12/uitextview-caught-with-trousers-down/
The problem comes from an easy to miss speciality in the font CSS shorthand. If you specify a pixel size with the font shorthand then this sets BOTH the font-size as well as the line-height. Since NSHTMLWriter puts the font AFTER the line-height this causes the line-height to be cancelled out by the font size.
See here for my Radar which includes the full analysis of the bug: http://www.cocoanetics.com/2012/12/radar-uitextview-ignores-minimummaximum-line-height-in-attributed-string/
I suggest you file a bug report as well and mention my Radar #12863734.
I don't know if this is enough for your purposes but I could adjust the line spacing by setting the minimum and maximum line height. Furthermore to use a font I put it into the font property of the text view rather than passing it as the value of NSFontAttributeName in the attributes dictionary. (Maybe this part is not (well) documented?)
About your attributes
lineSpacing is calculated from the bottom of the line to the bottom of the upper line and that space is constrained to values between minimumLineHeight and miximumLineHeight. What I am trying to say is that maybe some values in your attributes are cancelling or overriding others.
Also if you need to just adjust the spacing between line you probably don't need to use paragraphStyle.lineHeightMultiple :)
The code
This worked for me:
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.minimumLineHeight = 35.f;
paragraphStyle.maximumLineHeight = 35.f;
UIFont *font = [UIFont fontWithName:#"AmericanTypewriter" size:18.f];
NSString *string = #"This is a test.\nWill I pass?\n日本語のもじもあるEnglish\nEnglish y Español";
NSDictionary *attributtes = #{
NSParagraphStyleAttributeName : paragraphStyle,
};
self.textView.font = font;
self.textView.attributedText = [[NSAttributedString alloc] initWithString:string
attributes:attributtes];
Additional Notes
There seems to be a situation with Japanese/Chinesse and maybe other characters mixed with alphabet characters in the same line. It will make that line to have a bigger leading to solve that you need to set up the minimum and maximum line height as I did.
You can see the problem when rendering my example without attributes.
Setting maximumLineHeight seems to resolve this issue for me;
CGFloat fontSize = 22.f;
titleLabel.font = [UIFont boldSystemFontOfSize:fontSize];
NSMutableParagraphStyle *paragraphStyle = [[[NSMutableParagraphStyle alloc] init] autorelease];
paragraphStyle.maximumLineHeight = fontSize/2;
titleLabel.attributedText = [[[NSAttributedString alloc]
initWithString:#"This is a test.\nWill I pass?"
attributes: #{ NSParagraphStyleAttributeName : paragraphStyle,
NSFontAttributeName : titleLabel.font}]
autorelease];
For this particular string you need to set paragraphSpacing instead. What's about lineSpacing, I believe it's just not supported yet on iOS.
As nacho4d answered, in iOS 6 you need to use minimumLineHeight and maximumLineHeight and set font directly in UITextView, not in NSAttributedString as line height in that case will be overridden.
Please note that when you set font in UITextView, the "editable" property of UITextView should be set to YES, in other case attributed text would not be affected.
These issues are present only in iOS 6. In iOS 7 and above everything is ok;
In my case, none of the paragraph styling was working. The fix was to set the attributed text on the label AFTER doing any frame adjustments on the label. :)

NSTextField add line spacing

I use NSTextField not NSTextView to receive the user input, but I need to custom the font and textColor and line spacing. I use the code below, it's ok for font and color but I don't know how to set a line spacing.
[self.titleField setTextColor:textColor];
[self.titleField setFont:bold14];
And I also use a NSAttributedString to solve the problem:
NSFont *bold14 = [NSFont boldSystemFontOfSize:14.0];
NSColor *textColor = [NSColor redColor];
NSMutableParagraphStyle *textParagraph = [[NSMutableParagraphStyle alloc] init];
[textParagraph setLineSpacing:10.0];
NSDictionary *attrDic = [NSDictionary dictionaryWithObjectsAndKeys:bold14, NSFontAttributeName, textColor, NSForegroundColorAttributeName, textParagraph, NSParagraphStyleAttributeName, nil];
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:title attributes:attrDic];
[self.titleField setAllowsEditingTextAttributes:YES];
[self.titleField setAttributedStringValue:attrString];
the code above is ok to show a attributed string, but when I delete the string in the textfield and start to input, the words come without any attribute.
How can I input a string in NSTextField with custom font, color and line spacing?
It's best to stay with NSTextField's attribute setting methods instead of an NSAttributedString because then it can send the settings to the field editor. Every text field has an NSTextView (most of the time) "Field Editor"; and the field editor is what is doing the editing.
Your NSAttributedString isn't sticking because you're only telling the textfield to temporarily display that one string. When the field editor pops up the text field (cell) passes on its own attributes like textField.font and textField.textColor but never the NSAttributedString's attributes.
It would be best to use an NSTextView to be able to use -setDefaultParagraphStyle because you're editing multiple lines anyways, from what I see. If you can't, because of performance problems or something else, then:
Subclass NSTextFieldCell, because that's what does all the NSTextField work, and override
- (NSText *)setUpFieldEditorAttributes:(NSText *)textObj
(declared in NSCell) to set up attributes for your field editor the way you want it, so you can send it a line height value through -setDefaultParagraphStyle (and font etc.) yourself. (textObj is the field editor to be set up).

How to control the text color of an NSTextField when it is displaying a placeholder marker?

When the NSTextField (Label) is bound to a controller selection with bindings, and I have specified placeholder values for the Multiple Values Marker, No Selection Marker, etc it draws the text with a gray color that does not show up well on a dark background.
Is there a way to change the text color it uses to display the placeholder text?
Use an attributed string specifying the color you want, like this:
NSDictionary *blueDict = [NSDictionary dictionaryWithObject: [NSColor blueColor]
forKey: NSForegroundColorAttributeName];
NSAttributedString *blueString = [[[NSAttributedString alloc] initWithString: #"test"
attributes: blueDict] autorelease];
Then you can either set the placeholder attributed string directly:
[[field cell] setPlaceholderAttributedString: blueString];
or do it through a binding, for example:
[field2 bind: #"value" toObject: [NSUserDefaults standardUserDefaults]
withKeyPath: #"foo"
options: [NSDictionary dictionaryWithObject: blueString forKey: NSNullPlaceholderBindingOption]];
you can try to make your own "palceholder". I mean your can show a label over your textfield with any text patameters you want.