Attributed String - ios

a text that has some part with yellow backgroundI need some padding to left and right of yellow colored text and need the label "3" to be center..I am using attributed text for it.can anyone help me on this.Below is the code i am using.Attaching screenshot as well.
string CardTileText = "3 Shared Documents";
NSAttributedString decoratedText = CardTileText.GetAttributedStringFromHtml("#ffe601","3");
public static NSAttributedString GetAttributedStringFromHtml(this string source, UIColor color, string identifier)
{
var atts = new UIStringAttributes();
UIFont newConnFont = UIFont.FromName("NotoSans-Bold", 16);
NSRange range = GetRangeFor(source, identifier);
NSNumber offset = 5;
NSMutableParagraphStyle para = new NSMutableParagraphStyle
{
Alignment = UITextAlignment.Left
};
NSMutableAttributedString attributedString = new NSMutableAttributedString(source, atts);
attributedString.AddAttribute(UIStringAttributeKey.BackgroundColor, color, range);
attributedString.AddAttribute(UIStringAttributeKey.ForegroundColor, UIColor.Black, range);
attributedString.AddAttribute(UIStringAttributeKey.Font, newConnFont, range);
attributedString.AddAttribute(UIStringAttributeKey.KerningAdjustment, offset, range);
attributedString.AddAttribute(UIStringAttributeKey.ParagraphStyle, para, range);
return attributedString;
}
static NSRange GetRangeFor(string source, string substring)
{
var range = new NSRange
{
Location = source.IndexOf(substring, StringComparison.Ordinal),
Length = substring.Length
};
return range;
}

I'm afraid the only solution is that set offset(which is used on KerningAdjustment) with a proper value.
Here is the comparison
offset = 0.5
offset = 10

Related

Get surrogate pairs from an emoji

What I'm trying to do is check if an emoji can be rendered on the iOS device by using this:
let font = CTFontCreateWithName("AppleColorEmoji", 12, nil)
var code_point: [UniChar] = [0xD83D, 0xDE0D]
var glyphs: [CGGlyph] = [0, 0]
let has_glyph = CTFontGetGlyphsForCharacters(font, &code_point, &glyphs, 2)
if has_glyph == false {
return false
}
else {
return true
}
It takes two code points and checks if the emoji can be rendered. Now what I'm having trouble with is how do I get the surrogate pairs directly from an emoji. I've Googled around and I can't seem to find any way to do so. Any ideas?
What you are looking for is the UTF-16 representation of a character:
let emoji = "😍"
let utf16codepoints = Array(emoji.utf16)
utf16codepoints is an [UInt16] array, and UniChar is a type alias for UInt16, so this array can be used directly in CTFontGetGlyphsForCharacters() to check if a font has a glyph
for this character (now updated for Swift 3/4):
let font = CTFontCreateWithName("AppleColorEmoji" as CFString, 12, nil)
var glyphs: [CGGlyph] = [0, 0]
let has_glyph = CTFontGetGlyphsForCharacters(font, utf16codepoints, &glyphs, utf16codepoints.count)
print(has_glyph)
// true
Hex dump the array to verify that it is the same as the
code_point array in your question:
print(utf16codepoints.map { String($0, radix: 16)} )
// ["d83d", "de0d"]
print(utf16codepoints == [0xD83D, 0xDE0D])
// true
In case somebody is looking for Obj-C implementation:
- (BOOL)isEmojiSupported:(NSString*)emoji
{
NSUInteger length = [emoji length];
unichar characters[length + 1];
[emoji getCharacters:characters range:NSMakeRange(0, length)];
characters[length] = 0x0;
CGGlyph glyphs[length];
CTFontRef ctFont = CTFontCreateWithName(CFSTR("AppleColorEmoji"), 12, NULL);
BOOL ret = CTFontGetGlyphsForCharacters(ctFont, characters, glyphs, emoji.length);
CFRelease(ctFont);
return ret;
}

NSAttributedString: Setting FontAttributes doesn't change font

I am trying to change the font on an NSAttributedString.
I have a method which iterates over each character, and creates a new NSFontAttribute dictionary for that character. In the end, however, the string remains un-changed. To demonstrate, here is how I setup the string:
UIFontDescriptor *fontDescriptor = [UIFontDescriptor fontDescriptorWithName:#"Avenir-Book" size:14.0f];
NSDictionary *fontAttributes = [fontDescriptor fontAttributes];
[fontAttributes setValue:[NSString stringWithFormat:#"%u",[fontDescriptor symbolicTraits]] forKey:UIFontSymbolicTrait];
[mutableAttributedString setAttributes:fontAttributes range:(NSRange){0,length}];
This produces the following NSFontAttribute dictionary for the entire string:
Font Attributes: {
NSCTFontSymbolicTrait = 2147483648;
NSFontNameAttribute = "Avenir-Book";
NSFontSizeAttribute = 14;
}
I go through and modify each character's FontAttribute to add bolding or italics, as follows:
for (int i = (int)range.location; i < (range.location + range.length); i++){
/* Extract Font Attributes */
NSDictionary *extractedAttributes = [[mutableAttributedString attributesAtIndex:i effectiveRange:NULL]mutableCopy];
/* Determine New Trait */
uint newTrait = ((uint)[[extractedAttributes valueForKey:UIFontSymbolicTrait]longLongValue] | [self symbolicTraitForMarkdownType:markdown]); // (markDown is a mask)
/* Set New Trait */
[extractedAttributes setValue:[NSString stringWithFormat:#"%u",newTrait] forKey:UIFontSymbolicTrait];
/* Create New Font Descriptor */
UIFontDescriptor *newDescriptor = [UIFontDescriptor fontDescriptorWithFontAttributes:extractedAttributes];
newDescriptor = [newDescriptor fontDescriptorWithSymbolicTraits:newTrait];
/* Apply Font Descriptor */
[mutableAttributedString setAttributes:[newDescriptor fontAttributes] range:(NSRange){i,1}];
}
This produces many different FontAttributes:
Index 1: {
NSCTFontSymbolicTrait = 2147483650;
NSFontNameAttribute = "Avenir-Black";
NSFontSizeAttribute = 14;
}
Index 2: {
NSCTFontSymbolicTrait = 2147483651;
NSFontNameAttribute = "Avenir-BlackOblique";
NSFontSizeAttribute = 14;
}
However, the NSAttributedString itself remains completely un-changed. It is still the default Font. How can I get it to reflect the changes I am making to its attributes?
Here is the solution:
1: The documentation for UIFontDescriptor defines the key: UIFontDescriptorNameAttribute as an NSString instance, which it is.
2: The documentation for NSAttributedString defines the key: NSFontAttributeName as a UIFont instance.
So obtaining the fontAttributes dictionary from a UIFontDescriptor initialized with a the method: (UIFontDescriptor *)fontDescriptorWithName:(NSString *)fontName size:(CGFloat)size will only set the keys UIFontDescriptorNameAttribute and UIFontDescriptorSizeAttribute. If you wish to actually modify the font of an NSAttributedString however, you need to apply attributes with a UIFont instance saved to the key NSFontAttributeName.
This is where the confusion comes from. However, it should be noted that you can actually obtain UIFontDescriptorNameAttribute from the fontAttributes dictionary of a UIFontDescriptor instance with the key NSFontAttributeName. This may also be confusing.

Getting specific value from string

In My Application i am Using ArcGIS . in that one i want Search one place and get the places related to that name. Now I want to Parse all the address of that places and put them in a table View. after typing the address and after i press search Button i will get all the Markers on the Map. Now i'm trying to get the complete address of the Place But i was unable to do this.I want to get "place_addr" value from the below string can any one please help me how can i get that value in ios
AGSFindLocationResult name=Disneyland, extent=AGSEnvelope: xmin = -13129988.221853, ymin = 4000701.846518, xmax = -13124422.247314, ymax = 4007401.106877, spatial reference: [AGSSpatialReference: wkid = 102100, wkt = null], graphic=geometry: AGSPoint: x = -13127204.667522, y = 4004050.640580, spatial reference: [AGSSpatialReference: wkid = 102100, wkt = null], symbol: { AGSPictureMarkerSymbol: imageName: BluePushpin.png, width: 36.000000, height: 36.000000 }, attributes: {
AddBldg = "";
AddNum = "";
AddNumFrom = "";
AddNumTo = "";
"Addr_type" = POI;
City = Anaheim;
Country = USA;
DisplayX = "-117.923687";
DisplayY = "33.815467";
Distance = 0;
LangCode = "";
"Loc_name" = "Gaz.WorldGazetteer.POI2";
"Match_addr" = Disneyland;
Nbrhd = "";
Phone = "(714)781-4565";
PlaceName = Disneyland;
"Place_addr" = "1313 S Disneyland Dr Anaheim, CA 92802";
Postal = "";
PostalExt = "";
Rank = "2.5";
Region = California;
Score = 100;
Side = "";
StAddr = "";
StDir = "";
StName = "";
StPreDir = "";
StPreType = "";
StType = "";
Subregion = Orange;
Type = "Amusement Park";
URL = "";
X = "-117.923687";
Xmax = "-117.898691";
Xmin = "-117.948691";
Y = "33.815467";
Ymax = "33.84047000000002";
Ymin = "33.79047";
}, visible: 1
IF what you're getting back really is a string, then you should look at the string searching methods like rangeOfString.
That will return an NSRange that contains your label, "place_addr".
Then you'd want to search from that point forward for a semicolon, using the related method rangeOfString:options:range:, that searches in a specific range of a string.
Finally, you'd need to calculate a range for the contents, that would be 5 characters past the end of the NSRange of "place_addr", and up to, but not including, the final semicolon.
Then you'd use substringWithRange to extract the string who's range you calculated above.
Give that a try, and come back if you have trouble.
Please try the code below
- (NSArray *)findKey:(NSString *)key inString:(NSString *)allString
{
NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:1];
NSArray *splitArray = [allString componentsSeparatedByString:#";"];
for (NSString *s in splitArray) {
NSArray * keyValueArry = [s componentsSeparatedByString:#"="];
if([keyValueArry count] > 1)
{
NSString *_key = [keyValueArry objectAtIndex:0];
NSString *_value = [keyValueArry objectAtIndex:1];
if([_key isEqualToString:key])
{
[result addObject:_value];
}
}
}
return result;
}
where key is place_addr, and allString is as your sample string.

How to check if a character is supported by a font

I'm working on an app with a text field. The text wrote in this field will be printed and I have an issue with some characters like emoji, chinese characters, etc... because the font do not provide these characters.
It's why I want to get all the character provided by a font (The font is downloaded so I can deal directly with the file or with an UIFont object).
I heard about CTFontGetGlyphsForCharacters but I'm not sure that this function do what I want and I can't get it work.
Here is my code :
CTFontRef fontRef = CTFontCreateWithName((CFStringRef)font.fontName, font.pointSize, NULL);
NSString *characters = #"🐯"; // emoji character
NSUInteger count = characters.length;
CGGlyph glyphs[count];
if (CTFontGetGlyphsForCharacters(fontRef, (const unichar*)[characters cStringUsingEncoding:NSUTF8StringEncoding], glyphs, count) == false)
NSLog(#"CTFontGetGlyphsForCharacters failed.");
Here CTFontGetGlyphsForCharacters return false. It's what I want because the character '🐯' is not provided by the font used.
The problem is when I replace NSString *characters = #"🐯" by NSString *characters = #"abc", CTFontGetGlyphsForCharacters return again false. Obviously, my font provide a glyph for all the ASCII characters.
I finally solve it :
- (BOOL)isCharacter:(unichar)character supportedByFont:(UIFont *)aFont
{
UniChar characters[] = { character };
CGGlyph glyphs[1] = { };
CTFontRef ctFont = CTFontCreateWithName((CFStringRef)aFont.fontName, aFont.pointSize, NULL);
BOOL ret = CTFontGetGlyphsForCharacters(ctFont, characters, glyphs, 1);
CFRelease(ctFont);
return ret;
}

NSString: strip out <b></b> and make an attributed string with color for that segment?

Say if I have a string:
This is a < b >simple < /b > string.
I need to get rid of the < b >, (sorry there is no space between b and angle bracket, for some reason the preview does not show it), also make the word 'simple' to be bold, my thought was:
replace the angle brackets and br with empty space
make the 'simple' segment to have attributes
The problem is once the tags are removed, I still need to know the word's location, do I first remember the location of 'simple', after removal, the location-4 should be the new location of 'simple'? Is there any better way? Or even transform html tag to attributes?
Thanks
edit:
Should be b instead of br
There is API available in iOS 7 that makes this very easy. It will convert an NSString of (possible) HTML text to an NSAttributedString.
NSDictionary *options = #{ NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType };
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithData:[myHTMLString dataUsingEncoding:NSUTF8StringEncoding] options:options documentAttributes:nil error:nil];
It will even preserve any in-line CSS applied, even background-color!
Note that if no font and font size is specified in the HTML text, the default will be Times New Roman 12. You could specify it in this fashion: <style>body { font-family:-apple-system; font-size:14px; }<style>. If you do not specify the font via CSS, you can still override the font, but you will need to manually handle bold, italics, etc otherwise that formatting will be lost if you set the font for the entire string. One approach is to enumerateAttribute: NSFontAttributeName on the mutable attributed string looking for 'bold' etc in the font name, and if it's found then replace that range with the desired font, such as the user's preferred font and size but the bold etc version of it, and continue replacing fonts with each range obtained from the enumeration.
The current answer is OK, and I have +1'd it. Yet it was only a clue, not a real solution.
If you are looking for a solution to the OP's question, take a look here.
You should focus on the following:
First implement these methods:
- (NSString *)styledHTMLwithHTML:(NSString *)HTML {
NSString *style = #"<meta charset=\"UTF-8\"><style> body { font-family: 'HelveticaNeue'; font-size: 20px; } b {font-family: 'MarkerFelt-Wide'; }</style>";
return [NSString stringWithFormat:#"%#%#", style, HTML];
}
- (NSAttributedString *)attributedStringWithHTML:(NSString *)HTML {
NSDictionary *options = #{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType };
return [[NSAttributedString alloc] initWithData:[HTML dataUsingEncoding:NSUTF8StringEncoding] options:options documentAttributes:NULL error:NULL];
}
Later use them like that:
// This is a string that you might find in your model
NSString *html = #"This is <b>bold</b>";
// Apply some inline CSS
NSString *styledHtml = [self styledHTMLwithHTML:html];
// Generate an attributed string from the HTML
NSAttributedString *attributedText = [self attributedStringWithHTML:styledHtml];
// Set the attributedText property of the UILabel
label.attributedText = attributedText;
Use this method (Swift 5):
extension Swift {
// Color all strings between two tags and remove the tags
mutating func colorSubstringsBetweenTags(start: String, end: String, color: UIColor, font: UIFont? = nil) -> NSAttributedString {
var string = self
let attribute = NSMutableAttributedString(string: string)
while let openedEm = string.range(of: start, range: string.startIndex..<string.endIndex) {
let substringFrom = openedEm.upperBound
guard let closedEm = string.range(of: end, range: openedEm.upperBound..<string.endIndex) else { return attribute }
let substringTo = closedEm.lowerBound
let nsrange = NSRange(substringFrom..<substringTo, in: string)
if let font = font { attribute.addAttributes([.font: font], range: nsrange) }
attribute.addAttribute(.foregroundColor, value: color, range: nsrange)
attribute.mutableString.replaceCharacters(in: NSRange(closedEm, in: string), with: "")
attribute.mutableString.replaceCharacters(in: NSRange(openedEm, in: string), with: "")
string = attribute.mutableString as String
}
return attribute
}
}
Usage:
var text = "some text with <b>tags</b> or other <b>TAG</b>"
yourLabel.attributedText = text.colorSubstringsBetweenTags(start: "<b>", end: "</b>", color: .red)
Similar to Michael's answer just updated for latest swift version,
extension UILabel {
func setHTMLFromString(htmlText: String) {
// Apply some inline CSS
let style = "<meta charset=\"UTF-8\"><style> body { font-family: 'TheSansArabic-Plain'; font-size: 12px; } b {font-family: 'TheSansArabic-Bold'; font-size: 12px; } </style>"
let styledHtml = style + htmlText
// Generate an attributed string from the HTML
let attributedText = try? NSAttributedString(data: styledHtml.data(using: .utf8)!, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil)
// Set the attributedText property of the UILabel
self.attributedText = attributedText
}
}
Use In your code like this,
override func viewDidLoad() {
super.viewDidLoad()
label.setHTMLFromString(htmlText: "This is <b>BOLD text</b>")
}

Resources