NSTextView displaying HTML via Cocoa Bindings - macos

I've got a string with some simple HTML in it (mainly just and tags). I'm displaying them in an NSTextView using Cocoa Bindings. Unfortunately the html just shows up inline, it doesn't actually change the formatting. Is there a way to do get the html to render using bindings? If not, how would you do it?

Turns out it's fairly straightforward. The NSTextView bindings panel in Interface Builder has a "Value" section at the top, and the first choice is Attributed String. Just bind the Attributed String to an NSAttributedString property on your model object. You can create the attributed string from a normal string with html tags in it like so:
NSData *data = [htmlString dataUsingEncoding:NSUTF8StringEncoding];
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithHTML:data
baseURL:nil
documentAttributes:nil];
I don't know how much HTML you can cram in there (maybe javascript doesn't work) but basic stuff like stylesheets seems to work fine.

Swift 5:
let data = htmlString.data(using: .utf8)!
if let attributedString = NSAttributedString(html: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding:String.Encoding.utf8.rawValue], documentAttributes: nil)
{
textView.textStorage?.append(attributedString)
textView.textColor = .labelColor
textView.font = NSFont.systemFont(ofSize: 15)
}

Related

Cocoa NSTextField - binding and setting attributes

I've got a xib with a Label (TheLabel)... which is an NSTextField. It's text is not editable by the user.
I have it's value bound to an NSString* in my controller class.
I have it's font bound to a NSFont* in my controller class.
I can change the NSString in my controller class and I see it reflected in the label.
I can change the NSFont in my controller class and I see that reflected in the label.
But...
I can't for the life of me figure out how to turn on and off underlining.
If I call this function...
-(void)setUnderlineType:(NSNumber*)underline
{
NSMutableAttributedString* content = [[TheLabel attributedStringValue] mutableCopy];
[content addAttribute:NSUnderlineStyleAttributeName value:underline range:NSMakeRange(0, content.length)];
[TheLabel setAttributedStringValue:content];
}
... I get an underline, but then the bound font is ignored and I get some standard font. From then on, changing the NSFont in my controller has no visible effect on the NSTextField.
I tried removing attributes from 'content' before adding the underline... removing the font attributes... but that doesn't work either.
Any time I call this function, the font that is bound to the NSTextField become 'ignored' and I see a standard font is a standard size.
Any guidance would be greatly appreciated.
You need to set NSFontAttributeName to update a font of NSAttributedString.
NSFont *font = ...;
NSMutableAttributedString* content = [[theLabel attributedStringValue] mutableCopy];
[content addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, content.length)];
[theLabel setAttributedStringValue:content];

Can a non-editable NSTextView highlight links using setAutomaticLinkDetectionEnabled?

I've been using a NSTextView to display some non-editable text and would like to highlight any links within it's string. I've seen some code that parses out the links and adds attributes. That would work fine, but I was wondering if I could somehow reuse the built-in link detection somehow.
I've tried setting:
[textView setEnabledTextCheckingTypes:NSTextCheckingTypeLink];
[textView setAutomaticLinkDetectionEnabled:YES];
and using:
[textView checkTextInDocument:nil];
after setting the string.
For the sake of completeness, here is how I've manually added links to a NSTextView:
- (void)highlightLinksInTextView:(NSTextView *)view {
NSDataDetector *linkDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:nil];
NSArray *matches = [linkDetector matchesInString:view.string options:0 range:NSMakeRange(0, view.string.length)];
[view.textStorage beginEditing];
for (NSTextCheckingResult *match in matches) {
if (!match.URL) continue;
NSDictionary *linkAttributes = #{
NSLinkAttributeName: match.URL,
};
[view.textStorage addAttributes:linkAttributes range:match.range];
}
[view.textStorage endEditing];
}
Unfortunately you have to call this every time you set the NSTextView string.
I recently stumbled upon this and created an NSTextView subclass, LinkDetectingTextView.swift. Hope this helps someone in the future.

NSOutlineView source list style, view based, change font

I'm using an NSOutlineView with source list style, and using the view based (rather than cell based) outline view.
I would like to be able to make some rows bold. However, my attempts to change the font (manually in IB, through code in viewForTableColumn:…, or through the Font Bold binding) have so far been ignored.
From this message, it appears that this is because the source list style for NSOutlineView takes over managing the text field's appearance:
I'm guessing that you've hooked up your text field to the textField outlet of the NSTableCellView? If so, I think you might be running into NSTableView's automatic management of appearance for source lists.
Try disconnecting the text field from the textField outlet and see if your custom font sticks.
If I disconnect the textField outlet, the appearance does come under my control, and my emboldening works.
However, now I can't get it to look like the automatic one. By which I mean, when NSOutlineView was managing the text field's appearance, the font was bold and gained a drop shadow when any item was selected, but when I'm managing it manually this is not the case.
Can anyone answer either of these questions:
How can I get the Font Bold binding to work when NSOutlineView is managing the appearance of my text field
If I don't have NSOutlineView manage the appearance of my text field, how can I make it look and behave like it would if I did have it manage it?
I think I found the solution:
NSTableCellView manages the appearance of it's textField outlet by setting the backgroundStyle property on cells of contained controls. Setting this to NSBackgroundStyleDark triggers a special path in NSTextFieldCell which essentially sets an attributedStringValue, changing the text color and adding an shadow via NSShadowAttributeName.
What you could do is two things:
Set the backgroundStyle on your own in a custom row or cell view subclass.
Use a custom NSTextFieldCell in the cell's text field and change the behavior/drawing.
We did the latter since we needed a different look for a themed (differently colored) table view. The most convenient (albeit surely not most efficient) location we found for this was to override - drawInteriorWithFrame:inView: and modify the cell's attributed string before calling super, restoring the original afterwards:
- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
NSAttributedString *originalString = self.attributedStringValue;
// Customize string as you like
if (/* whatever */)
[self setAttributedStringValue: /* some string */];
// Regular drawing
[super drawInteriorWithFrame:cellFrame inView:controlView];
// Reset string
if (self.attributedStringValue != originalString)
self.attributedStringValue = originalString;
}
In the hope this may help others in similar situations.
Not sure if I have missed anything in your question but changing the font using the following works for me. ReminderTableCellView is just a subclass of NSTableCellView with an additional dateField added.
- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item {
//LOG(#"viewForTableColumn called");
// For the groups, we just return a regular text view.
if ([_topLevelItems containsObject:item]) {
//LOG(#" top level");
NSTableCellView *result = [outlineView makeViewWithIdentifier:#"HeaderCell" owner:self];
// Uppercase the string value, but don't set anything else. NSOutlineView automatically applies attributes as necessary
NSString *value = [item uppercaseString];
[result.textField setStringValue:value];
//[result.textField setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
return result;
} else {
//LOG(#" menu item");
// The cell is setup in IB. The textField and imageView outlets are properly setup.
// Special attributes are automatically applied by NSTableView/NSOutlineView for the source list
ReminderTableCellView *result = [outlineView makeViewWithIdentifier:#"DataCell" owner:self];
if ([item isKindOfClass:[OSTreeNode class]]) {
[result.textField setFont:[NSFont boldSystemFontOfSize:13]];
result.textField.stringValue = [item displayName];
result.dateField.stringValue = [item nextReminderDateAsString];
}
else
result.textField.stringValue = [item description];
if (_loading)
result.textField.textColor = [NSColor grayColor];
else
result.textField.textColor = [NSColor textColor];
NSImage *image = [NSImage imageNamed:#"ReminderMenuIcon.png"];
[image setSize:NSMakeSize(16,16)];
[result.imageView setImage:image];
//[result.imageView setImage:nil];
return result;
}
}
Resulting view is shown below. Note this is is an NSOutlineView with Source Listing option selected but I can't see why this would'nt work for a normal outlineView.

HTML in textview

I am using textview in cocoa
How we show a HTML data in textview
You didn't say if you're targeting the Mac or the iPhone. I'll assume the Mac since you tagged your question with cocoa.
It would be worth looking into the Text System documentation for an overview of how NSTextView works. But at a basic level, all NSTextViews have an NSTextStorage object (accessible via the textStorage method) NSTextStorage happens to be a subclass of NSMutableAttributedString.
NSAttributedString has an initializer called initWithHTML:baseURL:documentAttributes: which will parse an HTML string for you. Once you've got that, you can append the resulting string to your NSTextView's textStorage. For example:
NSData *htmlData = // get the html, e.g. from a file
NSURL *aBaseURL = // a URL used to resolve relative links e.g. '/directory/page.html'
NSAttributedString *formattedHTML = [[NSAttributedString alloc] initWithHTML:htmlData baseURL:aBaseURL documentAttributes:NULL];
[[myTextView textStorage] appendAttributedString:formattedHTML];
[formattedHTML release];
You can use an UIWebView and fill it with a string instead of a webpage.
Why not use a WebView (part of the WebKit framework)? Although NSTextView has rudimentary HTML support, WebView gives you all of the power of the modern, standards-compliant, WebKit framework.
If macOS NSTextView
Swift 5:
let data = htmlString.data(using: .utf8)!
if let attributedString = NSAttributedString(html: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding:String.Encoding.utf8.rawValue], documentAttributes: nil)
{
textView.textStorage?.append(attributedString)
textView.textColor = .labelColor
textView.font = NSFont.systemFont(ofSize: 15)
}

Easy way to make a UILabel cover and exactly match contents of a UITextField?

UI crimes aside... I'd like to have a UILabel replace a UITextField after some user input. Is there an easy way to accomplish this so when the UITextField is hidden its value gets replaced by a UILabel that doesn't appear to move...appearing to "set" into the background so to speak?
I've managed to do this by nudging the fields around in IB and making the fonts identical but as I'm slowly getting comfortable with Cocoa I was wondering if there might be a quick trick in the frameworks somewhere?!? Perhaps some way to extract the text's view off the UITextField?
Setting the borderStyle property on the UITextField to UITextBorderStyleNone should make it look a lot like an UILabel.
Basically implementing two of the UITextFieldDelegate methods like so:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
textField.borderStyle = UITextBorderStyleRoundedRect;
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
textField.borderStyle = UITextBorderStyleNone;
}

Resources