Recent Updates Toggle Comment Threads | Keyboard Shortcuts

  • Jay Versluis 8:07 pm on April 17, 2014 Permalink | Reply
    Tags: Cocoa Bindings, , , ,   

    How to bind an NSArrayController to an NSTableView 

    Core-Data-IconIn this screencast I will show you how to bind a Table View to an Array Controller in Cocoa, using Xcode 5.1 and OS X Mavericks.

    We’re using Core Data to save our entries and – check it out – we’re not writing a single line of code!

    Cocoa Bindings is one of the most exciting features in OS X development for me, and I hope that one day it’ll find its way into iOS too.

    The project is also available on GitHub:

    I’ve written more about how to do this here:

     

     
  • Jay Versluis 8:18 am on April 16, 2014 Permalink | Reply
    Tags: , NSEvent, NSView   

    How to handle mouse events in OS X 

    Handling mouse events in OS X is very different from the way in which we’re used to dealing with touch events in iOS. Here’s how to do it:

    You must subclass an NSView and assign your class in the the xib file – much like you would subclass a UIViewController and assign your own in the storyboard. Every Cocoa Application has at least one view, found under the Window of your app (you may have to expand the Document Outline for this to show, it’s at the bottom left in Interface Builder):

    Screen Shot 2014-04-16 at 08.16.19

    Then in your own class you can track mouse events by implementing these functions among several others:

    // react to mouse events
    - (void)mouseDown:(NSEvent *)theEvent {
        
        NSLog(@"Mouse was clicked");
    }
    
    - (void)mouseDragged:(NSEvent *)theEvent {
        
        NSLog(@"Mouse is dragging");
    }
    
    - (void)mouseUp:(NSEvent *)theEvent {
        
        NSLog(@"Mouse was let go");
    }
    

    The Apple documentation also mentions that we should override the following method for our custom view to accept mouse events – but in OS X 10.9.2 I didn’t find this to make a difference:

    - (BOOL)acceptsFirstResponder {
        
        return YES;
    }
    

    First Mouse Event

    There is something called the “first mouse” event. This is called when an app or window not currently in focus is clicked on. Usually that window will ignore where you clicked and only bring the app or window into focus, and then reacts to mouse events.

    You can change this behaviour by overriding the following method:

    // accept first mouse events
    - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent {
        
        NSLog(@"Got first mouse event");
        return YES;
    }
    
     
  • Jay Versluis 7:54 pm on April 15, 2014 Permalink | Reply
    Tags: , UISplitViewController   

    How to show the Master View button in a UISplitViewController app 

    I was tinkering with an iPad version of one of my apps, when I noticed that the nav bar button that hides and shows the Master View Controller in portrait mode was no longer displayed. You know, the one that reads “Master” by default.

    This is a problem if my app starts in portrait mode, because there’s no way to bring this button back. The Split View Controller handles that button in its willHideViewController delegate method – which was seemingly never called upon startup (anymore).

    I can’t call that method manually on startup because I don’t have the parameters that call this method – but I did find a solution that forces the Split View to call this method:

    // bring back the Bike Tours button in portrait
    [self.splitViewController.view setNeedsLayout];
    

    Calling this somewhere in viewDidLoad doesn’t hurt your app – but it avoids having to solve The Case of the Disappearing Split View Button.

     
  • Jay Versluis 7:37 pm on April 12, 2014 Permalink | Reply
    Tags: , NSThread   

    How to execute a method on a background thread in iOS 

    To call something from a background thread we can use dispatch_async.

    This cryptic looking statement will throw something to a background thread while the main thread can deal with other functions. This is especially useful if a method would take a long time to return and would be blocking your app while it’s waiting for an answer.

    When your method comes back with a result if executes a block which can be used to react to that result.

    Consider this example:

    - (IBAction)mainQueue:(id)sender {
        
        // call this on the main thread
        [NSThread sleepForTimeInterval:3];
        int i = arc4random() % 100;
        self.title = [[NSString alloc]initWithFormat:@"Result: %d", i];
        
    }
    

    This method waits for 3 seconds and then presents a random number between 0 and 99. Hook it up to a button in the Single View Template. Then add a Date Picker. Don’t hook that one up though: just spin it with all your might, and then click your button to see what happens.

    The Date Picker stops rolling while your action sleeps, and continues to spin when it’s finished to present the result. Simon Allardice used this trick in his course on Lynda.com – well worth watching.

    Now hook up another button to the following method:

    - (IBAction)backgroundQueue:(id)sender {
        
        // call the same method on a background thread
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            
            [NSThread sleepForTimeInterval:3];
            int i = arc4random() % 100;
            
            // update UI on the main thread
            dispatch_async(dispatch_get_main_queue(), ^{
                self.title = [[NSString alloc]initWithFormat:@"Result: %d", i];
            });
    
        });
    }
    

    This looks complicated, but thanks to Xcode’s code completion it’s easy to use wherever you need. Here’s what’s happening:

    We create a background queue and execute the same function as above. When it’s done we’ll grab the main queue (or main thread) again and update our UI element. This has to be done on the main thread. If you try it on the background thread it won’t work (it won’t crash either which is good to know).

    Try it out! You’ll soon be dispatching all kinds of things on a background thread!

    Here’s a fully working iOS 7 Demo Project on GitHub:

    I’ve written another article about this subject here:

     
  • Jay Versluis 10:05 am on April 11, 2014 Permalink | Reply
    Tags: ,   

    Xcode crashes every time you open it – and how to fix it 

    Have you seen this error message before? Perhaps the last 200 times you’ve tried to open Xcode?

    Screen Shot 2014-04-11 at 09.31.34It’s usually followed by its loveable companion, the crash report window. The one where you can enter a comment that nobody at Apple will ever read or respond to:

    Screen Shot 2014-04-11 at 09.33.20This is a peculiarity that can happen with a particular project. To avoid the first message, select the option “Don’t reopen windows”. This will bring up the Xcode welcome screen. The second message (the almighty Crash Reporter) may still come back to haunt you when you open a particular project – and only that project.

    Looks like that project has a problem.

    There is usually a way to fix this by deleting the xcuserdata file from the project. This is one of the most annoying features for version control: this set of files keeps track of which tabs/windows are open, which groups are expanded, which methods are collapsed, and in general how your project looks like when you left it.

    The trouble is of course that this file changes every 0.000001 seconds, so every time you make a git commit it’s almost instantly outdated. The implication is that you can’t switch to or merge a branch. If you go ahead and mess with this file outside of Xcode problems can arise – usually resulting in us developers wanting to take up another hobby or calling our therapist.

    How to fix this

    To fix this problem:

    • open a Finder window and navigate to your project
    • right-click on the .xcodeproj file (it’s a package actually)
    • select Show Package Contents
    • a new window appears
    • delete a folder called xcuserdata

    Now open your project again in Xcode and it should work. Your workspace will be reset, bringing a very small amount of happiness and tranquility back into your life.

    Until this problem happens again.

    How to avoid this in the future

    In an ideal world those xcuserdata files should all be ignored by Git. And in an ideal world we should never need to use external version control tools like the command line or GitHub for Mac.

    Sadly the version control tools in Xcode are far from perfect which means that we have no other choice. You can use your external tool to ignore these files of course, but adding them to a .gitignore is not enough to make them disappear retrospectively or in other branches.

    Here’s how to do that: once added to .gitignore, you must issue the following command:

    git rm --cached ProjectFolder.xcodeproj/project.xcworkspace/xcuserdata/yourUserName.xcuserdatad/UserInterfaceState.xcuserstate
    git commit -m "Removing file thats driving me insane"

    This may restore some order to your project.

     
  • Jay Versluis 2:10 pm on April 9, 2014 Permalink | Reply
    Tags: ,   

    How to tear down your Core Data Stack 

    Core-Data-IconYou may have heard this expression before: “You need to tear down your Core Data stack before you do xyz”. It’s frequently mentioned in the Apple documentation – sadly without telling us what on earth this means or how to do it.

    I had assumed that it means rebuilding every aspect of Core Data, which without a complete app re-launch looks like a daunting task. But it’s relatively easy if you know how to do it. In this article I’ll show you how.

    The secret is in the NSPersistentStoreCoordinator: all we really have to do is remove the store files from it and then add them again. Here’s a method that does just that and more (add it to your AppDelegate.m file to use it):

    - (void)rebuildCoreData {
        
        [self.managedObjectContext lock];
        [self.managedObjectContext reset];
        
        NSArray *stores = self.persistentStoreCoordinator.persistentStores;
        for (NSPersistentStore *currentStore in stores) {
            
            // remove store files
            [self.persistentStoreCoordinator removePersistentStore:currentStore error:nil];
    
            // delete those store files
            [[NSFileManager defaultManager]removeItemAtURL:currentStore.URL error:nil];
        }
        
        _managedObjectContext = nil;
        _persistentStoreCoordinator = nil;
        
        [self managedObjectContext];
        [self.managedObjectContext unlock];
    }
    

    Let’s go through this line by line: first we’ll lock and reset our managed object context for safe measure. This will make sure no other thread is working with it while we’re meddling.

    Next we’ll iterate through all possible store files in our coordinator. The Core Data template provide only one, but just in case you have more, we’ll remove them all. In this loop we’re also deleting all files from the hard drive.

    Then we set the variables of both coordinator and object context to nil – an ingenious tip from Luke McNeice (Thanks, Luke!) We do this because in the next step we can just call the custom initialiser for self managedObjectContext again which will rebuild the stack.

    Finally we unlock the context so other threads can access it concurrently.

    Here’s a fully working Demo Project on GitHub which employs this technique. It’s based on the Master/Detail Template and adds an extra button to tear down the stack in a running iOS App:

    Screencast

    I’m explaining how to do all this in a screencast too:

    Further Reading

     
c
compose new post
j
next post/next comment
k
previous post/previous comment
r
reply
e
edit
o
show/hide comments
t
go to top
l
go to login
h
show/hide help
shift + esc
cancel
Password Reset
Please enter your e-mail address. You will receive a new password via e-mail.