Using Frameworks Within NSBundles

I wanted to use Connection Kit for my current project, but ran into issues as it requires a few other frameworks to function. Many months ago I worked around this by copying the internal framework’s source files directly into Connection Kit.

Everything went well until I updated Connection Kit. Suddenly it required more internal frameworks, and copying their source became impractical.

Comments (5) | Trackback

Cocoa Undefined symbols “_GetCurrentKeyModifiers”

Merging some convenient (but old) code I found laying around into a new project, I came across this build error:

Undefined symbols for architecture i386:
  "_GetCurrentKeyModifiers", referenced from:
      -[Class mouseDown:] in Class.o
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Scrolling through the source for this class, I found calls to GetCurrentKeyModifiers() scattered about. This function is from Carbon, the AppKit way to get the same thing is:

[[NSApp currentEvent] modifierFlags]

Swapping out GetCurrentKeyModifiers() calls for this cleared those build errors right up.

Comments (1) | Trackback

Resizing NSTextField to Fit Content

The NSPopOver, a new addition to the Cocoa toolkit since OS X 10.7, is an excellent way to give the user feedback on actions requiring their input. In my use case, I needed to be able to show an NSPopOver containing a multiline label populated with a string of varying length.


Getting the NSPopOver, NSView & NTextField all working perfectly was very basic.


Ensuring that the NSPopOver’s NSView & NSTextField resized appropriately was more challenging.


I needed to get the rectangle that my string would fill when drawn. A short Google search led me here: SO: NSString sizeWithAttributes: Content Rect. As I know I’ll be doing this again, I made a category of NSString that provides this.


The rest was simple:

1
2
3
4
5
6
7
8
9
10
11
NSSize size = [string sizeWithWidth:200.0 andFont:[textLabel font]];
 
[view setFrameSize:NSMakeSize(size.width + 30, size.height + 30)];
 
[textLabel setFrame:NSMakeRect(15, 15, size.width, size.height)];
[textLabel setStringValue:string];
 
popover.contentSize = view.frame.size;
[popover showRelativeToRect:[textField bounds] 
                     ofView:textField
              preferredEdge:NSMaxXEdge];

The NSString+Size category is available here: Cocoa Categories.

After doing this, I rethought my approach and streamlined it by creating a category on NSPopover, which is available in the above repository. I wrote about the process here: User Feedback & the NSPopover.

No comments | Trackback

Extracting hex value from NSColor

Example project available on GitHub –

NSColor+Hex category available on GitHub –

This category on NSColor allows one to get or set an NSColor’s colour using hex values. Intitialising an NSColor with a hexadecimal colour:
1
2
3
4
5
6
7
8
@try {
    // Both shorthand and full forms of hexadecimal colours are accepted
    [colorWell setColor:[NSColor colorWithHex:@"#F00"]];
    [colorWell setColor:[NSColor colorWithHex:@"#Ff0000"]];
}
@catch (NSException *exception) {
    NSLog(@"%@", [exception description]);
}
It’ll throw an exception if one attempts to use a string that is not 3 or 6 characters in length, excluding the hash. Getting the hexadecimal representation of a given NSColor:
1
NSString *hexColor = [color hexColor]
This being my first Cocoa category, I’m sure it must have rough edges. Nevertheless, I have found it very useful. Below is the header for NSColor+Hex:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//
//  NSColor+Hex.h
//  CINSColor+Hex
//
//  Created by Michael Robinson on 4/12/11.
//  Copyright 2011 Code of Interest. All rights reserved.
//
 
#import <AppKit/AppKit.h>
 
@interface NSColor (Hex)
 
+ (NSColor *) colorWithHex:(NSString *)hexColor;
- (NSString *) hexColor;
 
@end
And the implementation, more verbose than I’d like but it does the job. If anyone improves on it, I’ll be happy to update this page & accept your pull request on GitHub:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
//
//  NSColor+Hex.m
//  CINSColor+Hex
//
//  Created by Michael Robinson on 4/12/11.
//  Copyright 2011 Code of Interest. All rights reserved.
//
 
#import "NSColor+Hex.h"
 
@implementation NSColor (Hex)
 
 
+ (NSColor *) colorWithHex:(NSString *)hexColor {
 
    // Remove the hash if it exists
    hexColor = [hexColor stringByReplacingOccurrencesOfString:@"#" withString:@""];
    int length = (int)[hexColor length];
    bool triple = (length == 3);
 
    NSMutableArray *rgb = [[NSMutableArray alloc] init];
 
    // Make sure the string is three or six characters long
    if (triple || length == 6) {
 
        CFIndex i = 0;
        UniChar character = 0;
        NSString *segment = @"";
        CFStringInlineBuffer buffer;
        CFStringInitInlineBuffer((CFStringRef)hexColor, &buffer, CFRangeMake(0, length));
 
 
        while ((character = CFStringGetCharacterFromInlineBuffer(&buffer, i)) != 0 ) {
            if (triple) segment = [segment stringByAppendingFormat:@"%c%c", character, character];
            else segment = [segment stringByAppendingFormat:@"%c", character];
 
            if ((int)[segment length] == 2) {
                NSScanner *scanner = [[NSScanner alloc] initWithString:segment];
 
                unsigned number;
 
                while([scanner scanHexInt:&number]){
                    [rgb addObject:[NSNumber numberWithFloat:(float)(number / (float)255)]];
                }
                segment = @"";
            }
 
            i++;
        }
 
        // Pad the array out (for cases where we're given invalid input)
        while ([rgb count] != 3) [rgb addObject:[NSNumber numberWithFloat:0.0]];
 
        return [NSColor colorWithCalibratedRed:[[rgb objectAtIndex:0] floatValue] 
                                         green:[[rgb objectAtIndex:1] floatValue] 
                                          blue:[[rgb objectAtIndex:2] floatValue] 
                                         alpha:1];
    }
    else {
        NSException* invalidHexException = [NSException exceptionWithName:@"InvalidHexException"
                                                                   reason:@"Hex color not three or six characters excluding hash"                                    
                                                                 userInfo:nil];
        @throw invalidHexException;
 
    }
 
}
 
- (NSString *) hexColor {
     if ([[self colorSpaceName] isEqualToString:NSCalibratedWhiteColorSpace]) {
        return [NSString stringWithFormat:@"#%0.2X%0.2X%0.2X",
            (int)(r * [self whiteComponent]),
            (int)(g * [self whiteComponent]),
            (int)(b * [self whiteComponent])];
    }
    else if ([[self colorSpaceName] isEqualToString:NSCalibratedBlackColorSpace]) {
        return [NSString stringWithFormat:@"#%0.2X%0.2X%0.2X",
            (int)(r * [self blackComponent]),
            (int)(g * [self blackComponent]),
            (int)(b * [self blackComponent])];
    }
    else if ([[self colorSpaceName] isEqualToString:NSCalibratedRGBColorSpace]
             || [[self colorSpaceName] isEqualToString:NSDeviceRGBColorSpace]) {
        return [NSString stringWithFormat:@"#%0.2X%0.2X%0.2X",
            (int)(r * [self redComponent]),
            (int)(g * [self blueComponent]),
            (int)(b * [self greenComponent])];
    }
    return @"transparent";
}
 
@end
Comments (2) | Trackback

Cocoa Warning: Implicitly declaring C library function ‘objc_getClass’

In Slider 2, I needed to check for the presence for Sparkle, and only load Slider 2′s included version when Sparkle isn’t found.

The code I used to do this is included below, but the point of this article is really to provide a solution to the following warning I encountered:

warning: Semantic Issue: Implicitly declaring C library function 'objc_getClass' with type 'id (const char *)'

One will get this warning if using code when checking for the existence of a class like:

if (!objc_getClass("SUUpdater")) { ... }

The solution is to use NSClassFromString instead:

if (!NSClassFromString(@"SUUpdater")) { ... }

For an in-depth tutorial on how to check for the presence of a framework within a Cocoa bundle, see Detecting Presence of a Framework Within an NSBundle (Plugin).

No comments | Trackback