Reputation: 1319
for example, I have an NSTextField displayed as a label, whose content is: "Hello, Apple! And here I come!".
I want to locate the "Apple" section, and do some operation on exactly the characters above them (for example, check if -mouseDown: event is triggered on them). Can I get the position and size of this word?
Thank you in advance!
According to alastair's answer, I wrote an .m file to test that. Here is the digest:
/* Some definations to identify the complete text and the link */
#define _CFG_AMCHyperLinkTest_Text @"Hello, this is an Apple website!"
#define _CFG_AMCHyperLinkTest_Link @"Apple"
And the actual testing code:
/* test */
- (void)oneTimeInit
{
AMCDebug(@"Pre Text rect: %@\nlink rect: %@",
[AMCTools descriptionWithNSRect:[_labelHyperlink frame]],
[AMCTools descriptionWithNSRect:[_labelLink frame]]);
NSRange linkRange = [_CFG_AMCHyperLinkTest_Text rangeOfString:_CFG_AMCHyperLinkTest_Link];
NSRect boundingRect;
[_labelHyperlink setStringValue:_CFG_AMCHyperLinkTest_Text];
_layoutManager = [[NSLayoutManager alloc] init];
_textStorage = [[NSTextStorage alloc] initWithString:@"Apple"];
_textContainer = [[NSTextContainer alloc] initWithContainerSize:[_labelHyperlink frame]];
[_layoutManager setTextStorage:_textStorage];
[_layoutManager addTextContainer:_textContainer];
linkRange = [_layoutManager glyphRangeForCharacterRange:linkRange
actualCharacterRange:NULL];
AMCDebug(@"Char range: %@\nActual range: %@",
[AMCTools descriptionWithNSRange:[_CFG_AMCHyperLinkTest_Text rangeOfString:_CFG_AMCHyperLinkTest_Link]],
[AMCTools descriptionWithNSRange:linkRange]);
boundingRect = [_layoutManager boundingRectForGlyphRange:linkRange inTextContainer:_textContainer];
AMCDebug(@"Bounding rect: %@", [AMCTools descriptionWithNSRect:boundingRect]);
/* Now, we should set the link rect */
NSRect linkRect;
linkRect.size = [AMCTools string:_CFG_AMCHyperLinkTest_Link sizeWithSystemFontSize:13.0F];
linkRect.origin.x = [_labelHyperlink frame].origin.x + boundingRect.origin.x;
linkRect.origin.y = [_labelHyperlink frame].origin.y + boundingRect.origin.y;
// I have another text field who used to catch some mouse event and acted as an hyperlink.
// There are some codes to apply the "linkRect" to it.
...... /* Some codes less important */
AMCDebug(@"Text rect: %@\nlink rect: %@",
[AMCTools descriptionWithNSRect:[_labelHyperlink frame]],
[AMCTools descriptionWithNSRect:linkRect]);
}
And there are some description for some custom methods appeared above:
AMCTools
: My own tool to print some debug info of some instance or data structures
-string:sizeWithSystemFontSize:
: A method to determine minimum size of an text in system font.
AMCDebug()
: Like "NSLog" and don't mind.
And here is the output:
>> -[AMCHyperLinkTestViewController oneTimeInit]
2014-01-23 11:26:36.974 AMCTest[6665:303]
Pre Text rect: (17.0,443.0), 199.0*17.0
link rect: (64.0,47.0), 92.0*17.0
>> -[AMCHyperLinkTestViewController oneTimeInit]
2014-01-23 11:26:36.979 AMCTest[6665:303]
String size: 194.3*17.0
>> Line 74, -[AMCHyperLinkTestViewController oneTimeInit]
2014-01-23 11:26:36.995 AMCTest[6665:303]
Char range: (18 -> 23 <5>)
Actual range: (5 -> 5 <0>)
>> -[AMCHyperLinkTestViewController oneTimeInit]
2014-01-23 11:26:37.003 AMCTest[6665:303]
Bounding rect: (37.0,0.0), 0.0*14.0 <-- This does not look correct
>> -[AMCHyperLinkTestViewController oneTimeInit]
2014-01-23 11:26:37.007 AMCTest[6665:303]
Text rect: (17.0,443.0), 199.0*17.0
link rect: (54.0,443.0), 36.3*17.0
It did not work as I expected.
Upvotes: 2
Views: 1391
Reputation: 4433
If you’re simply interested in putting in a hyperlink, you can simply set the attributed string value to an attributed string containing a suitable hyperlink. (To do this, set an attribute of type NSLinkAttributeName
on the range you want to make into a link, with the value set to an NSURL
object. You may also wish to set an NSCursorAttributeName
attribute to change the user’s mouse pointer, and maybe other attributes as well to highlight the link to the user.)
If you’re trying to do something a bit more complicated, first note that NSTextField
will not render itself when editing (since it is actually the field editor that appears when it’s being edited), so doing this kind of thing for editable NSTextField
s is more complicated.
Otherwise, you can use NSLayoutManager
to calculate the layout for you, which will tell you where the word in question is located and what size it is. This is quite complicated (because text rendering is quite complicated), but essentially you need to:
NSLayoutManager
.NSTextStorage
and put the text from the field into it.NSTextContainer
of the appropriate size; for a single-line field, you should make this very wide to avoid wrapping, but for a multi-line field you want to make this the same size as the field (less any borders).-setTextStorage:
, -addTextContainer:
).-glyphRangeForCharacterRange:actualCharacterRange:
.-boundingRectForGlyphRange:inTextContainer:
.Don't do all of this just to make a hyperlink, though. Just use the built-in support for that.
Upvotes: 6