How to get data from property list from Documents? - objective-c

I'm new to the Objective-C
I want to read a file.plist from path Documents:
/4BC1E3AF-15AD-4F33-A55D-E52C60F13DE8/data/Containers/Data/Application/95BD9BE3-5A5A-4EAC-A9FE-133EF64D1D1A/Documents"
Can you help me replace this line of code?:
NSDictionary *dictRoot = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle]pathForResource:#"TestPlist" ofType:#"plist"]];
Full code:
here
#import "ViewController.h"
#interface ViewController () {
NSMutableArray *subjectList;
NSMutableArray *contentList;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
subjectList = [[NSMutableArray alloc]init];
contentList = [[NSMutableArray alloc]init];
NSDictionary *dictRoot = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle]pathForResource:#"TestPlist" ofType:#"plist"]];
NSArray *arrayList = [NSArray arrayWithArray:[dictRoot objectForKey:#"Data"]];
[arrayList enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop) {
[subjectList addObject:[obj valueForKey:#"Title"]];
[contentList addObject:[obj valueForKey:#"Content"]];
}];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [subjectList count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *simpleTableIdentifier = #"SimpleTableItem";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if(cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:simpleTableIdentifier];
}
cell.textLabel.text = [subjectList objectAtIndex:indexPath.row];
cell.detailTextLabel.text = [contentList objectAtIndex:indexPath.row];
return cell;
}
#end

You get the URL of the Documents folder in the container with
NSURL *documentsFolderURL = [[NSFileManager defaultManager]
URLForDirectory: NSDocumentDirectory
inDomain: NSUserDomainMask
appropriateForURL: nil
create: false
error: nil];
NSDictionary *dictRoot = [NSDictionary dictionaryWithContentsOfURL:[documentsFolderURL URLByAppendingPathComponent:#"TestPlist.plist"] error: nil];
There is also dictionaryWithContentsOfURL although I recommend to prefer NSPropertyListSerializtion

Related

Objective-C use of undeclared identifier 'numberOfSectionsInTableView'

Not sure where I went wrong. My understanding is that importing UIKit will enable use of the numberOfSectionsInTableView method... Any help is appreciated.
//HEADER
#import <UIKit/UIKit.h>
#interface MainScrollingTableVC : UITableViewController <UITableViewDelegate, UITableViewDataSource> {
int selectedIndex;
NSMutableArray *titleArray;
NSArray *subtitleArray;
NSArray *textArray;
}
#end
//IMPLEMENTATION
#import "MainScrollingTableVC.h"
#import "MainExpandingCellTableViewCell.h"
#interface MainScrollingTableVC ()
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#end
#implementation MainScrollingTableVC
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.delegate = self;
self.tableView.dataSource = self;
//index = -1 means no cell is expanded or should expand
selectedIndex = -1;
titleArray = [[NSMutableArray alloc] init];
NSString *string;
//create consecutive array of 8 numbers. 1...2...etc...
for(int ii = 1; ii <= 8; ii++) {
string = [[NSString alloc] initWithFormat:#"Row %i", ii];
[titleArray addObject:string];
}
subtitleArray = [[NSArray alloc] initWithObjects:#"First row", #"Second row", #"Third row", #"Fourth row", #"Fifth row", #"Sixth row", #"Seventh row", #"Eigth row", nil];
textArray = [[NSArray alloc] initWithObjects:#"Apple", #"Orange", #"Grape", #"Banana", #"Kiwi", #"Blueberry", #"Apricot", #"Lemon", nil];
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return titleArray.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"MainExpandingCell";
MainExpandingCell *cell = (MainExpandingCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"MainExpandingCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
if(selectedIndex == indexPath.row) {
//Do expanded cell stuff
} else {
//Do closed cell stuff
}
cell.titleLabel.text = [titleArray objectAtIndex:indexPath.row];
cell.timeLabel.text = [subtitleArray objectAtIndex: indexPath.row]; //CHECK VARS
cell.textLabel.text = [textArray objectAtIndex:indexPath.row];
int etaLabel = (indexPath.row + 1) * 25;
cell.etaLabel.text = [NSString stringWithFormat:#"%i", eta]
cell.clipsToBounds = YES;
return cell;
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
You are declaring your methods inside the viewDidLoad method. Objective-C doesn't support method nesting. Fix your whole code with its formatting and add the missing } accordingly.

Objective C Dynamic NSDictionary for UI Table View divided by Section Headers

So this is based off of my question here
objective c when to use NSDictionary instead of NSArray
Apologies for the noob questions. I just started learning objective c and I'm really confused.
- (void)viewDidLoad
{
[super viewDidLoad];
headers = [NSArray arrayWithObjects:#"Header Section 1 (H1)", #"Header Section 2 (H2)", nil];
events = [NSArray arrayWithObjects:#"H1 Content 1", #"H1 Content 2", nil];
myInfo = [NSArray arrayWithObjects:#"H2 Content 1", #"H2 Content 2", #"H2 Content 3", nil];
menuDetails = [[NSMutableDictionary alloc] init];
for(NSString *event in events){
[menuDetails setValue:event forKey:#"Header Section 1 (H1)"];
}
for(NSString *info in myInfo){
[menuDetails setValue:info forKey:#"Header Section 1 (H2)"];
}
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return [[menuDetails allKeys] count];
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
return [[[menuDetails allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)]objectAtIndex:section];
}
Wanted to limit this for the First Section but not on the 2nd section
-(NSInteger)tableView:(UITableView *)tableview numberOfRowsInSection:(NSInteger)section {
return 5;
}
So I'm not really sure how to make this dynamic when I try to fill up the cells with this method. I'm not really sure how to make sure it selects the "Key" of the section the specific cell is at (blocked line) :
-(UITableViewCell *)tableView:(UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath *) indexPath {
static NSString *CellIdentifier = #"OptionCellIdentifier";
MenuOptionTableCell *cell = (MenuOptionTableCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil){
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"OptionCellIdentifier" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
NSDictionary *menuItem = [[menuDetails valueForKey:[[[menuDetails allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];
cell.optionName.text = [menuItem objectForKey:#"Events"];
return cell;
}
EDIT: GAAAHHH, the formatting is killing me. It won't get inside the code markup
This is where Idk what to do:
cell.optionName.text = [menuItem objectForKey:#"Events"];
So what I wanted to happen is:
Section Header 1
H1 Content 1
H1 Content 2
Section Header 1
H2 Content 1
H2 Content 2
H1 Content 3
I would do this by first creating a "MyMenuSection" object as follows;
#interface MyMenuSection : NSObject {
NSString *title;
NSMutableArray *rows;
}
#property (nonatomic, retain) NSString *title;
#property (nonatomic, retain) NSMutableArray *rows;
#end
#implementation
#synthesize title;
#synthesize rows;
#dealloc {
[title release];
[rows release];
[super dealloc];
}
#end
Then in your viewDidLoad (or wherever you decide to inialize/assign the data) you would do this;
-(void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray menuDetails = [NSMutableArray array];
MyMenuSection section = [[MyMenuSection alloc] init];
section.title = #"Header Section 1 (H1)";
[section.rows addObjects:#"H1 Content 1", #"H1 Content 2", nil];
[menuDetails addObject:section];
[section release];
section = [[MyMenuSection alloc] init];
section.title = #"Header Section 2 (H2)";
[section.rows addObjects:#"H2 Content 1", #"H2 Content 2", #"H2 Content 3", nil];
[menuDetails addObject:section];
[section release];
self.menuDetails = menuDetails;
[menuDetails release];
}
Then your UITableView data and delegate callbacks would look like this;
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [menuDetails count];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (section == 0) {
return 2;
} else {
return [[(MyMenuSection *)[menuDetails objectAtIndex:indexPath.section] rows] count];
}
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [(MyMenuSection *)[menuDetails objectAtIndex:section] title];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *) indexPath {
static NSString *cellIdentifier = #"OptionCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell == nil){
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"OptionCellIdentifier" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.optionName.text = (NSString *)[[(MyMenuSection *)[menuDetails objectAtIndex:indexPath.section] rows] objectAtIndex:[indexPath.row]];
return cell;
}
YMMV, Happy Coding
Sounds like you're over thinking things here. My biggest recommendation here would be to get rid of all your separate arrays and start nesting them within each other. By that I mean make menuItems a NSArray of your arrays events and myInfo.
This will make it easier for you later on to get your dynamic row count using .count.
example:
events = [NSArray arrayWithObjects:#"H1 Content 1", #"H1 Content 2", nil];
myInfo = [NSArray arrayWithObjects:#"H2 Content 1", #"H2 Content 2", #"H2 Content 3", nil];
NSArray *menuItems = [NSArray arrayWithObjects:events, myInfo, nil];
Now when you call the array in
-(NSInteger)tableView:(UITableView *)tableview numberOfRowsInSection:(NSInteger)section{
NSArray *tempArray = [NSArray objectAtIndex: section];
return tempArray.count;
}
As for pulling the cell names later on.
cell.optionName.text = [[menuItem objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];

Problems Populating TableView with NSMutableArray

I'm using SudzC for an iPhone app. I'm succesfully calling my ASP.Net webservice and pulling the needed data into an NSMutable array called tableData. I have a tableView that should display the contents of tableData; however this is not working. I've looked for hours at this problem. I'm very new to the Objective C world so I'm thinking it's a small oversight on my part. I've linked my TableView to the ViewController (delegate/datasource) as well.
Here's is the code I have:
#import "ViewController.h"
#import "DocumentServiceJSONService.h"
#interface ViewController ()
#end
#implementation ViewController
NSMutableArray *tableData;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
-(void)viewWillAppear:(BOOL) animated {
tableData = [[NSMutableArray alloc] init];
DocumentServiceJSONService* service = [[DocumentServiceJSONService alloc] init];
[service GetAllEMSDocumentsXMLAsString: self action:#selector(handleGetAllEMSDocuments:)];
[super viewWillAppear:animated];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void) handleGetAllEMSDocuments:(id) result {
if ([result isKindOfClass:[NSError class]]) {
//if an error has occurred, handle it
return;
}
if([result isKindOfClass: [SoapFault class]]) {
return;
}
NSString* xmlString = result;
CXMLDocument *xmlDoc = [[CXMLDocument alloc] initWithXMLString:xmlString options:0 error:nil];
NSArray *nodes = NULL;
nodes = [xmlDoc nodesForXPath:#"//EMSDocuments" error:nil];
for (CXMLElement *node in nodes) {
NSMutableDictionary *item = [[NSMutableDictionary alloc] init];
int counter;
for (counter = 0; counter < [node childCount]; counter++) {
[item setObject:[[node childAtIndex:counter] stringValue] forKey:[[node childAtIndex:counter] name]];
}
//[item setObject:[[node attributeForName:#"DisplayName"] stringValue] forKey:#"DisplayName"];
NSString *displayName = [item objectForKey:#"DisplayName"];
[tableData addObject:displayName];
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [tableData count];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell.textLabel.text = [tableData objectAtIndex:indexPath.row];
return cell;
}
#end
Any advice is appreciated.
Make sure you have in your .h the delegate and datasource correctly set:
#interface YourViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
And if you are using XIBs for the user interface, that the tableview is connected to the File's Owner as datasource and delegate.
--
EDIT:
Creating a Outlet for you tableview:

NSMutableDictionary and NSArray

I am splitting a String:
#"Sam|26|Developer,Hannah|22|Team Leader,Max|1|Dog"
and using NSMutableDictionary to display in a TableViewCell with 3 labels. Code:
- (void)viewDidLoad {
[super viewDidLoad];
NSString *test = #"Sam Parrish|26|Developer,Hannah Rajamets|22|Team Leader,Max Parrish|1|Dog";
testArray = [[NSArray alloc] init];
testArray = [test componentsSeparatedByString:#","];
dict = [NSMutableDictionary dictionary];
for (NSString *s in testArray) {
testArrayNew = [s componentsSeparatedByString:#"|"];
[dict setObject:[testArrayNew objectAtIndex:1] forKey:[testArrayNew objectAtIndex:0]];
[dict setObject:[testArrayNew objectAtIndex:2] forKey:[testArrayNew objectAtIndex:1]];
NSLog(#"Dictionary: %#", [dict description]);
}
[dict retain];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[dict allKeys] count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CustomCell";
untitled *cell = (untitled *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"untitled" owner:nil options:nil];
for (id currentObject in topLevelObjects) {
if ([currentObject isKindOfClass:[UITableViewCell class]]) {
cell = (untitled *) currentObject;
break;
}
}
}
// Configure the cell.
cell.nameLabel.text = [[dict allKeys] objectAtIndex:[indexPath row]];
cell.ageLabel.text = [dict objectForKey:cell.nameLabel.text];
cell.jobLabel.text = [dict objectForKey:cell.ageLabel.text];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
this is displaying 6 TableViewCells when I run, 3 of which are perfect, the other 3 are an assortment of the data. i realise this is to do with the setObject: forKey: but can't seem to find the solution for this to work correctly.
any help much appreciated..
sam
I would store the data as an NSArray holding NSArrays....
- (void)viewDidLoad {
[super viewDidLoad];
NSString *test = #"Sam Parrish|26|Developer,Hannah Rajamets|22|Team Leader,Max Parrish|1|Dog";
data = [[NSMutableArray alloc] init];
for (NSString *s in testArray) {
[data addObject:[s componentsSeparatedByString:#"|"]];
}
}
Then in
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
use
// Configure the cell.
NSArray *cellData = [data objectAtIndex:indexPath.row];
cell.nameLabel.text = [cellData objectAtIndex:0];
cell.ageLabel.text = [cellData objectAtIndex:1];
cell.jobLabel.text = [cellData objectAtIndex:2];
If I understand you correctly, you're saying that the first three cells are correct and the other three shouldn't be shown at all? If that's the case, the problem is this:
return [[dict allKeys] count];
Shouldn't it be...
return [testArray count];
...in which case, you need to retain testArray.
In addition, you have a memory leak. You create an instance of NSArray, then you immediately leak it by assigning the return value of componentsSeparatedByString: to the same variable. Have you run the static analyser over your code?

UIImage & UITableViewCell dimensions

I am loading into my UITableViewCells different sized images dynamically via URLs. I want to be able to resize the Cell depending on the size of the image that's downloaded. Can I extract the dimensions then change the heightForRowAtIndexPath for each cell?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSData *receivedData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[arrayOfImages objectAtIndex:indexPath.row]]
options:NSUncachedRead
error: &error];
UIImage *image = [[UIImage alloc] initWithData:receivedData];
imgView.image = image;
[cell.contentView addSubview:imgView];
....
The short answer: once you have the image, then image.size.height should give you what you want. However, heightForRowAtIndexPath: gets called before cellForRowAtIndexPath:. So you will probably want to implement some type of async image loader. Have a look at the Apple sample LazyTableImages for a full implementation of that.
The long answer: I made a few shortcuts to answer your question without writing 1000 lines of code, but this should demonstrate the idea. The important bits are cellForRowAtIndexPath: and heightForRowAtIndexPath:.
RootViewController.m
#import "RootViewController.h"
#interface RootViewController()
- (void)loadImagesForOnscreenRows;
- (void)loadImageForIndexPath:(NSIndexPath *)indexPath;
#end
#implementation RootViewController
static NSMutableDictionary *images;
static NSDictionary *pathAndName;
#pragma mark -
#pragma mark NSObject
- (void)dealloc {
[images release];
[pathAndName release];
[super dealloc];
}
#pragma mark UIViewController
- (void)viewDidLoad {
// image paths from WikiMedia
pathAndName = [[NSDictionary alloc] initWithObjectsAndKeys:
#"http://upload.wikimedia.org/wikipedia/commons/8/89/Crystal_Clear_app_virus_detected_2.png", #"Big Virus",
#"http://upload.wikimedia.org/wikipedia/commons/thumb/c/c7/Crystal_Clear_app_virus_detected.png/64px-Crystal_Clear_app_virus_detected.png", #"Little Virus",
nil];
images = [[NSMutableDictionary alloc] initWithCapacity:[pathAndName allKeys].count];
}
- (void)viewDidUnload {
[images release];
images = nil;
[pathAndName release];
pathAndName = nil;
}
#pragma mark UIScrollViewDelegate
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (!decelerate) {
[self loadImagesForOnscreenRows];
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[self loadImagesForOnscreenRows];
}
#pragma mark UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [pathAndName allKeys].count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// typical cell creation
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// if the image is downloaded, then set the cell image.
NSString *key = [[pathAndName allKeys] objectAtIndex:indexPath.row];
UIImage *image = [images objectForKey:key];
if (image) {
[cell.imageView setImage:image];
}
[cell.textLabel setText:key];
return cell;
}
#pragma mark UITableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *key = [[pathAndName allKeys] objectAtIndex:indexPath.row];
UIImage *image = [images objectForKey:key];
if (image) {
return image.size.height;
}
// if the image is not yet downloaded, kick off downloading it and return a default row height.
[self performSelector:#selector(loadImageForIndexPath:) withObject:indexPath afterDelay:0.1f];
return tableView.rowHeight;
}
#pragma mark -
#pragma mark RootViewController
#pragma mark Private Extension
- (void)loadImagesForOnscreenRows {
NSArray *visiblePaths = [self.tableView indexPathsForVisibleRows];
for (NSIndexPath *indexPath in visiblePaths) {
[self loadImageForIndexPath:indexPath];
}
}
// This is a big shortcut from the Apple sample, LazyTableImages.
- (void)loadImageForIndexPath:(NSIndexPath *)indexPath {
NSString *key = [[pathAndName allKeys] objectAtIndex:indexPath.row];
if ([images objectForKey:key]) return;
NSString *path = [pathAndName objectForKey:key];
NSError *error = nil;
NSData *receivedData = [NSData dataWithContentsOfURL:[NSURL URLWithString:path]
options:NSUncachedRead
error: &error];
if (error) return;
UIImage *image = [[UIImage alloc] initWithData:receivedData];
[images setObject:image forKey:key];
[image release];
NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
[self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
}
#end