By Michael Robinson on May 16, 2012,
Filed in: Cocoa | Objective CI 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.
Tags: Cocoa, NSBundle, Objective C
By Michael Robinson on May 7, 2012,
Filed in: CocoaMerging 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.
Tags: AppKit, Carbon, Cocoa
By Michael Robinson on March 7, 2012,
Filed in: Cocoa | Objective CThe 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.
Tags: Category, Cocoa, NSString
By Michael Robinson on December 6, 2011,
Filed in: Cocoa | Objective CExample 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 |
Tags: Cocoa, NSColor, Objective C
By Michael Robinson on November 27, 2011,
Filed in: Cocoa | Objective CIn 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).
Tags: Cocoa, NSClassFromString, objc_getClass