Change selected object with multiple nsarraycontrollers - objective-c

I am trying to implement example as shown on developer.apple.com
Everything works but selectedWeapon does not change.
Q1: I have no idea what I forgot so the selection would work correctly
Q2: According to Apple "the example requires no actual code to set up the user interface". I am filling objects from code. Is there any way to add objects into arrayController in XIB?
#implementation Combatant
- (instancetype)initWithName:(NSString *)aName
{
self = [super init];
if (self) {
_weapons = #[#"Dagger", #"Sword", #"Pike"];
_name = aName;
_selectedWeapon = [_weapons firstObject];
}
return self;
}
#end
#interface Combatant : NSObject
#property (nonatomic, strong) NSString *name;
#property id selectedWeapon;
#property NSArray *weapons;
- (instancetype)initWithName:(NSString *)aName;
#end
Repository: https://github.com/xhruso00/moderatelyComplexBindings

Q1: The selection index of NSPopupButton wasn't linked to arrayController. Without it the arrayController did not know which option is selected.
Q2: Impossible. Apple talks about the glue code.

Related

Issue adding to NSMutableArray

I have looked all over the place for anyone who has experienced this issue but have yet to find anything relevant, so I thought I'd ask it myself...
I have a custom object (HitterData) which I will use to populate cells in a UITableView, then two ViewControllers (one is hitterTableViewController, the other is a "detail" view controller labeled "AddPlayerViewController").
The problem is that I can add HitterData objects to the NSMutableArray in my Table VC, but only one, and then when I add another one using the detail view controller, the Mutable array is "reinitialized" and I can again only have one object at a time.
HitterObject:
#implementation HitterData.m
#synthesize hitterName = _hitterName;
#synthesize position = _position;
-(id)initWIthNameAndPosition:(NSString *)hitterName position:(NSString *)position {
if ((self = [super init])) {
self.hitterName = _hitterName;
self.position = _position;
}
return self;
}
HitterTableViewController.h
#import "HitterData.h"
#import "HitterDoc.h"
#import "AddPlayerViewController.h"
#interface HitterTableViewController : UITableViewController
#property (nonatomic, strong) NSMutableArray *hitters;
- (IBAction)backButton:(id)sender;
- (IBAction)addPlayerView:(id)sender;
-(void)addHitterObject:(HitterData *)hitter;
HitterTableViewController.m (only relevant to make this more readable)
#implementation HitterTableViewController
#synthesize hitters = _hitters;
- (void)viewDidLoad {
[super viewDidLoad];
self.hitters = [NSMutableArray array];
}
-(void)addHitterObject:(HitterData *)hitter {
if(_hitters != nil) {
[_hitters addObject:hitter];
} else {
_hitters = [NSMutableArray array];
[_hitters addObject:hitter];
NSLog(#"MutableArray is not valid");
}
}
AddPlayerViewController.h
#interface AddPlayerViewController : UIViewController <UITextFieldDelegate, UINavigationControllerDelegate>
#property (weak, nonatomic) IBOutlet UITextField *nameTextField;
#property (weak, nonatomic) IBOutlet UITextField *positionTextField;
#property (nonatomic) HitterTableViewController *hitterTable;
#property (nonatomic) NSString *hitterName;
#property (nonatomic) NSString *position;
//-(void)addNewHitterToHittersArray:(HitterData *)hitter;
- (IBAction)addPlayerToRoster:(id)sender;
AddPlayerViewController.m
#implementation AddPlayerViewController
#synthesize hitterTable;
- (void)viewDidLoad {
[super viewDidLoad];
hitterTable = [[HitterTableViewController alloc] init];
}
- (IBAction)addPlayerToRoster:(id)sender {
self.hitterName = [self.nameTextField text];
self.position = [self.positionTextField text];
HitterData *hitter = [[HitterData alloc] init];
hitter.hitterName = self.hitterName;
hitter.position = self.position;
[hitterTable addHitterObject:hitter];
ArraySingleton *arrayS = [[ArraySingleton alloc] init];
[arrayS initializeArray];
[arrayS addToHittersArray:hitter];
if (arrayS) {
NSLog(#"arrayS exists in AddPlayerVC");
} else {
NSLog(#"arrayS does not exist");
}
[self performSegueWithIdentifier:#"backToTeamTableViewController" sender:self];
}
Am I missing something here?
Guess based just on the code shown:
Every time you wish to add a player it looks like you create a new AddPlayerView/AddPlayerViewController. In turn that controller creates, in its viewDidLoad, a new HitterTableViewController - which of course has its own empty array. The code should instead be referencing the existing HitterTableViewController.
BTW: The common design pattern is MVC - model, view, controller - consider whether you are in your current situation because you've stored part of your model, the array, in your controller, and maybe both controllers should be referencing a common shared model object containing that array.
BTW: All those #synthesise statements are unneeded. In modern Obj-C synthesis is automatic and you rarely need these statements. Also it is generally recommended to not use the property backing variable directly, and certainly not when storing into the property as this breaks KVO. (There also appears to be a related typo in HitterData.m but as you don't report that as not working it is probably just a typo in your question and not code.)
HTH
AddPlayerViewController should know nothing about HitterTableViewController, return the new HitterData object with a delegate method.
- (IBAction)addPlayerToRoster:(id)sender
{
Hitter *hitter = [[Hitter alloc] init];
hitter.name = [self.nameTextField text];
hitter.position = [self.positionTextField text];
[self.delegate didAddHitter:hitter];
}
Then back in HitterTableViewController
- (void)didAddHitter:(Hitter *)hitter
{
[self.hitters addHitter:hitter];
[self dismissViewControllerAnimated:YES completion:nil];
}

Spitting out objects from array in Objective-C

I'm quite new to classes and objects and I have a question:
I'm keeping track of books which can be input by textFields.
3 properties per book: Title, Author and Description.
What I'm trying to do is get all the objects of books in a NSMutableArray called: Collection.
(at the moment it's only 1 book (objectAtIndex:0)
Which is currently working but when I try to spit them back out I only get the description of the book. I'd love to get all the items (title, author, description).
What I've been wondering is: should I make a new (collection) class for example called BookCollection and make an array there? But how would I init it etc?
The code is below, help and tips are welcome!
(Started about a month ago)
Book.h
#import <Foundation/Foundation.h>
#interface Book : NSObject
#property(nonatomic,strong)NSString* title;
#property(nonatomic,strong)NSString* author;
#property(nonatomic,strong)NSString* description;
-(id)initWithTitle:(NSString*)newTitle withAuthor:(NSString*)newAuthor andDescription:(NSString*)newDesription;
Book.m
#import "Book.h"
#implementation Book
#synthesize title,author,description;
-(id)initWithTitle:(NSString*)newTitle withAuthor:(NSString*)newAuthor andDescription:(NSString*)newDesription{
self = [super init];
if (self) {
title = newTitle;
author = newAuthor;
description = newDesription;
}
return self;
}
#end
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize lblTitle,lblAuthor,lblDescription;
#synthesize collection;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
}
- (IBAction)buttonClick:(id)sender {
//alloc the array that will hold the books
collection = [[NSMutableArray alloc]init];
//create a new book
Book *newBook = [[Book alloc]initWithTitle:[lblTitle stringValue] withAuthor:[lblAuthor stringValue] andDescription:[lblDescription stringValue]];
//logging the items of the book
NSLog(#"%#",newBook.description);
NSLog(#"%#",newBook.title);
NSLog(#"%#",newBook.author);
//adding the book to the collection
[collection addObject:newBook];
//logging the book items from the collection
NSLog(#"%#",[collection objectAtIndex:0]);
//problem... only logs 1 item from the object...
}
#end
AppDelegate.h
#import <Cocoa/Cocoa.h>
#import "Book.h"
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property(nonatomic,strong)NSMutableArray *collection;
#property (assign) IBOutlet NSWindow *window;
#property (weak) IBOutlet NSTextField *lblTitle;
#property (weak) IBOutlet NSTextField *lblAuthor;
#property (weak) IBOutlet NSTextField *lblDescription;
- (IBAction)buttonClick:(id)sender;
#end
You need to define your Book class's -description method.
When you call NSLog(#"%#", someObject), the object's -description method gets called and placed inside the %# format specifier. You'll want to override your Book class's -description method to print out all of the object's fields.
Have a look here for a good example.
To clarify, when you call:
NSLog(#"%#",newBook.description);
NSLog(#"%#",newBook.title);
NSLog(#"%#",newBook.author);
You are (quite correctly) logging each individual field. However, when you call:
NSLog(#"%#",[collection objectAtIndex:0]);
You are essentially writing:
NSLog(#"%#",newBook); // Gets an NSString from [newBook description];
And so you need to implement - (NSString *)desctiprion for the Book class to get the logging behaviour you are after.
Step 1 : remove collection = [[NSMutableArray alloc]init]; from - (IBAction)buttonClick:(id)sender and put it into applicationDidFinishLaunching method. Problem is You are initializing your array each time you add New Book to Collection Array.
To Iterate : all the objects of Array use following snippet
[collection enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop){
Book *objBook = (Book *)obj;
NSLog(#"%#",objBook.description);
NSLog(#"%#",objBook.title);
NSLog(#"%#",objBook.author);;
}];
This Description Method is amazing! I had no idea it was even possible #PLPiper! Thank you
Although it gets harder... at this very moment I'm just logging it all out (for me as a developer). But for example... if I wanted all these properties in labels (string value). So if I wanted to spit them all out through my array, how would that work?
I've seen code that uses the following example(sort of) which I like and it's an easy-read.
for (int i = 0; (i<=[collection.count]); i++) {
[titleLabel setStringValue:[dateCollection objectAtIndex:i].title]
[authorLabel setStringValue:[dateCollection objectAtIndex:i].author]
[descriptionLabel setStringValue:[dateCollection objectAtIndex:i].description]
}
In theory this should work, but in practice I'm missing something here...

Property of two classes [duplicate]

This question already has an answer here:
How to make a property of a property
(1 answer)
Closed 9 years ago.
I want to have a result that looks like this player.type.property, An example of this is with UILabel, self.label.text. The .text being the property of the two classes.
A suggestion I have had is to do something like this:
player.type = [[MyCustomObject alloc] init];
player.type.property = #"value";
Although I'm not quite sure exactly how to go about doing this correctly, every method I have tried doesn't work.
Here is what I have tried:
Marketplace.h
#import "Item.h"
#interface Marketplace : NSObject
#property (nonatomic, assign) Item *market;
Item.h
#interface Item : NSObject
#property (nonatomic, assign) int price;
Starter.m
#import "Marketplace.h"
#import "Item.h"
#implementation MainGameDisplay
{
Marketplace *market;
Item *itemName;
}
-(void) executedMethod {
market.itemName = [[market alloc] init];
//2 errors: "Property 'itemName not found on object of type 'MarketPlace'" and "No visible #interface for 'MarketPlace' declares the selector alloc"
market.itemName.price = 5; //"Property 'itemName' not found on object of type 'Marketplace*'"
}
Each pointer to class object must be alloc init, so you need to over-write the -(id)init inside its class.
Item.h
#interface Item : NSObject
#property (nonatomic) NSInteger price;
Marketplace.h
#import "Item.h"
#interface Marketplace : NSObject
#property (nonatomic, strong) Item *item;//Item is a class, must use strong or retain
Marketplace.m
-(id)init{
if (self = [super init]) {
self.item = [[Item alloc] init];//Item must alloc together when MarcketPlace init
}
return self;
}
*Then you just init the Marketplace
#implementation MainGameDisplay
{
Marketplace *market;
Item *itemName;
}
-(void) executedMethod {
market = [Marketplace alloc] init];
//Now you can access
market.item.price = 5;
}
1 . make a Interface named PlayerType Put some property there and synthesize them.
2. now make a Interface named Player and import the PlayerType Interface there.
3. make a property of PlayerType Interface like #property(nonatomic, strong) PlayerType *type.
now made variable of Player it will allow you to access property of a property.

How do I access my array from my table data source?

I am starting my first Cocoa Project. And I have a serious (for me) but maybe easy problem (for you) to solve and I need some direction where to start.
The short description: I have built a class "PortConnection.h" who writes all ports found by an external class (AMSerial.h) into an array when the function -listPorts is called. Here is the code for the PortConnection.h
#import <Cocoa/Cocoa.h>
#import "AMSerialPortList.h"
#import "AMSerialPortAdditions.h"
#import "AMSerialPort.h"
#interface PortConnection : NSObject {
#private
AMSerialPort *port;
NSMutableArray *portArray;
}
- (void)listDevices;
#property (nonatomic, retain) NSMutableArray *portArray;
#property (nonatomic, retain) AMSerialPort *port;
#end
and following the PortConnection.m
#import "PortConnection.h"
#import "AMSerialPortList.h"
#import "AMSerialPortAdditions.h"
#import "AMSerialPort.h"
#implementation PortConnection
#synthesize portArray;
#synthesize port;
- (void)listDevices
{
// get an port enumerator
NSEnumerator *enumerator = [AMSerialPortList portEnumerator];
AMSerialPort *aPort;
while ((aPort = [enumerator nextObject]))
{
// Add Devices to Array
[portArray addObject:[aPort bsdPath]];
}
}
So far it is perfectly working.
Now my questions
I have implemented an tableView in the GUI for display the results of the method called above. This file is called "PortTableViewController.h" and is the datasource for my TableView.
Here is the .h file
#import <Foundation/Foundation.h>
#import "PortConnection.h"
#interface PortTableViewController : NSObject <NSTableViewDataSource> {
#private
IBOutlet NSTableView *portTableView;
}
#property (assign) IBOutlet NSTableView *portTableView;
#end
and here is the .m file:
#import "PortTableViewController.h"
#import "PortConnection.h"
#implementation PortTableViewController
#synthesize portTableView;
#pragma mark -
#pragma mark TableView Delegates
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView{
PortConnection *portConnection = [[PortConnection alloc] init];
[portConnection listDevices];
return [portConnection.portArray count];
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
??????? I DO NOT HAVE A CLUE HOW TO ACCESS THE ARRAY IN PORTCONNECTION.M CORRECTLY
}
#end
QUESTIONS:
1) When having a look on the TableViewDataSourceDelegates how do I access the filled portArray in the PortConnection.m class correctly. It doesn't seem to work the way I do it.
2) Do I have to create an Object from Portconnection.h every time I want to retrieve Data from it in a tableviewdelegate method?
I am really thankful for every kind of help! I want to learn something.. and I really appreciate your support! Thanks.. for question in order to help me, don't hesitate. I really appreciate it....
Thanks
Sebastian
A simple fix is to have your table view controller declare an instance variable that holds a PortConnection instance. This instance is created and sent -listDevices in -init, it is used by all methods in your table view controller (which means that all methods refer to the same PortConnection instance), and released in -dealloc.
For example:
PortTableViewController.h
#import <Foundation/Foundation.h>
#import "PortConnection.h"
#interface PortTableViewController : NSObject <NSTableViewDataSource> {
#private
IBOutlet NSTableView *portTableView;
PortConnection *portConnection;
}
#property (assign) IBOutlet NSTableView *portTableView;
#end
PortTableViewController.m
#import "PortTableViewController.h"
#import "PortConnection.h"
#implementation PortTableViewController
#synthesize portTableView;
#pragma mark -
#pragma mark TableView Delegates
- (id)init {
self = [super init];
if (self) {
portConnection = [[PortConnection alloc] init];
[portConnection listDevices];
}
return self;
}
- (void)dealloc {
[portConnection release];
[super dealloc];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView{
return [portConnection.portArray count];
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
return [portConnection.portArray objectAtIndex:row];
// or whatever behaviour provides an object value for the column/row
}
#end
If you don't want to create a field in every delegate you could create a static variable in PortConnection which holds the array. Initially the array is nil and on the first call to get the ports you create the list if needed.
In the implementation file:
static NSMutableArray *portArray;
+ (NSArray) listPorts {
if(portArray != nil)
return (NSArray *)portArray;
NSEnumerator *enumerator = [AMSerialPortList portEnumerator];
AMSerialPort *aPort;
portArray = [[NSMutableArray alloc] init];
while ((aPort = [enumerator nextObject])) {
// Add Devices to Array
[portArray addObject:[aPort bsdPath]];
}
}
This of course depends on how often the portArray will change, if it's often I would probably just generate it every time.
You could also do a getPortArray which calls generatePortArray if portArray is nil
You should only need a single PortConnection instance, but your table view controller will somehow need to know about it. It could be that the PortTableViewController creates and owns the PortConnection object, or it could be that some other object, like the app delegate or another controller creates it.
In the former case, it's trivial... the PortTableViewController creates the PortConnection instance, and therefore it has a reference to it and can access its portArray property at well.
In the latter case, things aren't much more complicated: the object that creates the PortController should give the PortTableViewController a pointer to the PortController. The PortTableViewController should then retain the PortController and stash the pointer in an instance variable so that it can access the portArray property as needed.

Objective-C NSArray

I'm new to Obj-C and iPhone SDK. The test application I'm stock with is a color switcher containing two buttons ("Back", "Forward") and one text label. The idea is to switch between rainbow colors (background) and setting an appropriate text label in a cyclic manner.
I declared NSArray (which is to contain colors names) in RainbowViewController.h, synthesized it in RainbowViewController.h and I can't add any string into that array.
This is "h" file:
#import <UIKit/UIKit.h>
#interface RainbowViewController : UIViewController {
IBOutlet UILabel *currentColorTextLabel;
NSArray *colorsArray;
NSString *msg;
}
#property (nonatomic, retain) IBOutlet UILabel *currentColorTextLabel;
#property (nonatomic, retain) NSArray *colorsArray;
#property (nonatomic, retain) NSString *msg;
- (IBAction) pressForwardButton;
- (IBAction) pressBackButton;
#end
This is "m" file:
#import "RainbowViewController.h"
#import <Foundation/Foundation.h>
#implementation RainbowViewController
#synthesize currentColorTextLabel;
#synthesize colorsArray;
#synthesize msg;
int currentArrayIndex = 0;
colorsArray = [[NSArray alloc] init]; //here i get "Initializer element is not constant" error message
[coloursArray addObject:#"Red"]; //here I get "Expected identifier or '(' before '[' token"
[coloursArray addObject:#"Orange"];
//etc
- (IBAction) pressForwardButton {
//here I'm going to increment currentArrayIndex, set an appropriate color, and update a currentColorTextLabel based on currentArrayIndex.
}
- (IBAction) pressBackButton {
}
//auto-genereted code here
#end
I'm new to obj-c as well, but I think you need to initialize the array with objects, or use an NSMutableArray if you want to add objects after it is created.
You have the code that should go in your init method just sitting out in the middle of the file. You can't set instance variables like that.
jasongetsdown is correct. You need to instantiate the NSArray object with the objects it will contain and nil terminated.
#"Red", #"Blue", nil
If you wish to have an array that you can change you need to make it a Mutable Array.
However, you have another problem here. Your property that you are synthesizing and allocating for is an object named colorsArray and you are trying to pass a method to a coloursArray object, two different spellings.