Archive for December 2009
Working through the Cocoa book, I successfully built and ran the example Core Data application. All was good until I noticed a build warning, “The ‘data’ binding is depreciated in Mac OS X versions 10.6 and later.” I looked at the discussion forum for the book (which isn’t much of a discussion forum – can’t provide links to other sites) and saw another mention of the problem. The issue comes from using the data binding on the Image Well, as described at the top of page 179 (in the 3rd edition of the book):
Data(not Value) of the image view to
Cars. Choose the controller key
selectionand the keypath
So, what’s the alternative that doesn’t use depreciated code? I don’t know for certain, but there’s discussion of it at the Mac Rumors forum. It may no longer be possible to create a Core Data application without writing code to implement a Value Transformer, if you want to convert from NSData to NSImage.
I’m sure there are good reasons behind codebase changes like this one. I look forward to understanding enough Obj-C and Cocoa to know what they are.
(Oddly, after doing nothing other than closing and reopening Xcode, I no longer get the build warning, which is even more confusing.)
This challenge is simple — type in the code provided, add the bindings in Interface Builder, and go… that is, until the “make sure to add sorting!” comment at the end.
Sorting the RaiseMan application that does not use NSArrayController requires only a careful reading of the “For the More Curious: Sorting without NSArrayController” section in Chapter 8. The key sentence here, which I missed several times, is, “An optional table view dataSource method is triggered when the user clicks the header of a column with a sort descriptor:”
There are a couple of things to notice and do:
tableView:sortDescriptorsDidChange: is a delegate method… so this means that you have to set MyDocument to be the delegate of the table view. Presumably you already did that if you set up the bindings correctly.
2. You need to enter the code that appears just above the “Challenge 1” header on page 135 into MyDocument.m – this is the delegate method called when you click the column header.
3. You cannot use the delegate method code as it appears in the book if you entered the code in Challenge 2, because you have a pointer to an IBOutlet object called tableView and this delegate method waits for a call from the interface element, not from your pointer, if that makes sense. Think about it this way — the flow of information happens in two directions: that coming in from the interface to the method, and that going from the method back to the interface. You’ll receive a compile warning if you call it tableView. Change it to aTableView. (You probably noticed similar changes in the code under Challenge 2.) When referring to the incoming messages, those should be called aTableView. When referring to outgoing messages, those should be tableView because that is what your IBOutlet pointer is called. Therefore, the final line,
[tableView reloadData] needs to remain tableView and not change to aTableView because you now want to call on the IBOutlet pointer to send instructions back to the interface.
4. The line
[myArray sortUsingDescriptors:newDescriptors]; needs to contain the name of your array… employees. It should read:
[employees sortUsingDescriptors:newDescriptors]; so that it sorts your array.
The final point tripped me up longer than any of the rest of it because I thought that the purpose of the exercise was to avoid using key coding. I was wrong. The point of the exercise is to not use NSArrayController. To sort your columns, you have to have sort descriptors. Those are entered in the same spot as in the previous challenge and the chapter exercise. In other words, when you have the name column in the table selected, the Interface Builder Table Column Attributes should look like this:
The same discussion about
localizedStandardCompare: is valid here, so you can change it to the latter if you don’t want Z to appear before a when you sort. Look here for more info. The full code follows the break:
Alright! Another challenge where I knew exactly what I wanted to do and spent a lot of time wrestling with the interface.
The challenge is to change the sorting on the RaiseMan column to show the names in the order of the number of characters in them. The hint is to use a key path and the “length” method of strings. Seems straightforward. In code, to find the length of the personName string, I would simply write [personName length]. Let’s see… key values, paths, selectors, etc….. What??
The obvious thing to do here is to return to the previous chapter and read about key paths. Except, that doesn’t help at all and may lead to confusion. (I would reference my previous post about the confusion I experience when trying to migrate coding concepts to Interface Builder.)
There’s no point in drawing this out. The proper answer is simply to make the sort key personName.length and the selector to be compare:, like this:
You have to use compare: instead of caseInsensitiveCompare: because there is no case to an integer — no lowercase and/or uppercase to compare.
I tried several other approaches, including different Selectors, and the notation discussed in Chapter 7, such as “… valueForKeyPath@”personName.length];”. I think that my general lack of knowledge of C and my inexperience with Obj-C led me to try everything but the obvious. You see, the discussion of key paths in Chapter 7 only mentioned that they could be used to find attributes of objects. Length is, of course, an attribute of a string, but the challenge mentions length not as an attribute, but as a method of Strings. I took that to mean as a method of NSString. That returned me to the initial problem – how to return the value from [personName length] to the sort key.
In the end, I don’t think that this challenge is much of a challenge because I don’t recall ever learning that I could use a method with dot notation to return an attribute as a key path. My suggestion to the author would be to specifically mention this in the “For the More Curious: Key Paths” section of chapter 7, alongside the mention of “Properties and Their Attributes,” where the author specifically derides the dot syntax and says that he won’t be using it in the book.
Still, though, I haven’t crossed the mental bridge from length being a method and length being a property and/or attribute of NSString. It must be a getter method for NSString, and hence, works with the dot syntax? The Apple developer documentation states that length is a primitive method — does this mean it can be accessed as a getter method?
While I now know that I can use this syntax for strings, I have yet to find a description of why it works or how it applies to other classes and methods. The challenge, therefore, became a guessing game and the learning lesson became frustration.
One of my gripes with most of the programming books that I’ve read is that the early and ‘simple’ lessons about how to sort an array neglect multiple occurrences of the same word, capitalized differently. Cocoa Programming for Mac OS X, 3rd Edition is one of the books that ignores this point. Granted, I have no doubt the author is aware of the issue and easily could address it, but he chose not to in the text. Faced with a similar challenge in a Ruby programming book, I wrote a method that would properly handle such cases.
In the Cocoa book, Chapter 8 starts the reader on the development of the RaiseMan application. Sorting is implemented through a key path in Interface Builder, using the selector caseInsensitiveCompare:. It works great until you do the following:
Granted, the method used is caseInsensitiveCompare:. The documentation lacks a clear explanation of what this does. It sounds self-explanatory. Nevertheless, it annoys me. The first challenge at the end of the chapter is to implement a different type of sorting, using only Interface Builder. Prior to getting to that, I trolled through the documentation to find how to sort in a case sensitive manner. You might think that this would be called caseSensitiveCompare, but it’s not. I don’t know what the appropriate method to use here actually is, but I went with localizedStandardCompare: instead of caseInsensitiveCompare: and ended up with the following:
This is better, but shouldn’t the capitalized items appear before the lowercase items? That’s how it would appear in a dictionary, right? Also, what does localized mean in this context? Finally, why doesn’t the documentation include examples of how sorted/compared items will return? Including such examples would clarify all of this for me.
Therein lies my biggest problem with Cocoa so far. I understand Objective-C. I’m not an expert, by any means, but I get it and I can write foundation scripts without too much of a hassle. Cocoa isn’t clicking with me because I’m struggling to connect the dots between what I see in Interface Builder and what happens in code. For instance, caseInsensitiveCompare: and localizedStandardCompare: are methods. In code, I know how to call a method, how to pass it arguments, etc. Why are these methods called “Selectors” in Interface Builder? The sort key is personName, why isn’t the sort method caseInsensitiveCompare:? That would make more sense to me.
Chapter 8 in the Cocoa book is a deluge of unexplained Interface Builder instructions. Using NSArrayController as a controller object in the MVC framework makes sense but the steps for implementing it are difficult to grasp because what would be clear in written code becomes unclear when entered in separate text boxes in Interface Builder.
Practice makes perfect, I suppose. I’ll keep at it and I look forward to the “A Ha!” moment when it clicks and I can move forward without all of the hand holding.
It’s been busy around here. I’ve started and stopped this challenge a few times over the course of a week, but finally returned to it yesterday evening. The time away from it actually made it easier. Armed with the knowledge that most of what I needed to know was in the chapter and that the challenge would be easier than the teachings in the chapter, I dove in and tried to keep it simple. I haven’t made it editable yet, but probably will tackle that tonight, since I think it will be simple. The final app looks like this:
Well, this was a good lesson for me to learn some objective-C while working. The problem is that I was faced with a lot of .xml files, each of which had thousands of values concatenated as a single string in several list elements. This is what the .xml files looked like:
View the entire post to see the code.
Read the rest of this entry »
I love it when my personal interests and desires to learn overlap with my work duties. Faced with the onerous task of converting a gazillion .xml files to an Access database, I tried one manually before deciding to learn some Objective-C in the process. Granted, this is some simple stuff for experienced programmers, but it’s a good learning lesson for me. It also allows me to focus for a minute on the Obj-C without worrying about the Cocoa (interface) elements. I’ll post the code when I’m done with it. Until then, egg nog.