Search for an NSView inside a NIB by string - macos

I'm trying to write an application which should display some data. The data in question comes from a different module in our code (written in C, not ObjC), and for various reasons is identified by a string, not an integer or other form of constant. After the glue code, I have an incoming method on my AppDelegate like so:
-(void)newstringdata:(NSString*)data withLabel:(NSString*)label;
This method should always take the value of data and set it as the text for a particular label in the UI. The problem is, which label.
I could of course create an NSDictionary and fill it at run time with the possible values for the label parameter in the newstringdata:withLabel: method and references to outlets, but this seems somewhat ugly and inefficient; it requires me to maintain the outlets, the nib, and the NSDictionary-initializing code.
Instead, if possible, I would like to set a property in the interface designer somewhere, and then do a lookup in my newstringdata:withLabel: method based on the label which was passed which returns the NSLabel.
Is this possible? If so, how would I do it?

If the value of label will never include a slash (/), backslash (\), or colon (:), you can use the identifier property of NSView.
In the xib, enter each label string as the NSTextViews's identifier (in the Identity tab, 3rd tab from left).
Then in your code, loop through all views and:
if ([aView.identifier isEqualToString:label])
[aView setStringValue:data];

Related

Simplest way to identify an Interface Builder element in code

I am building a UI in Interface Builder and am looking for the simplest (least code) way to identify an element from code.
I'd like to avoid using outlets because frankly I detest visual programming and don't want to pollute my class space with countless outlet properties. Is there some unique string identifier I can assign to static elements that I can either reference directly or easily look up from code?
Ideally I just want to look up an object by its id like I can do in JavaScript:
document.getElementById('myIdentifier');
I agree with rightfold that outlets are the best solution, but there is an answer that addresses your question directly: you can use the (integer) tag property of UIView (setting it either in IB or in code), and then you can fetch the view with the method UIView -viewWithTag:.
Successive calls to -viewWithTag: will iterate through the subviews that have the given tag. Because it's an integer, you'll probably want to use named constants for tag references in code, but unfortunately there's no way (that I am aware of) to use constants in that manner in IB.
The default value for the tag property is 0, so avoid using that as a semantically meaningful tag.

NSTextView / NSScrollView - A few questions to help me understand it's proper usage

I have created a "notes" field designed to hold multiple paragraphs of text which I would like to store in a custom object. Originally, I just used an NSTextField as a temporary solution, but this does not allow me to scroll or have multiple paragraphs of text...
In IB I have placed a NSTextView (which seems to be wrapped inside an NSScrollView.) Upon execution of my program, seems to allow me to enter text in multiple paragraphs, scroll, etc. In short it LOOKS to be exactly what I want would like it to be. So far so good.
Now, I need to retrieve the data from this field and store it in my custom object. This is where I'm getting a bit lost within the developer documentation...
My goals are fairly straight forward:
Allow users to type away in the box.
Store the contents of the box into a variable (array, etc.) in my custom object when the user moves to another field, leaving the notes field.
Display the users stored text in the text box next time the record is viewed.
Second, is there a simple way to retrieve and store the data into a "notes" variable in my custom object (such as an NSString object? I would think having multiple would exclude an NSString object as an option here, but maybe I'm wrong) or am I getting into a more complex area here (such as having to store it in an array of NSString objects, etc.)?
Any help would be appreciated!
You can get the data using -string, defined by NSText (e.g. NSString *savedString = [aTextView string])
Your save code can be put in your NSTextDelegate (read, delegate of the NSTextView, because it's the immediate superclass), in – textDidEndEditing: which will be called, well, when editing is finished (e.g. when the user clicks outside the view) or one of the other methods.
Then to reload the saved string if you emptied the text view or something, use [textView setString:savedString] before editing begins.
NSTextDelegate documentation: here.
I'm not sure what you mena when you say "store the contents of the box into a variable (array, etc.) Are you hoping for an array of custom notes? Text views store a string of data, so the easiest way of storing its value is using one string; if you need an array of notes you'd have to split the string value into different paragraphs, which shouldn't be too hard.

understanding Xcode debugger var display

newbie question: can anyone tip me to how to understand/interpret what is displayed in the debugger var pane?
Ex: I am passing an NSDictionary as a method param. I set a breakpoint so I can examine the values in the dictionary. The image below (if it comes through..) shows the expanded view of this var in the debugger. It correctly reports that it contains 3 name/value pairs but as I expand all the sections, I simply can not find where these are stored.
Do I have to create local vars of these name/value pairs in order to view them when I want to check? I know I can use NSLog or printf but sometimes I just want a quick peek.
Right click the variable, click "Edit Summary Format" and type the following:
{(NSString*)[$VAR description]}:s
This replaces the GDB formatter for NSDictionary with a call to the more expensive description method.
That is, instead "x key/value pairs", you'll see the contents of the dictionary as produced by -[NSDictionary description].
This is the same as typing po dictionary in the console window. Or right clicking the variable and choosing "Print Description". Both of them call the description method of the object.
If you are curious, you can find this formatter at /Developer/Library/Xcode/CustomDataViews/Foundation.plist under the key NSDictionary. What you type as replacement is saved in /Users/USERNAME/Library/Developer/Xcode/UserData/Debugger/CustomDataFormatters and will persist across runs until you delete that file.
A NSDictionary is really a class cluster and few people know the inside structure. At this point you ain't going to find much use for that debugger tree.
This is what you are looking for :
Click on your dict, then click on the little "i" icon :-)

Make NSFormatter validate NSTextFieldCell continuously

In Cocoa, I have an NSOutlineView where the cells are NSTextFieldCell. The cell displays values which are strings that are formatted according to certain rules (such as floats or pairs of floats with a space in between). I have made a custom NSFormatter to validate the text, and this seems to work with no problem.
However, the cell (or the outline view, I'm unsure what is causing this) only seems to use the formatter at the moment my editing would end. If I type some alphabetic characters into the text field (which violates the formatting rules), these characters show up -- the only way I notice the formatter doing its job is that I'm now prevented from moving keyboard focus away from this cell. If I return the contents of the cell to a valid form, then I can move focus away.
I have set both the cell and the outline view to be "continuous".
It would be better if I was unable to enter text into the cell in the first place. Is it possible to make it like that, and if so, how?
Answering my own question because I found the solution. There is an optional method to override on NSFormatter, and this solves the problem. The optional method is:
- (BOOL) isPartialStringValid: (NSString*) partialString
newEditingString: (NSString**) newString
errorDescription: (NSString**) error
Here one can simply return NO if the partialString is invalid. One can return a fixed string by reference in newString if one wants.
There is another method which could also have been used, but it is more complex:
- (BOOL) isPartialStringValid: (NSString**) partialStringPtr
proposedSelectedRange: (NSRangePointer) proposedSelRangePtr
originalString: (NSString*) origString
originalSelectedRange: (NSRange) origSelRange
errorDescription: (NSString**) error

What's the best way to add a composite property for binding to an existing class

Let's say I have a Size class which has height and width properties (in reality the class is a bit more complex than this, but Size makes a good example).
I want to display this as $width x $height in my UI.
The obvious way to do this is to bind to a dimensions property which is dependent on width and height.
My question is where is the best place to add this new property?
I could add it to the Size class itself in the modal, but then if another controller wants to display the string different I'm stuck creating yet another property. I'm also a bit reluctant to do this because in my case the Size class is in a framework that will be used in a couple different places (although likely all from code I have control over).
I could add it in a category to the Size class inside the project containing the view/controller so it will only be availiable in the places I know it will be used, but in various places I've seen suggestions that categories tend to be overused, and am forced to wonder if this is one of those cases.
In the case of a single Size instance I could create the property in the controller class containing it, but this becomes more difficult when you have an array of Sizes.
I could bind to the entire size object and use a transformer to turn them into strings, but binding to an array of sizes would then cause you to have to transform each element of the array into a new array in the transformer, which seems a bit ugly.
If want to display this composite value as a string, then bind "Display Pattern 1" of a text field to the width property and "Display Pattern 2" (shown when you bind Display Pattern 1) to the height property. In the Display Pattern 1 binding, set the "Display Pattern" to %{value1}# x %{value2}# (yes, slightly unintuitive syntax). This will give a text field that displays "[width] x [height]" and will update on changes in either property.
You can do the same with an NSTextFieldCell, e.g. as the cell in an NSTableColumn. The downside of this method is that the text field will not be able to edit the bound width and height values. You would have to write an NSValueTransformer if you need to be able to edit them.
Using multiple bindings through a display pattern as Barry suggested sounds like the best approach, at least without knowing more about your UI. I've used the same thing in the past, where I built an inspector for an array of images and had the dimensions bound to a single text field.
In general it's good practice to use value transformers or formatters if you can, but in cases of last resort there's nothing wrong with using a category. I've done this in the past when I had trouble binding to a date, but breaking it down into individual time and date pieces. A category is a good approach because it lets you maintain separation with the model, but you don't need to do anything crazy like binding directly to the controller.
I want to display this as $width x $height in my UI.
Why not two fields? Then you could make them editable.
(in reality the class is a bit more complex than this, but Size makes a good example)
Assuming the above is not feasible in your real situation, you might try creating a custom subclass of NSFormatter, and setting it as the formatter on the cell (I assume this is in a table view, since you wouldn't bind a single control to an array). You would then bind to whole Size objects.

Resources