2

I have some pure Html string and some of them has title tags <b>Title</b>.

facilities: "<b>Facilities</b><br/>24-hour security, Barbecue area, Car park, Clubhouse, Function room, Gym, Outdoor swimming pool, Playground, Swimming pool<br/><br/><b>Rooms</b><br/>Dining room, Ensuites, Living room, Maid\'s room, Utility room<br/><br/><b>Outdoor</b><br/>Balcony<br/><br/><b>View</b><br/>City, Open<br/><br/><b>Direction</b><br/>South East"

So i use NSRegularExpression pattern to extract titles from string and store in an array of strings. And later, i make these titles bold (attributed string) and display. So this is how i do that:

var titlesArray = [String]()
let regex = try! NSRegularExpression(pattern: "<b>(.*?)</b>", options: [])
let basicDescription = facilities as NSString

regex.enumerateMatchesInString(facilities, options: [], range: NSMakeRange(0, facilities.characters.count)) { result, flags, stop in
  if let range = result?.rangeAtIndex(1) {
    titlesArray.append(basicDescription.substringWithRange(range))
  }
}

let convertedDescription = facilities.html2String as NSString
let attributedString = NSMutableAttributedString(string: convertedDescription as String, attributes: [NSFontAttributeName:UIFont.systemFontOfSize(14.0)])
let boldFontAttribute = [NSFontAttributeName: UIFont.boldSystemFontOfSize(15.0)]

if titlesArray.count > 0 {
   for i in 0..<titlesArray.count {
   attributedString.addAttributes(boldFontAttribute, range: convertedDescription.rangeOfString(titlesArray[i]))
   } 
}

So, everything is alright. But the problem is, sometimes i receive Html tagged strings which has duplicate words where one of them is title with title tag, another one is just a simple word which i do not need to bold. But this function will look for that word and bold it inside for loop and ignore the real title which comes after the simple word.

This is what i get:

enter image description here

So here, how can i ignore the first "Outdoor" and bold the second one which i want. Thank you for any help.

Umit Kaya
  • 5,771
  • 3
  • 38
  • 52
  • How would you garante that only the first one is your problem? I would recommend an html parser instead of regex to read that html. [This](http://stackoverflow.com/questions/201323/using-a-regular-expression-to-validate-an-email-address) is why you shouldn't use regex to parse an html string. – Jorge Campos Jan 19 '17 at 15:50
  • yes i use html parser to convert Html into normal String. But it doesnt offer to attribute text. Thats why to attribute(bold) words i use regex. – Umit Kaya Jan 19 '17 at 15:52
  • Yeah I agree with Jorge I think you need a completely different approach to your problem. Either that or you should add the bold attribute as you work through the text and not add the bold attribute after. – TheAmateurProgrammer Jan 19 '17 at 15:52
  • This is the line: facilities.html2String as NSString converting Html into String – Umit Kaya Jan 19 '17 at 15:52
  • 1
    Question, why don't you use `init(data:, options:, documentAttributes:)`? – Larme Jan 19 '17 at 15:56
  • Only convert html to string is not what we meant. A proper parser will understand the html code therefore you can do whatever you want with the html code as removing bold from blocks of inner tags (as you problem) etc. – Jorge Campos Jan 19 '17 at 15:59
  • I guess Html parser in Swift does not support title tags. So it is not very useful. – Umit Kaya Jan 19 '17 at 16:05
  • Possible duplicate of [Convert HTML to NSAttributedString in iOS](http://stackoverflow.com/questions/4217820/convert-html-to-nsattributedstring-in-ios) – Larme Jan 19 '17 at 16:12

2 Answers2

1

In Objective-C, shouldn't be too hard to translate in Swift (since it seems you already know some of the methods).

attr1 is rendered with init(data:, options:, documentAttributes:). I didn't add any other effects (like preferred size for bold/normal, color, you just have to enumerate on it and change the effects) attr2 is rendered more the way you wanted with your regex. It just doesn't take in account all tags, just the bold, and I hardly coded the replacement for new lines (<br/> into \n). But that could be something to use. I didn't do more tests on your regex (the while loop could be stucked?)

NSString *str = @"<b>Facilities</b><br/>24-hour security, Barbecue area, Car park, Clubhouse, Function room, Gym, Outdoor swimming pool, Playground, Swimming pool<br/><br/><b>Rooms</b><br/>Dining room, Ensuites, Living room, Maid\'s room, Utility room<br/><br/><b>Outdoor</b><br/>Balcony<br/><br/><b>View</b><br/>City, Open<br/><br/><b>Direction</b><br/>South East";

NSError *errorAttr1 = nil;
NSAttributedString *attr1 = [[NSAttributedString alloc] initWithData:[str dataUsingEncoding:NSUTF8StringEncoding] options:@{NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType} documentAttributes:nil error:&errorAttr1];
if (errorAttr1)
{
    NSLog(@"Error AttributedStr Conversion with initWithData:options:documentsAttributes:error: %@", errorAttr1);
}
else
{
    NSLog(@"attr1: %@", attr1);
    [_tv1 setAttributedText:attr1];

}

str = [str stringByReplacingOccurrencesOfString:@"<br/>" withString:@"\n"];

NSError *errorRegex = nil;
NSString *openingTag = @"<b>";
NSString *closingTag = @"</b>";
NSString *pattern = [NSString stringWithFormat:@"%@(.*?)%@", openingTag, closingTag];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&errorRegex];
if (errorRegex)
{
    NSLog(@"Error regex: %@", errorRegex);
    return;
}


NSDictionary *boldAttributes = @{NSForegroundColorAttributeName:[UIColor darkGrayColor],
                                 NSFontAttributeName:[UIFont boldSystemFontOfSize:15]};
NSDictionary *normalAttributes = @{NSForegroundColorAttributeName:[UIColor darkGrayColor],
                                   NSFontAttributeName:[UIFont systemFontOfSize:14]};

NSMutableAttributedString *attr2 = [[NSMutableAttributedString alloc] initWithString:str attributes:normalAttributes]; //Add the initial attributes there

//Now we'll add the specific attribues
NSTextCheckingResult *match = [regex firstMatchInString:[attr2 string] options:0 range:NSMakeRange(0, [attr2 length])];
while (match)
{
    NSRange range = [match range];
    NSString *foundStr = [[attr2 string] substringWithRange:range];
    NSAttributedString *temp = [[NSAttributedString alloc] initWithString:[foundStr substringWithRange:NSMakeRange([openingTag length], [foundStr length]-[openingTag length]-[closingTag length])] attributes:boldAttributes];
    [attr2 replaceCharactersInRange:range withAttributedString:temp];
    match = [regex firstMatchInString:[attr2 string] options:0 range:NSMakeRange(0, [attr2 length])];
}
NSLog(@"attr2: %@", attr2);
[_tv2 setAttributedText:attr2];

_tv1 and _tv2 are two UITextView (IBOulet). It rendered: (_tv1 is the one on top, _tv2 is the second one).

enter image description here

Larme
  • 24,190
  • 6
  • 51
  • 81
  • thank you. i can modify things to Swift to make it work. But in this case how can i change the font? Font requires string, where i have attributed string. – Umit Kaya Jan 19 '17 at 17:02
  • I don't understand your question, are you asking about method one or method two? For method two: modify the `boldAttributes` or `normalAttributes`, for method one http://stackoverflow.com/a/41413014/1801544 , or you can also add style CSS/HTML in the html part before rendering. – Larme Jan 19 '17 at 17:05
0

I dont know what exactly you are trying to do, but did tou consider using an initializer that takes HTML?

An example code for Playgrounds would be:

if let url = Bundle.main.url(forResource: "file", withExtension: "html") {
    do {
        let data = try Data(contentsOf: url)
        let attributedString = try NSAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil)
        let labelRect = CGRect(x: 0, y: 0, width: 500, height: 250)
        let label = UILabel(frame: labelRect)
        label.numberOfLines = 2
        label.attributedText = attributedString

    } catch {
        print(error.localizedDescription)
    }
}

With this HTML:

<style>
    body {
     font-size: 20;
     font-family: sans-serif;
    }
</style>

<p><b>Hello</b> world</p>
<p>Good morning!</p>

The output looks like:

enter image description here

MirekE
  • 11,515
  • 5
  • 35
  • 28