I have a UITextView where I\'m managing an NSAttributedString, initially entered as normal via the keyboard. I save the attributed string as HTML, which looks fine. When I
Using <span>
and css works for me, following Parsing HTML into NSAttributedText - how to set font?:
// HTML -> NSAttributedString
+ (NSAttributedString *)attributedStringFromHTML:(NSString *)html {
NSError *error;
NSDictionary *options = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType};
NSAttributedString *attrString = [[NSAttributedString alloc] initWithData:[html dataUsingEncoding:NSUTF8StringEncoding] options:options documentAttributes:nil error:&error];
if(!attrString) {
NSLog(@"creating attributed string from HTML failed: %@", error.debugDescription);
}
return attrString;
}
// force font thrugh <span> & css
+ (NSAttributedString *)attributedStringFromHTML:(NSString *)html withFont:(UIFont *)font {
return [Util attributedStringFromHTML:[NSString stringWithFormat:@"<span style=\"font-family: %@; font-size: %f\";>%@</span>", font.fontName, font.pointSize, html]];
}
This sets the font name and size but doesn't impact the styles.
Fixed this magic Apple behaviour with following code:
static CGFloat const kListsLeading = 10.0;
static CGFloat const kListsAdditionalShift = 4.0;
//
// Fix Apple magic 4/3 koefficient
// http://stackoverflow.com/questions/20992950/ios7-font-size-change-when-create-nsattributedstring-from-html
//
static NSAttributedString *DecreaseFontSizeBecauseOfAppleMagic(NSAttributedString *astr) {
NSMutableAttributedString *mastr = [astr mutableCopy];
[mastr enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, astr.length) options:0 usingBlock:^(UIFont *_Nullable value, NSRange range, BOOL *_Nonnull stop) {
if (value) {
UIFont *font = [value fontWithSize:value.pointSize * 0.75];
[mastr addAttribute:NSFontAttributeName value:font range:range];
}
}];
[mastr enumerateAttribute:NSParagraphStyleAttributeName inRange:NSMakeRange(0, astr.length) options:0 usingBlock:^(NSParagraphStyle *_Nullable value, NSRange range, BOOL *_Nonnull stop) {
if (value) {
NSMutableParagraphStyle *style = [value mutableCopy];
style.minimumLineHeight *= 0.75;
if (style.firstLineHeadIndent == style.headIndent) {
style.firstLineHeadIndent *= 0.75;
style.headIndent *= 0.75;
}
else if (style.firstLineHeadIndent < kListsLeading) {
CGFloat shift = (kListsLeading - style.firstLineHeadIndent);
style.headIndent += shift + kListsAdditionalShift;
style.firstLineHeadIndent = kListsLeading;
NSMutableArray *tabs = [NSMutableArray array];
NSInteger index = 0;
for (NSTextTab *tab in style.tabStops) {
[tabs addObject:[[NSTextTab alloc] initWithTextAlignment:tab.alignment location:tab.location + shift + kListsAdditionalShift * (index ? 1 : 0) options:tab.options]];
index++;
}
style.tabStops = tabs;
}
style.tailIndent *= 0.75;
[mastr addAttribute:NSParagraphStyleAttributeName value:style range:range];
}
}];
return [mastr copy];
}
This is not entirely an answer, more an alternative and a comment on the various other answers given, so apologies.
The answers given @KishoreThindaak and @Danomatika are fine if you know what the font sizes should be - but my application has a Mac OS twin which can generate any size text, and therefore had to be general.
The answer given by @k06a works for simple text, but I found that it failed with embedded styles - particularly with multiple styles on a line which itself was embedded in an <li>
tag.
After many hours of trying to fix this, I'm afraid I abandoned HTML altogether as the disk format and adopted RTF instead, which works fine, and provides an RTF file that is readable on all platforms. Simple code for getting RTF below...
extension NSAttributedString {
func rtf(encoding: String.Encoding) -> Data? {
let options: [NSAttributedString.DocumentAttributeKey : Any] = [
NSAttributedString.DocumentAttributeKey.documentType: NSAttributedString.DocumentType.rtf,
NSAttributedString.DocumentAttributeKey.characterEncoding: encoding.rawValue
]
return try? self.data(from: NSMakeRange(0, self.length), documentAttributes: options)
}
class func from(rtfData data: Data, encoding: String.Encoding) -> NSAttributedString? {
let options: [NSAttributedString.DocumentReadingOptionKey : Any] = [
NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.rtf,
NSAttributedString.DocumentReadingOptionKey.characterEncoding: encoding.rawValue
]
return try? NSMutableAttributedString(data: data, options: options, documentAttributes: nil)
}
}
I also faced this issue, I fixed it by iterating to the attributes and reseting the old font size as follows
NSMutableAttributedString *res = [attributedText mutableCopy];
[res beginEditing];
[res enumerateAttribute:NSFontAttributeName
inRange:NSMakeRange(0, res.length)
options:0
usingBlock:^(id value, NSRange range, BOOL *stop) {
if (value) {
UIFont *oldFont = (UIFont *)value;
UIFont *newFont = [oldFont fontWithSize:15];
[res addAttribute:NSFontAttributeName value:newFont range:range];
}
}];
[res endEditing];
[self.textFileInputView setAttributedText:res];
Swift 3.0 Version
With 0.75 ratio
yourAttrStr.beginEditing()
yourAttrStr.enumerateAttribute(NSFontAttributeName, in: NSMakeRange(0, yourAttrStr.length), options: .init(rawValue: 0)) {
(value, range, stop) in
if let font = value as? UIFont {
let resizedFont = font.withSize(font.pointSize * 0.75)
yourAttrStr.addAttribute(NSFontAttributeName, value: resizedFont, range: range)
}
}
yourAttrStr.endEditing()//yourAttrStrwill be the same size as html string