Edit your target’s scheme. Add a new pre-action to the build phase. Make sure provide settings is selected. Enter this script in:
rm "${TARGET_TEMP_DIR}/${PRODUCT_NAME}.xcent" > /dev/null
Edit your target’s scheme. Add a new pre-action to the build phase. Make sure provide settings is selected. Enter this script in:
rm "${TARGET_TEMP_DIR}/${PRODUCT_NAME}.xcent" > /dev/null
Cursor being mysteriously reset on the first drag? It’s somehow caused by having a NSToolbar on your window.
Sample project available here: https://github.com/panupan/TestDrag
After some investigating with a NSCursor category:
@implementation NSCursor(Hack)
- (void)set
{
NSLog(@"%@",[NSThread callStackSymbols]);
}
@end
I found this fixed it (use at your own risk!):
@interface NSDragDestination : NSObject {
unsigned long long _lastDragDestinationOperation;
}
@end
@implementation NSDragDestination(NSToolbarFix)
- (void)_setLastDragDestinationOperation:(unsigned long long)arg1;
{
// Fixes NSToolbar breaking default NSCursor behavior, e.g., cursor image on initial drag.
_lastDragDestinationOperation = arg1;
}
@end
While implementing zoom, I found that I didn’t quite grasp the difference between a view’s frame and its bounds. I decided to create a sample project to test things out. Check it out along with my findings on Github: https://github.com/panupan/FramesVsBounds.
Are you getting a crash trying to store your block in a NSDictionary instance?
From http://developer.apple.com/library/ios/#releasenotes/ObjectiveC/RN-TransitioningToARC/_index.html:
Blocks “just work” when you pass blocks up the stack in ARC mode, such as in a return. You don’t have to call Block Copy any more. You still need to use [^{} copy] when passing “down” the stack into arrayWithObjects: and other methods that do a retain.
As an optimization, block storage starts out on the stack—just like blocks themselves do. If the block is copied using Block_copy (or in Objective-C when the block is sent a copy), variables are copied to the heap. Thus, the address of a __block variable can change over time.
This means that the block variables in your function are stored in the stack, and when the function returns, they get wiped out. If you’re using ARC, your crash is caused by block variables being over released. Calling copy on a block makes block variables stick around.
Here’s what it looks like:
__weak id weakSelf = self; // Weak block variable prevents retain cycle.
void (^myBlock)() = ^() {
NSLog(@"Called from %@!", weakSelf);
};
// Copy the block so the block variable sticks around!
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:[myBlock copy], @"MyBlock", nil];
You can use NSString’s rangeOfCharacterFromSet method to search the string for whitespace and newlines:
// Trim leading whitespace and newlines.
NSCharacterSet *charSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSRange range = [attString.string rangeOfCharacterFromSet:charSet];
while (range.length != 0 && range.location == 0)
{
[attString replaceCharactersInRange:range
withString:@""];
range = [attString.string rangeOfCharacterFromSet:charSet];
}
// Trim trailing whitespace and newlines.
range = [attString.string rangeOfCharacterFromSet:charSet
options:NSBackwardsSearch];
while (range.length != 0 && NSMaxRange(range) == attString.length)
{
[attString replaceCharactersInRange:range
withString:@""];
range = [attString.string rangeOfCharacterFromSet:charSet
options:NSBackwardsSearch];
}
Need to apply an overall effect to your NSView? It’s pretty easy using QuartzCore filters. Here’s an example applying a grayscale effect first and then a gamma effect on top.
EDIT: You need to enable layer backing for this to work.
#import <QuartzCore/QuartzCore.h>
- (void)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.wantsLayer = YES; // Enable layer backing.
}
return self;
}
- (void)addFilters
{
if (!_filtersAdded) {
// Yes, apply grayscale filter
CIFilter *filt = [CIFilter filterWithName:@"CIColorMonochrome"]; // CIImage
[filt setDefaults];
[filt setValue:[CIColor colorWithRed:.3 green:.3 blue:.3 alpha:1] forKey:@"inputColor"];
CIFilter *filt2 = [CIFilter filterWithName:@"CIGammaAdjust"]; // CIImage
[filt2 setDefaults];
[filt2 setValue:[NSNumber numberWithFloat:0.3] forKey:@"inputPower"];
[self setContentFilters:[NSArray arrayWithObjects:filt, filt2, nil]];
}
else {
[self setContentFilters:[NSArray array]];
}
_filtersAdded = !_filtersAdded;
}
In an effort to better grasp why double indirection is needed, e.g., (NSError **), I wrote up a little sample code:
- (void)doFirstThingWith:(NSMutableString *)string {
// Set local string pointer to the newly created string.
// note: This DOES NOT affect the string pointer outside of this scope!
string = [@"first" mutableCopy];
}
- (void)doSecondThingWith:(NSMutableString *)string {
// Directly modify referenced string.
[string replaceCharactersInRange:NSMakeRange(0, string.length) withString:@"second"];
}
- (void)doThirdThingWith:(NSMutableString **)string {
// Update referenced pointer to point to the newly created string.
*string = [@"third" mutableCopy];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSMutableString *string = [@"before" mutableCopy];
[self doFirstThingWith:string];
NSLog(@"doFirstThingWith: %@", string);
// doFirstThingWith is basically doing this:
NSMutableString *string2 = string;
string2 = [@"first" mutableCopy];
NSLog(@"doFirstThingWith: %@", string);
[self doSecondThingWith:string];
NSLog(@"doSecondThingWith: %@", string);
[self doThirdThingWith:&string];
NSLog(@"doThirdThingWith: %@", string);
}
The resulting output:
2012-03-24 18:06:37.071 PointerTest[1195:707] doFirstThingWith: before 2012-03-24 18:06:37.072 PointerTest[1195:707] doFirstThingWith: before 2012-03-24 18:06:37.073 PointerTest[1195:707] doSecondThingWith: second 2012-03-24 18:06:37.073 PointerTest[1195:707] doThirdThingWith: third
We see that doFirstThingWith does not update the passed in string, this is because we are actually updating the method’s local string pointer instead of the original string pointer.
In doSecondThingWith, we see that we can update the referenced object and have the changes stick. This concept is already familiar to most of us.
In doThirdThingWith, we use a pointer to the original string pointer. This way, we can update the original pointer to point to a newly created string. Using double indirection gives us a mechanism to return newly created objects via method arguments, this being one of its practical uses.
Check out these links for more information:
The easy way is to use an NSArrayController and add an observer like so:
[friendsArrayController addObserver:self forKeyPath:@"arrangedObjects.name" options:0 context:nil];
The hard way is to observe a model’s collection property and add/remove observers when objects get added/removed from the collection like so:
[person addObserver:self forKeyPath:@"friends" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (object == person && [keyPath isEqualTo:@"friends"]) {
if ([[change valueForKey:NSKeyValueChangeKindKey] intValue] == NSKeyValueChangeInsertion) {
for (Friend *newFriend in [change valueForKey:NSKeyValueChangeNewKey]) {
if (![observedFriends containsObject:newFriend]) {
[newFriend addObserver:self forKeyPath:@"name" options:0 context:nil];
}
}
}
else if ([[change valueForKey:NSKeyValueChangeKindKey] intValue] == NSKeyValueChangeRemoval) {
for (Friend *oldFriend in [change valueForKey:NSKeyValueChangeOldKey]) {
if (![observedFriends containsObject:oldFriend]) {
[oldFriend removeObserver:self forKeyPath:@"name"];
}
}
}
else if ([[change valueForKey:NSKeyValueChangeKindKey] intValue] == NSKeyValueChangeReplacement) {
for (Friend *newFriend in [change valueForKey:NSKeyValueChangeNewKey]) {
if (![observedFriends containsObject:newFriend]) {
[newFriend addObserver:self forKeyPath:@"name" options:0 context:nil];
}
}
for (Friend *oldFriend in [change valueForKey:NSKeyValueChangeOldKey]) {
if (![observedFriends containsObject:oldFriend]) {
[oldFriend removeObserver:self forKeyPath:@"name"];
}
}
}
}
}
So I had a vanilla Core Data entity with a @property (nonatomic, retain) NSString *text property and a NSTextView *textView used to edit text that would be stored to this value. Initialization looked something like textView.string = model.text and saving looks like model.text = textView.string in textDidEndEditing:.
All good right? Wrong. The value wasn’t sticking. It was changed even though the model’s setter without ever being called. Can you guess why?
It took me wayy too long to debug, but finally… checking out NSText’s string getter method, we see:
For performance reasons, this method returns the current backing store of the text object. If you want to maintain a snapshot of this as you manipulate the text storage, you should make a copy of the appropriate substring.
Tracing NSLog(@"%@", [textView.string class]) we see it is actually an instance of NSBigMutableString. This means both the text editor and entity now hold a reference to the same mutable NSString. Basic CS101, duh! There are two possible fixes: Define the string property using copy vs retain, @property (nonatomic, copy) NSString *text or manually copy the string when setting the value like model.text = [textView.string copy]. See this Stack Overflow post for more info on when to use retain/copy on NSString.
NSKeyValueObserving’s addObserver method now creates a weak reference when your project uses ARC, so no more retain cycles, yippie! Don’t forget to remove your observers in your dealloc method or you may get observer still registered exceptions.