Hillegass Chap 20. NSFontManager

Discussion in 'Mac Programming' started by hubrisForAll, Jan 18, 2011.

  1. hubrisForAll macrumors newbie

    Joined:
    Jan 13, 2011
    Location:
    Houston
    #1
    Hello all. Working my way through Hillegass, I find myself stuck on Chapter 20, Challenge 2, along with a few general questions. I've found several solutions to this challenge but none seem correct.

    Background:

    The program is a simple Text Drawing Application - with two text boxes and one large Custom View that display's whatever Key you press. The image View draw's from a string (NSString), and attributes are added to it via an 'attributes' mutable dictionary.

    In the challenge, Hillegass wants you to add checkbox's to make the text bold and / or italic:

    2 checkboxe's (one for bold, one for italic)
    2 BOOL values (bold, italic).
    Use NSFontManager, with the sample code given:

    fontManager = [NSFontManager sharedFontManager];
    boldFont = [fontManager convertFont:aFont toHaveTrait:NSBoldFontMask];

    The relevant Code up to this point:

    BigLetterView.h:
    Code:
    @interface BigLetterView : NSView {
    	NSColor *bgColor;
    	[B]NSString *string;
    	NSMutableDictionary *attributes;
    	
    	BOOL *italic;
    	BOOL *bold;[/B]
    
    }
    
    @property (readwrite, retain) NSColor *bgColor;
    @property (copy, readwrite) NSString *string;
    
    - (void)prepareAttributes;
    - (void)drawStringCenteredIn:(NSRect)r;
    - (IBAction)savePDF:(id)sender;
    
    - (IBAction)italicCheckbox:(id)sender;
    - (IBAction)boldCheckbox:(id)sender;
    With 'prepareAttributes' called in the 'init' function.


    One solution I found, invovling the prepareAttributes function:
    Code:
    - (void)prepareAttributes {
      attributes = [[NSMutableDictionary alloc] init];
      
      NSFont *font = [NSFont fontWithName:@"Helvetica" size:75];
    
      [B]NSFontManager *fontManager = [NSFontManager sharedFontManager];
      if (italic) font = [fontManager convertFont:font toHaveTrait:NSItalicFontMask];
      if (bold) font = [fontManager convertFont:font toHaveTrait:NSBoldFontMask];
      
      [attributes setObject:font
                     forKey:NSFontAttributeName];[/B]
      
      [attributes setObject:[NSColor redColor]
                     forKey:NSForegroundColorAttributeName];
     
      [self setNeedsDisplay:YES];
    }

    With this method being called whenever the bold or italic setter's are called. However, I would think this would leak memory (specifically the new *font pointer, and a new 'attributes' variable).

    I was wandering if another way would be to simply, inside the 'bold' and 'italic' setter's, have them:

    1. Set themselves to new value
    2. Check that value, use 'if' statement to then set the font to bold or un-bold, using the following Code:

    Code:
    NSFontManager *fontManager = [NSFontManager sharedFontManager];
    	NSFont *newFont;
    
    newFont = [fontManager convertFont:[attributes valueForKey:NSFontAttributeName] toHaveTrait:NSBoldFontMask];
    
    [attributes setObject:newFont forKey:NSFontAttributeName]; 
    
    [self setNeedsDisplay:YES];
    
    
    So, with this solution, I'm trying to find out if I am leaking memory, OR if there is a more elegant way to write this - it seems having to call all this code each time I call the setter is excessive. Hillegass makes no indication that I should have to declare new variables (for newFont, for instance) in my BigLetterView header file.

    Any and all suggestions or criticism's appreciated.
     
  2. chown33, Jan 18, 2011
    Last edited: Jan 18, 2011

    chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #2
    Whatever else is wrong, the description in blue does not match the variable declarations in red. Furthermore, the use of BOOL* (i.e. a pointer to BOOL) is inconsistent with the if statements in red.

    Not everything gets a *. Sometimes you have to use the plain type name.

    I'm still looking at the code, but this error is so fundamental it profoundly affects how the code behaves. Pointer-to-BOOL is completely different from BOOL itself.


    Where did you find the code? Post the exact URL.

    If it contains the pointer-to-BOOL error, along with the other errors evident in the code, then it's a bad example. Or a good example of doing it badly.

    On the other hand, if you found something and modified it, then some of the errors may have been introduced by you. There's no way for us to tell without seeing the original you found.


    Why do you think you have to declare newFont in the header?

    You have a local variable named newFont in the method itself.

    Not everything needs to be an instance variable. Nor should it be. Local variables are frequently used, and with good reasons. If you don't know the difference, you need to review some fundamentals.
     
  3. hubrisForAll thread starter macrumors newbie

    Joined:
    Jan 13, 2011
    Location:
    Houston
    #3
    Silly me - on a Hillegass marathon right now, I'll up the coffee.

    Thanks.
     
  4. McGordon macrumors member

    Joined:
    Dec 28, 2010
    Location:
    Scotland
    #4
    I'd say your new proposed solution is much better. In your original solution, you're leaking the 'NSMutableDictionary *attributes' by creating a new one every time the italic or bold checkbox is clicked.
    The prepareAttributes method was fine for its intended purpose of setting up the attributes once per object, but not meant to be used repeatedly every time you want to change the attributes.

    Here's what I did when I did the challenge:

    Code:
    - (void)setItalic:(BOOL)it {
       italic = it;
       NSFont *font = [attributes objectForKey:NSFontAttributeName];
       NSFontManager *fontmanager = [NSFontManager sharedFontManager];
       
       if (italic) {
          font = [fontmanager convertFont:font
                              toHaveTrait:NSItalicFontMask];
       }
       else {
          font = [fontmanager convertFont:font 
                           toNotHaveTrait:NSItalicFontMask];
       }
       [attributes setObject:font 
                      forKey:NSFontAttributeName];
       [self setNeedsDisplay:YES];
    }
    
    
    similar for the setBold method. I suppose you could have created different versions of each font and switched between them, but you'd need 4 to cover the combinations of bold and italic off and on.

    Don't worry about having to create new variables or methods in a challenge. You're on your own and can do whatever you like to get the job done.
     
  5. hubrisForAll thread starter macrumors newbie

    Joined:
    Jan 13, 2011
    Location:
    Houston
    #5
    Awesome. Thanks for the swift replies.


    Was thinking - given that you can directly read the state of checkbox buttons - is there really any need of using a BOOL for bold / italics on top of the checkbox? Would it perhaps be more appropriate to merely read the state of the Checkbox's and then have one IBAction for changing the font?
     
  6. gnasher729 macrumors P6

    gnasher729

    Joined:
    Nov 25, 2005
    #6
    If I just want to display some text in bold, some text in italic, and some text in bold italic, I'd need some code to do this that doesn't look at any checkboxes. What if you have four checkboxes, two controlling a first bit of text, and two controlling a second bit of text? Like "Headline bold", "Headline italic", "Body text bold", "Body text italic"?

    What if you decide to change the UI and have the text always bold, without any checkbox to change it?
     

Share This Page