Attributed text in TextView - Objective-c

Hi there,

Because Xcode crashes when clicking in IB TextView after selecting "attributed", I need to know how to get parts of text in different styles and sizes, for example:


This should be in Helvetica bold 13 and this should be in Helvetica normal 11


How can I do that in code (Objective-c) ?


Thanks in advance for any advice

Accepted Reply

Here is how you would do in Swift, tested. Conversion to objc should be straightforward.


If the textView is: "This should be in Helvetica bold 13 and this should be in Helvetica normal 11"


        let attributedText = NSMutableAttributedString(attributedString: textView.attributedText!)

        // Use NSString so the result of rangeOfString is an NSRange.
        let text = textView.text! as NSString

        // Find the range of each element to modify.
        let boldRange = text.range(of: "This should be in Helvetica bold 13")
       
        let boldFont = UIFont(name: "Helvetica-bold", size: 13.0) as Any
       
        // Handle bold
        attributedText.addAttribute(NSAttributedString.Key.font, value: boldFont, range: boldRange)
       
        // Handle small.
        let smallRange = text.range(of: "and this should be in Helvetica normal 11")
        let smallFont = UIFont(name: "Helvetica", size: 11.0) as Any

        attributedText.addAttribute(NSAttributedString.Key.font, value: smallFont, range: smallRange)

        textView.attributedText = attributedText

Replies

Here is how you would do in Swift, tested. Conversion to objc should be straightforward.


If the textView is: "This should be in Helvetica bold 13 and this should be in Helvetica normal 11"


        let attributedText = NSMutableAttributedString(attributedString: textView.attributedText!)

        // Use NSString so the result of rangeOfString is an NSRange.
        let text = textView.text! as NSString

        // Find the range of each element to modify.
        let boldRange = text.range(of: "This should be in Helvetica bold 13")
       
        let boldFont = UIFont(name: "Helvetica-bold", size: 13.0) as Any
       
        // Handle bold
        attributedText.addAttribute(NSAttributedString.Key.font, value: boldFont, range: boldRange)
       
        // Handle small.
        let smallRange = text.range(of: "and this should be in Helvetica normal 11")
        let smallFont = UIFont(name: "Helvetica", size: 11.0) as Any

        attributedText.addAttribute(NSAttributedString.Key.font, value: smallFont, range: smallRange)

        textView.attributedText = attributedText

Because Xcode crashes when clicking in IB TextView after selecting "attributed",


In XCode 11.2ß2, OSX 10.14.6, i did this in IB.


- Created a TextView, with the required string "This should be in Helvetica bold 13 and this should be in Helvetica normal 11"

- Set it to Attributed

- selected the font for first part

- selected font for second part


Et voila, it worked as well.

Tried to write in objc, should be something like this:


     NSString *theString = self.textView.textStorage.string;
     NSMutableAttributedString * attributedText = [[NSMutableAttributedString alloc] initWithString: theString];
  
    NSRange boldRange = [testString rangeOfString:@"This should be in Helvetica bold 13"];
    UIFont * boldFont = [UIFont fontWithName:@"Helvetica-bold" size: 13];
    attributedText addAttribute:NSFontAttributeName value:boldFont range: boldRange];

    NSRange smallFontRange = [testString rangeOfString:@"and this should be in Helvetica normal 11"];
    UIFont * smallFont = [UIFont fontWithName:@"Helvetica" size: 11];
    [attributedText addAttribute:NSFontAttributeName value: smallFont range: smallFontRange];

    self.textView.attributedText = attributedText;

I can't reproduce your crash. It works fine here.


However, hard coding text is problematic. Trying to hard code attributed text is even worse. You can't localize that stuff easily. Xcode's IB text editor is difficult, and that's being kind.


What I do is just put placeholder text in text views. All of my actual attributed text is in RTF resource files. I just load them on demand and store them into the text view.


The only problem is layout. I have a hack where I try to estimate how tall a field would be based on my dynamic, styled text. There are system methods for that. Then I update a constraint with the height. It is a little flaky, but it works.

Thanks a lot Claude31, it helps me.


NSString *myRegularString = @"Title1\n\nBody1\n\nTitle2\n\nBody2"

    // Multiply the fontSize iPhone/iPad
    CGFloat fontSize;
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
        fontSize = 1;
    }
    else {
        fontSize = 2;
    }


In short, I added a calculation to know the length of the Body1 like this :


NSMutableAttributedString *myStyledTxt = [[NSMutableAttributedString alloc] initWithString:myRegularString];

    // Title1
    NSRange range = [myRegularString rangeOfString:@"Title1" options:NSRegularExpressionSearch|NSCaseInsensitiveSearch];
    if (range.location != NSNotFound) {
        // Transfer the styledTxt  
        [myStyledTxt addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"Futura-Bold" size:13 * fontSize] range:range];
        [myStyledTxt addAttribute:NSForegroundColorAttributeName value:[UIColor systemGrayColor] range:range];
        
    }
    
    // Compute the new range.location. 
    NSUInteger newLocation = range.location + range.length + 1;   //   .\n\n
    // Compute the length of Body1, it is between end of Title1 and the begining of Title2
    range = [myRegularString rangeOfString:@"iCarousel" options:NSRegularExpressionSearch|NSCaseInsensitiveSearch];
    // Ajust range.location and range.length
    range.length = range.location - newLocation;
    range.location = range.location = newLocation;
    // Transfer the styledTxt
    [myStyledTxt addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"Futura" size:11 * fontSize] range:range];
    [myStyledTxt addAttribute:NSForegroundColorAttributeName value:[UIColor systemGrayColor] range:range];


That's seems to be ok.

The Todo is to justified the bodies and have a foreground black/background white pour the titles and a foreground black/background clearGray for the bodies. The background should use the entire width.

It was easier with IB :-)

I'm working with a MacBook Pro 15" OS 10.15.2, Xcode 11.3(11.C29)

https://www.judogokyo.com/ftp/TxtViewCrash.mp4