I have an application that has the following parts:
StoreDataController.h
StoreDataController.m
StoreTableViewController.h
StoreTableViewController.m
I created a property & method in StoreDataController that retrieves data from a URL and converts it to JSON. I then store it in an Array. I'm trying to get the table controller to display the array in the table but it isn't displaying. What do I need to do to have the Table display the contents of the array? Here is the code I have:
StoreDataController.h
#interface StoreDataController : NSObject
#property (nonatomic, retain) NSArray *storeNames;
-(void)addStoreNamesObject:(NSArray *)storeNames;
#end
StoreDataController.m
#import "StoreDataController.h"
#import "SBJson.h"
#implementation StoreDataController
-(void)addStoreNamesObject:(NSArray *)storeNames
{
NSString *strURL = [NSString stringWithFormat:#"http://10.247.245.87/stores/dodge.php"];
NSData *dataURL = [NSData dataWithContentsOfURL:[NSURL URLWithString:strURL]];
NSString *strResult = [[NSString alloc] initWithData:dataURL encoding:NSUTF8StringEncoding];
storeNames = [strResult JSONValue];
}
#end
StoreTableViewController.h
#import <UIKit/UIKit.h>
#class StoreDataController;
#interface StoreTableViewController : UITableViewController
#property (nonatomic, retain) StoreDataController *storeNameController;
#end
StoreTableViewController.m
#import "StoreTableViewController.h"
#import "StoreDataController.h"
#interface StoreTableViewController ()
#end
#implementation StoreTableViewController
- (void)awakeFromNib
{
[super awakeFromNib];
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _storeNameController.storeNames.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.textLabel.text = [_storeNameController.storeNames objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
[self.navigationController pushViewController:detailViewController animated:YES];
*/
}
#end
You at least need something to set the storeNameController property inside your StoreTableViewController so that it references the StoreDataController object that is doing the work of building the array.
How you do that depends on what creates the StoreDataController object and how that's related to your view controller.
(Aside: In general, you should use self.propertyName instead of _propertyName.)
Related
cannot delete the rows, am getting terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray removeObjectAtIndex:]: mutating method sent to immutable object
i have the following:
#import <UIKit/UIKit.h>
#class CSDetailViewController;
#interface CSMasterViewController : UITableViewController
#property (strong, nonatomic) CSDetailViewController *detailViewController;
#property (strong, nonatomic) NSMutableArray *kryeministrat;
#end
and the following at implementation:
#import "CSMasterViewController.h"
#import "CSDetailViewController.h"
#implementation CSMasterViewController
- (void)awakeFromNib
{
self.clearsSelectionOnViewWillAppear = NO;
self.preferredContentSize = CGSizeMake(320.0, 600.0);
[super awakeFromNib];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem;
NSString *path = [[NSBundle mainBundle]pathForResource:#"KryeministratList" ofType:#"plist"];
NSDictionary *kryeministratInfo = [NSDictionary dictionaryWithContentsOfFile:path];
self.kryeministrat = [kryeministratInfo objectForKey:#"kryeministrat"];
self.detailViewController = (CSDetailViewController *) [[self.splitViewController.viewControllers lastObject] topViewController];
}
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.kryeministrat count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
NSDictionary *kryeministrat = self.kryeministrat[indexPath.row];
cell.textLabel.text = kryeministrat[#"name"];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *kryeministrat = self.kryeministrat[indexPath.row];
NSString *urlString = kryeministrat[#"url"];
self.detailViewController.detailItem = urlString;
}
- (void)tableView:(UITableView *)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.kryeministrat removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
}
#end
from your exception, you should look into this code:
NSDictionary *kryeministratInfo = [NSDictionary dictionaryWithContentsOfFile:path];
self.kryeministrat = [kryeministratInfo objectForKey:#"kryeministrat"];
Although property kryeministrat is declared as NSMutableArray , kryeministratInfo is NSDictionary. It means the values in the kryeministratInfo are all immutable.
SO you can do like:
NSDictionary *kryeministratInfo = [NSDictionary dictionaryWithContentsOfFile:path];
self.kryeministrat = [NSMutableArray arrayWithArray:[kryeministratInfo objectForKey:#"kryeministrat"]];
I have a storyboard with tabbed browsing. One of the tabs is a table view. The table is populated by a list of animal "titles". when the title is pressed, a detailed view is opened that displays the title in addition to a sound and the number of times that the animal has been clicked. I have a view controller set up. I also have item.h/m and itemstore.h/m. there is also a detailedviewcontroller. my current problem is that in the item store i have two arays set up but right off the bat xcode is telling me that method definitions aren't found. its also giving my undeclared identifier errors.
FSAnimalsViewController.h (this is my table view controller)
#import <AVFoundation/AVAudioPlayer.h>
#import <UIKit/UIKit.h>
#import "FSDetailViewController.h"
#interface FSAnimalsViewController : UITableViewController
{
}
#end
FSAnimalsViewController.m
#import "FSAnimalsViewController.h"
#import "FSItemStore.h"
#import "FSItem.h"
#implementation FSAnimalsViewController
- (id)init
{
// Call the superclass's designated initializer
self = [super initWithStyle:UITableViewStyleGrouped];
if (self)
{
UINavigationItem *n = [self navigationItem];
[n setTitle:#"FoxSays"];
// Create a new bar button item that will send
// addNewItem: to ItemsViewController
[[self navigationItem] setLeftBarButtonItem:[self editButtonItem]];
}
return self;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[self tableView] reloadData];
}
- (id)initWithStyle:(UITableViewStyle)style
{
return [self init];
}
- (void)tableView:(UITableView *)aTableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
FSDetailViewController *detailViewController = [[FSDetailViewController alloc] init];
NSArray *items = [[FSItemStore defaultStore] allItems];
FSItem *selectedItem = [items objectAtIndex:[indexPath row]];
// Give detail view controller a pointer to the item object in row
[detailViewController setItem:selectedItem];
// Push it onto the top of the navigation controller's stack
[[self navigationController] pushViewController:detailViewController
animated:YES];
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return [[[FSItemStore defaultStore] allItems] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Set the cell identifier
static NSString *CellIdentifier = #"BasicCell";
// Reuse the cell from the identifier
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell if it doesn't exist
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Log the row for debugging
NSLog(#"%d", [indexPath row]);
// Get object from store
FSItem *item = [[[FSItemStore defaultStore] allItems] objectAtIndex:[indexPath row]];
// Set label to from property in object
[[cell textLabel] setText:[item title]];
return cell;
}
#end
FSItem.h
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVAudioPlayer.h>
#interface FSItem : NSObject
#property (nonatomic, copy) NSString *title;
#property (nonatomic) SystemSoundID *sound;
#property (nonatomic) int plays;
- (NSArray *)animals;
- (NSArray *)sounds;
#end
FSItem.m
#import <AudioToolbox/AudioToolbox.h>
#import "FSItem.h"
#implementation FSItem
NSString *title;
SystemSoundID *sound;
int plays;
- (NSArray *)animals
{
NSArray *animals = [NSArray arrayWithObjects:#"Dog",#"Cat",#"Bird",#"Mouse",#"Cow",#"Frog",#"Elephant",#"Duck",#"Fish",#"Seal",#"Fox", nil];
return animals;
}
- (NSArray *)sounds
{
NSArray *sounds = [NSArray arrayWithObjects:
#"Woof.mp3",
#"Meow.mp3",
#"tweet.mp3",
#"Squeak.mp3",
#"Moo.mp3",
#"Croak.mp3",
#"Toot.mp3",
#"Quack.mp3",
#"Blub.mp3",
#"OWOwOw.mp3",
#"Fox.mp3",
nil];
return sounds;
}
#end
FSItemStore.h
#import <AVFoundation/AVAudioPlayer.h>
#import <AudioToolbox/AudioToolbox.h>
#import <Foundation/Foundation.h>
#class FSItem;
#interface FSItemStore : NSObject
{
NSMutableArray *allItems;
}
#property (nonatomic) int i;
+ (FSItemStore *)defaultStore;
- (NSArray *)allItems;
- (NSArray *)animals;
- (NSArray *)sounds;
- (FSItem *)createItem;
#end
FSItemStore.m
#import <AudioToolbox/AudioToolbox.h>
#import "FSItem.h"
#import "FSItemStore.h"
#implementation FSItemStore
int i = 0;
- (NSArray *)allItems
{
return allItems;
}
+ (FSItemStore *)defaultStore;
{
static FSItemStore *defaultStore = nil;
if(!defaultStore)
defaultStore = [[super allocWithZone:nil] init];
return defaultStore;
}
- (FSItem *)createItem
{
FSItem *item = [[FSItem alloc] init];
if (i < [animals count])
{
[item setTitle: [animals objectAtIndex: i]];
[item setSound: [sounds objectAtIndex: i]];
[item setPlays: 0];
i++;
[allItems addObject: item];
}
return item;
}
#end
FSItemStore is where my problems seem to be. Its saying that method definition for sounds and animals isn't found and both sounds and animals are undeclared identifiers. Anyone got any ideas?
Your problem is that in your H file you are declaring that your class will implement a method called animals that will return an NSArray, and a method call sounds that will return another NSArray, but in your M file you are not implementing these methods.
In your FSItemStore.m you should implement these methods:
- (NSArray *)animals{
//Do whatever this method is supposed to do, return an NSArray.
}
- (NSArray *)sounds
{
//Do whatever this method is supposed to do, return an NSArray.
}
EDIT
If what you pretend is that FSItemStore inherits the methods from FSItem, you have to declare the interface that way:
#interface FSItemStore : FSItem //FSItem instead of NSObject
{
NSMutableArray *allItems;
}
If I understood your code correctly you want to set one of the animals and sounds initialized in FSItem::sounds and FSItem::animals to the new item that you create in FSItemStore::createItem. So the animals and sounds methods should be executed ont eh correct object - the FSItem object. Change your code in FSItemStore::createItem to this -
- (FSItem *)createItem
{
FSItem *item = [[FSItem alloc] init];
if (i < [animals count])
{
[item setTitle: [[item animals] objectAtIndex: i]];
[item setSound: [[item sounds] objectAtIndex: i]];
[item setPlays: 0];
i++;
[allItems addObject: item];
}
return item;
}
This is still a bad way of doing what you want, as the NSArray will be initialized everytime you create an item. If the number of sounds and animals is fixed, better define them so that they just get initialized once e.g. static objects in FSItem or property on FSItemStore
You haven't defined any properties or variables that are called animals or items. You have only defined the getter methods.
e.g.
#property(nonatomic,strong) NSArray *animals;
#property(nonatomic,strong) NSArray *items;
Then the implementation of your getters, would return these properties.
I'm trying to parse a JSON file containing some url's to pictures, titles and texts. I've tried to parse another JSON file in the same way, but only with text and it works. But this one will not work. Here's my code:
.h:
#import <UIKit/UIKit.h>
#interface PicturesViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>{
NSURLConnection *conn;
NSMutableData *responseData;
NSMutableArray *news;
NSIndexPath * indexPath;
UIActivityIndicatorView *loading;
}
#property (nonatomic, retain) NSIndexPath * indexPath;
#property (nonatomic, strong) NSArray *tweets;
#property (strong, nonatomic) IBOutlet UITableView *myTableView;
#end
.m:
#import "PicturesViewController.h"
#import "AppDelegate.h"
#import "RNBlurModalView.h"
#import "PictureJSON.h"
#interface PicturesViewController ()
{
NSInteger refreshIndex;
NSArray *images;
}
#end
#implementation PicturesViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Menu" style:UIBarButtonItemStyleBordered target:self action:#selector(showMenu)];
UIPanGestureRecognizer *gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(swipeHandler:)];
[self.view addGestureRecognizer:gestureRecognizer];
[self issueLoadRequest];
}
- (void)swipeHandler:(UIPanGestureRecognizer *)sender
{
[[self sideMenu] showFromPanGesture:sender];
}
#pragma mark -
#pragma mark Button actions
- (void)showMenu
{
[[self sideMenu] show];
}
#pragma mark - Table view data source
- (void)issueLoadRequest
{
// Dispatch this block asynchronosly. The block gets JSON data from the specified URL and performs the proper selector when done.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData* data = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://my-site/pictureparse.php?name=MyName"]];
[self performSelectorOnMainThread:#selector(receiveData:) withObject:data waitUntilDone:YES];
});
}
- (void)receiveData:(NSData *)data {
// When we have the data, we serialize it into native cocoa objects. (The outermost element from twitter is
self.tweets = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
[self.myTableView reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.tweets.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"PictureJSON";
PictureJSON *cell = (PictureJSON *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"PictureJSON" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
NSDictionary *tweet = [self.tweets objectAtIndex:indexPath.row];
cell.instaImage = [tweet objectForKey:#"link"];
cell.titleLabel.text = [tweet objectForKey:#"title"];
cell.timeLabel.text = [tweet objectForKey:#"published"];
return cell;
}
#end
I have a custom table view file named PictureJSON with a nib file which looks like this:
But when I launch my app I get this error:
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<PicturesViewController 0x17d86dc0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key instaImage.'
Can someone help me fix this?
Thanks!
Probably the JSON you got not have good structure, check the result of the query in JSONLint - The JSON Validator perhaps this can help you to detect the error.
I'm new to iOS and just trying to get data to display in UITableView. I basically have a project set up about authors. I want to display author names etc. So i have an Author model and AuthorsViewController which is a datasource and delegate for UITableView etc. I'm using storyboard (MainStoryboard) tableview and managed to connect it to AuthorsViewController in the "Identity Inspector".
Image of Storyboard if it helps, thanks:
Here, first, is the model: Author.h
#import <Foundation/Foundation.h>
#interface Author : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *book;
#property (nonatomic) int year;
#end
Author.m
#import "Author.h"
#implementation Author
#end
Here is the AuthorsViewController.h
#interface AuthorViewController : UITableViewController
<UITableViewDataSource, UITableViewDelegate>
#end
And AuthorsViewController.m
#import "AuthorViewController.h"
#interface AuthorViewController ()
#property (nonatomic, strong) NSMutableArray *authors;
#end
#implementation AuthorViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_authors = [[NSMutableArray alloc] init];
Author *auth = [[Author alloc] init];
[auth setName:#"David Powers"];
[auth setBook:#"PHP Solutions"];
[auth setYear:2010];
[_authors addObject:auth];
auth = [[Author alloc] init];
[auth setName:#"Lisa Snyder"];
[auth setBook:#"PHP security"];
[auth setYear:2011];
[_authors addObject:auth];
auth = [[Author alloc] init];
[auth setName:#"Rachel Andrew"];
[auth setBook:#"CSS3 Tips, Tricks and Hacks"];
[auth setYear:2012];
[_authors addObject: auth];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_authors count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"AuthorCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell != nil)
{
cell = [[UITableViewCell alloc]
initWithStyle: UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
Author *currentAuthor = [_authors objectAtIndex:[indexPath row]];
[[cell textLabel] setText: [currentAuthor name]];
NSLog(#"%#", [currentAuthor name]);
return cell;
}
#end
Since you made the table view with its cell in the storyboard, you don't need the if (cell == nil) clause at all. You also need to call [self.tableView reloadData] as the last line in viewDidLoad.
I think I'm not getting the handle on address book and UITableView, or maybe I'm just making it simpler than it is.
here is my .h file:
#interface Contacts : UITableViewController <ABPeoplePickerNavigationControllerDelegate> {
NSMutableArray *menuArray;
NSString *firstName;
NSString *lastName;
UIButton *addcontact;
}
#property (nonatomic, retain) NSMutableArray *menuArray;
#property (nonatomic, strong) UIButton *addcontact;
#property (nonatomic, strong) NSString *firstName;
#property (nonatomic, strong) NSString *lastName;
-(void)showPeoplePickerController;
#end
And my .m file:
-(IBAction)addcontact:(id)sender {
ABPeoplePickerNavigationController *peoplePicker=[[ABPeoplePickerNavigationController alloc] init];
[peoplePicker setPeoplePickerDelegate:self];
[self presentModalViewController:peoplePicker animated:YES]; }
- (void)viewDidLoad
{
menuArray = [[NSMutableArray alloc] init];
[super viewDidLoad];
}
- (BOOL)peoplePickerNavigationController:
(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person {
firstName = (__bridge NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
[menuArray addObject:firstName];
lastName = (__bridge NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
[menuArray addObject:lastName];
[self dismissModalViewControllerAnimated:YES];
return NO;}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [menuArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSString *cellValue = [menuArray objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}
I hope someone could help me with it, I'm fairley new to UITableViews and Addressbooks :)
For performance purposes (not to mention animation purposes), UITableView does not automatically update itself whenever the data source changes.
Try adding [myTableView reloadData]; after you add the first and last name to the arrays (or in viewWillAppear: