Bind Label to class' property - objective-c

I'm trying to bind a label (NSTextField) to a class' property. In Interface Builder I bind the label's Value to File's Owner with Model Key Path = self.aString.
File's Owner is a NSViewController's subclass with aString defined as: #property (nonatomic, strong) NSString *aString;
The label is placed inside a View Based NSTableView filled at run-time by a binding with a managed object context.
When i call [self setAString:#"..."] or [self setValue:#"..." forKey:#"aString"] in the class' implementation, nothing changes in the table view. However if the label is placed inside the view it works. Why?

This is a limitation of view-based table views, and from what I know the only way to fix it would be to set TableView.delegate to your File's Owner (or another object if you're trying to bind to that instead).
(I'm hoping for a better answer to explain why, because I don't know why this is the case)
Cake's answer is a good workaround.

I solved by subclassing NSTableCellView and adding to it a NSString property. Then i bound the value of the label to the Table Cell View with the name of the property as key.

You probably don't have direct access to it when it's inside the tableview. Try storing a reference to the tableview and then calling the label. So if your table had a reference named t, connect the label to the table and then:
t.labelName.text = #"string" Although, if it's inside a cell in the table you may need to use cellForRowAtIndexPath and reference the label inside a subclassed cell. If you don't know how to do this I can post some code.

Related

Where to define NSArray and where to define Button action?

I have made an array of text strings and want to pull these out an into a label by EITHER swiping of pressing a button. So i have two different functions/methods, the button and the swipe method.
Where and how do I define the array so that these methods can refer to it? Should it be a 'extern NSArray' ?
I have uploaded the image of full code externally http://s1.postimg.org/b2e3m4v67/Sk_rmbillede_2014_05_11_kl_15_48_28.png
Not sure though if that's a violation of some rules here(?)
You want the quote to change on swipe/button press.
In your button press/swipe methods you're setting the text property of the VC's label property to something called Quoteselected. And it looks like Quoteselected is a random element of the array Quotes - or at least maybe it is, since that random number could be 6-10, and you don't have any objects in the Quotes array at those indices - so if those numbers are ever generated by the random function, your program will crash due to an index out of bounds error.
What you probably want to do is generate a new random number on each user interaction and then at that point change the value of Quoteselected to be the object at that index of the array. And then assign that to the label's text property.
As far as defining the Array - I wouldn't have done it the way you did. What you've got there is an "ivar", an instance variable. On iOS, those are typically properties. And since it's a "private" array that outside classes won't need to know about, I'd declare it as a part of the class extension.
So,
#interface BOViewController()
#property NSArray *quotes;
#end
Also note my capitalization changes, that's better style.
So now you've got an array property declared, but there's no data in it. It depends on how you created your View Controller instance. Assuming you did it in a storyboard, it would go in awakeFromNib: or viewDidLoad: (if you instantiated the VC automatically, you might put it in the initWithNibName: method).
- (void)viewDidLoad {
[super viewDidLoad];
self.quotes = #[#"Test", #"Number 3"...];
Then when you want to reference the array in other parts of the class:
self.label.text = self.quotes[0];
Note that your existing code should work, it's just not typical Cocoa coding style.

Bindings vs IBOutlets in Objective-C/Cocoa

I'm going through a simple Objective-C/Cocoa program to try and learn the language and am getting a little confused about how some things are linked from the code I write to the interface builder.
For example, I have a simple NSString:
#property (assign) NSString *letters;
And in my interface builder, I have a text field and I use the text field's bindings to connect it to letters.
However, the example also has this:
#property (assign) IBOutlet NSArrayController *wordsController;
In the view I have a table that continuously changes and shows different words and those words are stored in an NSMutableArray. I suppose I can understand that I just can't bind the array to the the table because there are some more complexities. So in the Interface Builder I create an Array Controller and bind it to the table. In the Array Controller's bindings, I bind the Array Controller to the array of words.
I understand that the last thing I have to do is also bind the Array Controller to my NSArrayController object as well. I don't understand why I do this through the main controller object by making a connection between this outlet and the wordsController. In the Array Controller's bindings section there's a greyed out option, Content Object, which says "An NSArrayController that the NSArrayController treats as its content." Why wouldn't I set the binding here? What is the significance of it being an outlet and why is it different than my NSString letters?
Thanks
You are confusing bindings and IBOutlets. This is not unreasonable -- it's a lot of Control-dragging of connections and it can be hard to keep clear what's going on. Let me try to explain:
Bindings are a way to let Cocoa handle the mechanics of keeping a model (some collection of data, even something as simple as a single NSString) and a view (an object which displays on the screen) in sync. When you "bind" your NSString to a text field's value, you are asking the framework to communicate changes to the string or the text field "behind the scenes"; your object which owns the string gets notified to change the string's value when the text field changes, and vice versa.*
A similar situation applies to your mutable array, array controller, and table view. You're essentially right about the complications: the mutable array and the table view don't know how to talk to each other; the array controller stands in between and facilitates: ("Okay, tableView wants to know what to put in row i. Array, give me your object at index i." :) In the past, you would've had to write that code manually, and it looked very similar every time you did so.
That's what the bindings do. They are a way to reduce boilerplate code. The IBOutlet to the array controller gives your object a way to send messages to the array controller, if necessary. One simple example of why you might need to do this is to allow a menu item to trigger a method in the array controller; a document object or another controller might handle the action from the menu item and call the appropriate message on the array controller. You can also ask the array controller for its arrangedObjects to get, for example, the sorted and filtered version of its content array.
* One side note here is that your NSString property should almost certainly use retain, not assign. The object that contains this variable should be responsible for its memory.

updating table view with data in array?

I am creating a Ipad with two view controllers. One view controller is FirstViewController and the other is SecondViewController. In FirstViewController, I fill an array with numbers. Now in my SecondViewCOntroller, I have a table view. I want to put the array that I created in FirstViewController into my SecondViewController table view? How do I do this? Please help me!
You need to reference the NSArray object in the SecondViewController, you could do this by means of a delegate. A delegate is an instance variable that contains the pointer to the delegate, in this case the FirstviewController. Then in FirstViewController you add a property for the NSArray if its an instance variable, and call delegate.someArrayName in the secondviewController
This approach breaks MVC. You can't have data array as an instance variable in your FirstViewController. You'd have to store data in some other class (the M part of MVC). You fill that M part from FirstViewController (the V part) and then access that filled M part from SecondViewController. This way you won't be dependent on how those two controllers relate to each other (parent/child or siblings or whatever other hierarchy you may think of).
The most simple approach I can think of is storing serialized array in a plist file. Storing the file in first and accessing it in the second view controller.
The most straight forward approach will be to create a property on SecondViewController.h like:
#property (nonatomic, retain) NSMutableArray *yourArray;
and in SecondViewController.m, put:
#synthesize yourArray;
At this point you have created a property on SecondViewController. Now, when you are about to open Second View Controller, just create its instance and do something like following:
secondViewController.yourArray = array;
[self.navigationController pushViewController:secondViewController];

How to access attributes for a UI element in Interface Builder

For example, I have a UISlider that calls - (IBAction)sliderMoved:(id)sender, which performs some task once a new value has been "slid" to through the Value Changed event. But what if I want to do something based on whatever its initial value is?
Hard-coding something to match the default value in Interface Builder would be sloppy and hard to debug later if someone changes the nib file. So, how can I access the default value set for this UISlider in Interface Builder?
AND, more generally speaking, how does one access these attributes for any such UI element? Are there setter/getter methods already synthesized for these?
As Maunil said, Use bindings to connect the UISlider in InterfaceBuilder to a UISlider object in the code. Once this is done, you can access all its methods & properties.
To set the initial value, use the setValue: property of the UISlider, something like:
- (void)viewDidLoad {
...
// where mySlider is declared as a UISlider object and bound to your XIB slider
[mySlider setValue:some_default_value];
...
}
See the UISlider Class Reference documentation for more information.
You can declare the slider in the header file as IBOutlet, and then hook it up in IB. Then you will be able to access all its properties in code.

When does selectedCell change?

I have an NSTableView and I want to do something whenever the selectedCell element changes.
So, my table view is called tableView, and this is what I want to observe:
[tableView selectedCell]
I tried using key-value observing, but that didn't seem to work, or maybe I was doing it wrong. Any ideas?
Most properties of Cocoa's own classes are not observable. If a property is observable, the documentation for it will explicitly say so; if the documentation doesn't say a property is observable, assume it isn't.
Furthermore, properties that don't exist are doubly not observable. The documentation for NSTableView and NSOutlineView both mention no method named “selectedCell”. You should assume there isn't one.
If you want to know when the user selects a different row, be the table view's delegate; it sends delegate messages for that, if you'll respond to them.
NSTableView will use one and only one dataCell object for each column. selectedCell is the wrong way. You can use selectedColumn to get the selected column and then ask for its dataCell.
And: I guess you are searching for NSTableView delegate methods
tableViewSelectionDidChange: and tableViewSelectionIsChanging: