I want to show in a label the current number of elements in an NSArray called pages. Following other guides I did the following:
Created an NSArrayController in IB (called pagesController) and bounded it to the NSArray pages
Bounded the value property of the label to the NSArrayController with Controller Key = arrangedObjects and Model Key Path = #count
The problem is that when the program is running the label always shows "0". To check if the things are working correctly I tried to log a message when the user clicks on another button (the button basically inserts a new element in the pages array): NSLog(#"count = %d", [self.pagesController valueForKeyPath:#"arrangedObjects.#count"]); in this case the output is correct, i.e. the current number of elements in the array printed is correct.
Where am I mistaking?
You many not be using KVC correctly. Your array controller will only be notified with the pages property is redefined, not when objects are added to the array. Try wrapping your code with the appropriate change notifications:
[self willChangeValueForKey:#"pages"];
[pages addObject:someObject];
[self didChangeValueFOrKey:#"pages"];
Related
So what I'm trying to do essentially add a button next on each cell in my UITableView. Each cell in my tableView is being populated from my Event Class on parse where each object is displayed in a new cell.
Once a user hits this button it takes whatever Event object from the Event class they've clicked and possibly adds it to a "property" in the Parse User Class I'll create named "watching".
But how would I do something like this to where they're able to have all of these Events under the User Class "watching property"? Would I have to set up some relation of some sort?
Summary:
Basically wanting to have a watch list people are able to watch any of the objects posted by any of the users in the Events Class. These objects that are being watched are added to a property in the User Class so that I can easily create a UITableView pulling the current users watching property list of items and displaying them.
I hope I have understood what you want ... If I understand it you need a form of relationship between the object and the CurrentUser viewing it ...
normally to identify the selected cell within a IBAction you can 'use this method:
- (IBAction) button: (id) sender {
NSIndexPath * indexPath = [self.TableView indexPathForSelectedRow];
PFObject SelectedObject * = [self.YOUR_ARRAY objectAtIndex: indexPath.row];
}
This way you've identified which cell the current user has selected .. From this point on, continue with the PFObject to save the data the way you want
The essential thing is to set the goal of PFObject (in this case the selected cell) then continues to set the data to be set according to what you need
You could create a field on your PFUser class : #"watching"
Then use this as an array, so whenever the user clicks on something to watch ...
[[PFUser currentUser]addObject:objectToWatch.objectId forKey:#"watching"]; // adds it to the array
[[PFUser currentUser]save];
// something about arrays and parse objects, if you're using operations like "addObject" you should save or you'll get errors.
// the other option is to get an NSMutableArray from the array field, edit that, and reassociate it with the #"watching" key ... whichever you prefer
We now have an array of Id's associated with objects to watch. You could query like so
PFQuery * query = [PFQuery queryWithClassName:#"ObjectsToWatch"];
[query whereKey:#"objectId" containedIn:[PFUser currentUser][#"watching"]];
[query findObjectsInBackground]; // use a block method though
This is only one way to do it, hope it helps
So what I'm trying to do essentially add a button next on each cell in my UITableView. Each cell in my tableView is being populated from my Event Class on parse where each object is displayed in a new cell.
Once a user hits this button it takes whatever Event object from the Event class they've clicked and possibly adds it to a "property" in the Parse User Class I'll create named "watching".
But how would I do something like this to where they're able to have all of these Events under the User Class "watching property"? Would I have to set up some relation of some sort?
Summary:
Basically wanting to have a watch list people are able to watch any of the objects posted by any of the users in the Events Class. These objects that are being watched are added to a property in the User Class so that I can easily create a UITableView pulling the current users watching property list of items and displaying them.
I hope I have understood what you want ... If I understand it you need a form of relationship between the object and the CurrentUser viewing it ...
normally to identify the selected cell within a IBAction you can 'use this method:
- (IBAction) button: (id) sender {
NSIndexPath * indexPath = [self.TableView indexPathForSelectedRow];
PFObject SelectedObject * = [self.YOUR_ARRAY objectAtIndex: indexPath.row];
}
This way you've identified which cell the current user has selected .. From this point on, continue with the PFObject to save the data the way you want
The essential thing is to set the goal of PFObject (in this case the selected cell) then continues to set the data to be set according to what you need
You could create a field on your PFUser class : #"watching"
Then use this as an array, so whenever the user clicks on something to watch ...
[[PFUser currentUser]addObject:objectToWatch.objectId forKey:#"watching"]; // adds it to the array
[[PFUser currentUser]save];
// something about arrays and parse objects, if you're using operations like "addObject" you should save or you'll get errors.
// the other option is to get an NSMutableArray from the array field, edit that, and reassociate it with the #"watching" key ... whichever you prefer
We now have an array of Id's associated with objects to watch. You could query like so
PFQuery * query = [PFQuery queryWithClassName:#"ObjectsToWatch"];
[query whereKey:#"objectId" containedIn:[PFUser currentUser][#"watching"]];
[query findObjectsInBackground]; // use a block method though
This is only one way to do it, hope it helps
I'm writing my first master detail view in Cocoa. My data model is really simple: just an NSMutableArray that contains instances of a single class having a few NSStrings properties.
Here's what I've created so far:
A class representing the instances.
An NSMutableArray specified as a property of my app delegate to hold the class instances.
A master detail view, with an NSTable and some text fields to hold the properties of a selection instance.
An NSArrayController with a binding specifying the app delegate, and the name of the NSMutableArray property as the model key path.
Bindings between the columns of the NSTable and the NSArrayController (controller key = "arrangedObjects", and model key path of each column = a property of the class).
Bindings between the text fields of the view and the selection (controller key = "selection", and model key path of each text field = a property of the class).
A "+" button and a "-" button in the view to enable adding and removing objects.
However, I'm having two problems with this design:
(1) I can't find a good way to implement the "+" and "-" buttons. First, I bound them to the add: and remove: properties of the array controller. While this seems to work, it has a problem: my class declares an init member that initializes the NSStrings to stub values, but the array controller doesn't seem to [init] the new instances, because the new list entry has empty strings for each column.
Next, I attached them to IBActions in my app delegate that added or removed an object from the NSMutableArray. However, this feels wrong - it feels like I'm violating the model-view-controller architecture by not talking to the array controller. For example, the "-" function has to talk to the array controller to get the selected item. Also, I notice that I have to send a [didChangeValueForKey] message after altering the array - which feels like a signal that I'm doing this wrong.
(2) One of the detail subviews in my view is an NSTextView that's bound to an NSString in the selected object. It really isn't working as expected: when new text is entered, the text remains the same even if other members of the class (which should have different values) are selected. Also, the text data isn't being saved to any instance of the class.
Any thoughts? Thanks in advance.
Here is an example that should be close to what you want:
#import "AppDelegate.h"
#import "Members.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
self.theData = [NSMutableArray arrayWithObject:[[Members alloc] init]];
}
-(IBAction)addMember:(id)sender {
[self.controller addObject:[[Members alloc] init]];
}
-(IBAction)removeMember:(id)sender {
[self.controller removeObjectAtArrangedObjectIndex:self.controller.selectionIndex];
}
The array controller (whose IBOutlet is controller) has its content array bound to theData. Members is the name of my class that has 3 string properties, name, author and blurb. In the detail view, the 2 text field's are bound to ArrayController.selection.name or author, just like you said you did. The other property, blurb, is bound the same way (ArrayController.selection.blurb), but to the Attributed String value of a text view. This worked fine, I'm not sure what your trouble with the text view was. If I add text to the text view, it does show up there if I click another row in the mater table and then click back (also if I log the array, it shows up there too).
i created a nsarraycontroller to display its data on my nstableview. this is working. but how do i modify this data now?
[arrayController addObject:[...]]
adds an object, how do i get it back?
i tried:
NSMutableArray *data = [arrayController mutableArrayValueForKey:#"column1"];]
but then i get this error
2011-05-29 19:25:50.125 TestApp[1665:903] [<NSArrayController 0x113808500> valueForUndefinedKey:]: this class is not key value coding-compliant for the key column1.
(the objects in my arraycontroller representing the rows in my view are of a class (named FileEntry) consisting of 4 properties and i added these as keys to my nsarraycontroller)
i thought kvc-compilant means every attribute has a getter and setter? (and isnt that what properties do?).
i tested my class if i could use this function:
FileEntry *entry = [[FileEntry alloc] initWithUrl:#"test"]; //(this adds the string "test" to the property "fileurl")
NSLog(#"%#\n", [entry valueForKey:#"fileurl"]);
and it returns:
> 2011-05-29 19:31:54.760 TestApp[1718:903] test
and it works. so how can my class not be kvc-compilant?
anyway, i also tried to use the tableviews datasource instead but cant get those 2 functions to work. would that be of more use than the nsarraycontroller? is it even possible to modify data of the nsarraycontroller?
Usually you need a contend array for the NSArrayController to handle, so you could simply take this array and use the NSMutableArray methods. For example you have in your MyController.h a declaration of an array with its setter and getter methods, and you only have to bind the contend array of the array controller to it. Then you can take two buttons for the add and remove methods of the array controller to add something. Now you must bind the value of the column to e.g. FileEntry.fileurl of your array controller managed objects, Now you should be able to add stuff to the table view. And you can also Edit the names in the table view. If you want to get some stuff out of the array in your program somewhere, you can use an outlet of your table view like this:
NSInteger row = [myTableViewOutlet selectedRow];
FileEntry* myEntry = [myArray objectAtIndex:row];
I am working on an app that is not core data based - the data feed is a series of web services.
Two arrays are created from the data feed. The first holds season data, each array object being an NSDictionary. Two of the NSDictionary entries hold the data to be displayed in the popup ('seasonName') and an id ('seasonID') that acts as a pointer (in an external table) by matches defined for that season.
The second array is also a collection of NSDictionaries. Two of the entries hold the data to be displayed in the popup ('matchDescription') and the id ('matchSeasonId') that points to the seasonId defined in the NSDictionaries in first array.
I have two NSPopUps. I want the first to display the season names and the second to display the matches defined for that season, depending on the selection in the first.
I'm new at bindings, so excuse me if I've missed something obvious.
I've tried using ArrayControllers as follows:
SeasonsArrayController:
content bound to appDelegate seasonsPopUpArrayData.
seasonsPopup:
content bound to SeasonsArrayController.arrangedObjects; content value bound to SeasonsArrayController.arrangedObjects.seasonName
I see the season names fine.
I can obviously follow a similar route to see the matches, but I then see them all, instead of restricting the list to the matches for the season highlighted.
All the tutorials I can find seem to revolve around core data and utilise the relationships defined therein. I don't have that luxury here.
Any help very gratefully received.
This is not an answer - more an extension of the previous problem.
I created MatchesArrayController and subclassed it from NSArrayController to allow some customisation.
Following the example in 'Filtering Using a Custom Array Controller' from 'Cocoa Bindings Topics', I followed the same idea as above:
MatchessArrayController: content bound to appDelegate matchesPopUpArrayData.
matchesPopup: content bound to MatchesArrayController.arrangedObjects; content value bound to MatchesArrayController.arrangedObjects.matchDescription.
I've derived the selected item from seasonPopUp:sender and used this to identify the seasonId.
The idea is to change the arrangedObjects in MatchesArrayController by defining the following in;
- (NSArray *)arrangeObjects:(NSArray *)objects
{
if (searchString == nil) {
return [super arrangeObjects:objects];
}
NSMutableArray *filteredObjects = [NSMutableArray arrayWithCapacity:[objects count]];
NSEnumerator *objectsEnumerator = [objects objectEnumerator];
id item;
while (item = [objectsEnumerator nextObject]) {
if ([[[item valueForKeyPath:#"matchSeasonId"] stringValue] rangeOfString:searchString options:NSAnchoredSearch].location != NSNotFound) {
[filteredObjects addObject:item];
}
}
return [super arrangeObjects:filteredObjects];
}
- (void)searchWithString:(NSString *)theSearchString {
[self setSearchString:theSearchString];
[self rearrangeObjects];
}
- (void)setSearchString:(NSString *)aString
{
[aString retain];
[searchString release];
searchString=aString;
}
I've used NSLog to check that things are happening the way they are supposed to and all seems ok.
However, it still doesn't do what I want.
[self rearrangeObjects]; is supposed to invoke the arrangeObjects method but doesn't. I have to call it explicity
(i.e.[matchesArrayController arrangeObjects:matchesPopUpArrayData]; )
Even then, although filteredObjects gets changed the way it is supposed to, the drop down list does not get updated the way I want it to.