I am trying to create a generic UITableView in my iPhone app.
I have a UITableView which populates the data using an array via a SELECT query loop.
I add the data into my array and populate the array in cellForRowAtIndexPath:.
I get the section header using that array and by using a sort method, I put the section headers in Array1.
I would like to have titleForHeaderInSection: work by having section 0 be a static header name and sections 1 and later become generic, meaning the header name will come from Array1.
I am not sure how can I create that logic since the app always throws EXC_BAD_ACCESS with the code below.
My logic: I keep the count of the array in an int and see if the value is greater than 0. If it is, I add the section header for and objectAtIndex:0, otherwise I use the static one. But when the count gets to 2, for section 2 and objectAtIndex:1, it breaks and throws EXC_BAD_ACCESS.
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
int value = [[self Array1] count];
if(section == 0)
return #"Countries";
if (value > 0) {
if (section == value){
return [[self Array1] objectAtIndex:section - 1];
}
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
int count = [[self Array1] count];
return count + 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
int value = [[self Array1] count];
if (section == 0) {
return [self.Array count];
}
if (value > 0) {
if (section == [[self Array1] count]) {
NSString *initialLetter = [[self Array1] objectAtIndex:section - 1];
// get the array of elements that begin with that letter
NSArray *elementsWithInitialLetter = [self elementsWithInitialLetter:initialLetter];
// return the count
return [elementsWithInitialLetter count];
}
}
}
Looks like you're just missing a retain on the iVar backing the Array1 method. declare the array as a property:
#property (nonatomic, retain) NSArray* datastore;
Then cache the value you're referring to in the Array1 method in this method (probably in viewDidLoad).
self.datastore = [self Array1];
Then replace all remaining references to [self Array1] with self.datastore. Build run and see if it still crashes. (Don't forget to set self.datastore = nil in your dealloc!
Related
In my table view, I have 7 section. Each section count is 3 initially.
When I launch my app, numberOfRowsInSection method is calling from last section count(6) but data are displayed in all sections properly.
When I reload my table after insert event numberOfRowsInSection is calling again from 6 not from 0 so how to resolve it?
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSMutableArray *arrayOfItems = [workScheduleArray objectAtIndex:section];
return arrayOfItems.count;
}
-(void)insertNewDutyTimeRow:(UIButton *)addNewRowBtn atIndexPath:(NSIndexPath *)addNewDutyIndex
{
insertNewRow = [[NSMutableArray alloc]init];
[insertNewRow addObject:#"Start Time"];
[insertNewRow addObject:#"End time"];
[insertNewRow addObject:#"Service type"];
newArray = [workScheduleArray objectAtIndex:addNewDutyIndex.section];
[newArray insertObject:insertNewRow atIndex:addNewDutyIndex.row+1];
NSLog(#"WorkSchedule array:%#",workScheduleArray[addNewDutyIndex.section]);
[self.personalTable reloadData];
}
Your numberOfRowsInSection is generic, it returns the same number for all your sections. it begins by section 0 and ends by your last section (in your case its 6). So this is why you must specify all the cases. For example you can do this:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
switch (section) {
case 0:
NSMutableArray *arrayOfItems = [workScheduleArray objectAtIndex:section];
return arrayOfItems.count;
{...}
}
}
I have 6 different NSMutable arrays [(Arr(A); Arr(B); etc.] each of which represents data for one cell of a row in the table. The array is essentially the key representation. They are built in a prior classes, passed to the current class and have exactly the same number of string objects (63) in each. The table is essential a data representation of
Questions:
Must I build a NSDictionary with 'keys' and 'objects'. How do I build thatDictionary. I have tried various methods stepping through a count loop with no success. I can to find a method for inserting objects for specific keys.
Can I load the table directly from the various arrays without a ViewController. I have the table and id's laid out but I have not been able to add a ViewController to this class - I only have 'Files Owner'. If this is easiest how can I do that.
If needed, how do I take elements of the 6 arrays in order as a comma delimited string in another array that becomes the input to a row of of the table and therefore parsed into the table.
No code to offer because all my attempts w/code have been unsuccessful.
NEED SOME SPECIFIC DIRECTION HERE.
.h
// ReportsOutput.h
// Stamp Collection
// Created by Terry Lengel on 4/20/15.
// Copyright (c) 2015 Terry Lengel. All rights reserved.
#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
#import "ReportsClass.h"
#interface ReportsOutput : NSWindowController <NSMenuDelegate,NSTableViewDataSource,NSTableViewDelegate,NSApplicationDelegate>{
// variable and outlet for the table
IBOutlet NSTableView *rptTable;
}
// data element sources
#property(nonatomic,strong) NSMutableArray *tblYrScott;
#property(nonatomic,strong) NSMutableArray *tblYrExt;
#property(nonatomic,strong) NSMutableArray *tblYrYear;
#property(nonatomic,strong) NSMutableArray *tblYrType;
#property(nonatomic,strong) NSMutableArray *tblYrPrice;
#property(nonatomic,strong) NSMutableArray *tblYrDescription;
// the Display data source array for the table
#property(strong) NSMutableArray *rptData;
#pragma mark - Method Declarations
-(BOOL)conditionData;
-(NSDictionary *)makeDictionaryRecord:(NSString*)scott withInfo:(NSString*)ext withInfo:(NSString*)year withInfo:(NSString*)type withInfo:(NSString*)price withInfo:(NSString*)Description;
#end
.m
// ReportsOutput.m
// Stamp Collection
// Created by Terry Lengel on 4/20/15.
// Copyright (c) 2015 Terry Lengel. All rights reserved.
#import "ReportsOutput.h"
#import "ReportsClass.h"
#interface ReportsOutput ()
#end
#implementation ReportsOutput
#synthesize tblYrScott;
#synthesize tblYrExt;
#synthesize tblYrType;
#synthesize tblYrPrice;
#synthesize tblYrYear;
#synthesize tblYrDescription;
#synthesize rptData;
-(id)initWithWindow:(NSWindow *)window{
self = [super initWithWindow:window];
if (self){
// initialize code here
}
return self;
}
-(void)windowDidLoad {
[super windowDidLoad];
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
}
-(void)applicationDidFinishLaunching:(NSNotification *)notification{
//Insert code here to initialize your application
}
// Terminate the app by using the RED button:
-(BOOL)applicationShouldTerminateAfterLastWindowClosed:
(NSApplication *)sender{
return YES;
}
-(void)awakeFromNib{
if (self.conditionData == YES){
[rptTable reloadData];
}
}
-(BOOL)windowShouldClose:(id)sender{
return YES;
}
-(void)performClose:(id)sender{
[self close];
}
#pragma mark - Table View Data Source
-(NSInteger)numberOfRowsInTableView:(NSTableView *)tableView{
return rptData.count;
}
-(NSView *) tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
NSTableCellView *scott = [tableView makeViewWithIdentifier:#"Scott" owner:self];
scott.textField.stringValue = [self.rptData objectAtIndex:row];
return scott;
}
// request for sorting
-(void) tableView:(NSTableView *)tableView sortDescriptorsDidChange:(NSArray *)oldDescriptors{
// table view received sort request
//sort the data, then reload the tableView data:
[rptData sortUsingDescriptors:[rptTable sortDescriptors]];
[rptTable reloadData];
}
-(BOOL)conditionData{
BOOL conditionData = NO;
NSString *rPrice;
NSString *rExt;
NSString *rYear;
NSString *rType;
NSString *rDescription;
for (int b=0; b<[tblYrScott count]; ++b){
//condition source data to remove any null appearences
rExt = [tblYrExt objectAtIndex:b];
if (rExt == (id)[NSNull null] || rExt.length == 0 ){
rExt = #"None";
}else if ([rExt isEqualToString:#" "]){
rExt = #"None";
}else
rExt = [tblYrExt objectAtIndex:b];
rYear = [tblYrYear objectAtIndex:b];
if (rYear == (id)[NSNull null] || rYear.length == 0 ){
rYear = #" ";
}else
rYear = [tblYrYear objectAtIndex:b];
rType = [tblYrType objectAtIndex:b];
if (rType == (id)[NSNull null] || rType.length == 0 ){
rType = #" ";
}else
rType = [tblYrType objectAtIndex:b];
rPrice = [tblYrPrice objectAtIndex:b];
if (rPrice == (id)[NSNull null] || rPrice.length == 0 ){
rPrice = #"n/r";
}else
rPrice = [tblYrPrice objectAtIndex:b];
rDescription = [tblYrDescription objectAtIndex:b];
if (rDescription == (id)[NSNull null] || rDescription.length == 0 ){
rDescription = #" ";
}else
rDescription = [tblYrDescription objectAtIndex:b];
NSDictionary *rptData = #{#"Scott":[tblYrScott objectAtIndex:b],#"Ext":rExt,#"Year":rYear,#"Type":rType,#"Price":rPrice,#"Description":rDescription};
}
//[rptTable reloadData];
return conditionData = YES;
}
-(NSDictionary*) makeDictionaryRecord:(NSString *)scott withInfo: (NSString *)ext withInfo: (NSString *)year withInfo: (NSString *)type withInfo: (NSString *)price withInfo: (NSString *)description{
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:scott,#"Scott",ext,#"Ext",year,#"Year",type,#"Type",price,#"Price",description,#"Description", nil];
return dict;
}
#end
Data Sample:
2015-04-25 12:20:49.251 StampsProjectDev[2981:591183] rptData = {
Description = "Lunar New Year - Horse";
Ext = None;
Price = "n/r";
Scott = 4846;
Type = C;
Year = 2014;
}
2015-04-25 12:20:49.252 StampsProjectDev[2981:591183] rptData = {
Description = "Jimi Hendrix";
Ext = None;
Price = "n/r";
Scott = 4880;
Type = C;
Year = 2014;
}
2015-04-25 12:20:49.252 StampsProjectDev[2981:591183] rptData = {
Description = "Charlton Heston";
Ext = None;
Price = "n/r";
Scott = 4892;
Type = C;
Year = 2014;
}
2015-04-25 12:20:49.252 StampsProjectDev[2981:591183] rptData = {
Description = "Janice Joplin";
Ext = None;
Price = "n/r";
Scott = 4916;
Type = C;
Year = 2014;
}
2015-04-25 12:20:49.252 StampsProjectDev[2981:591183] rptData = {
Description = "Ralph Ellison";
Ext = None;
Price = "n/r";
Scott = 4866;
Type = C;
Year = 2014;
}
Must I build a NSDictionary with 'keys' and 'objects'. How do I build thatDictionary. I have tried various methods stepping through a count loop with no success. I can to find a method for inserting objects for specific keys.
Not required, but advisable.
// assume six arrays are array0, array1, ...
// assume array0.count == array1.count == etc
for (int i=0; i<array0.count; ++i) {
NSDictionary *di = #{ #"ar0":array0[i], #"ar1":array1[i],... };
}
But a better idea is an NSObject subclass that you create that is meaningful to your users and has properties from corresponding elements from the arrays (call it MeaningfulObject)....
// in your datasource's interface definition
#property(strong) NSMutableArray *myArrayOfMeaningfulObjects;
self.myArrayOfMeaningfulObjects = [#[] mutableCopy];
// assume array0.count == array1.count == etc
for (int i=0; i<array0.count; ++i) {
MeaningfulObject *mi = [MeaningfulObject meaningfulObjectWithAttribute0:array0[i] attribute1:array1[i]... ];
[self.myArrayOfMeaningfulObjects addObject:mi];
}
Can I load the table directly from the various arrays without a ViewController. I have the table and id's laid out but I have not been able to add a ViewController to this class - I only have 'Files Owner'. If this is easiest how can I do that.
The table loads itself. Your job is to provide it with a datasource, which is an array of objects representing rows. Most people opt to make the view controller who's view contains the table act as the datasource, but the table's datasource can be any object. The two required parts of the NSTableViewDatasource protocol are (1) numberOfRowsInTableView: like this:
return self.myArrayOfMeaningfulObjects.count;
And (2) tableView:viewForTableColumn:row: like this:
MeaningfulObject *mrow = self.myArrayOfMeaningfulObjects[indexPath.row];
// configure a tableview cell using mrow's attributes
cell.textLabel.text = mrow.attribute0;
If needed, how do I take elements of the 6 arrays in order as a comma delimited string in another array that becomes the input to a row of of the table and therefore parsed into the table.
Don't understand this one, but hopefully a good answer is implicit in the foregoing
EDIT, Upon review of the code, it seems to me that you've nearly got it. The code has declared an array that will be the basis of the datasource, like this:
#property(strong) NSMutableArray *rptData;
Good, but a consequential mistake is made here:
NSDictionary *rptData = #{#"Scott":[tblYrScott objectAtIndex:b],#"Ext":rExt,#"Year":rYear,#"Type":rType,#"Price":rPrice,#"Description":rDescription};
That's a dictionary which looks as if it represents a single row in the table, named confusingly like the NSArray property. It looks as if the dictionary is built then abandoned on the iteration of the loop, overwritten by the object for the next row. To make this work, the objects representing the rows must be added to the datasource, like this:
// renamed rptData dictionary to rowDictionary
NSDictionary *rowDictionary = #{#"Scott":[tblYrScott objectAtIndex:b],#"Ext":rExt,#"Year":rYear,#"Type":rType,#"Price":rPrice,#"Description":rDescription};
[self.rptData addObject:rowDictionary];
I want a sectioned UITableView but I don't know how, my situation:
I have 2 arrays
array Date ("12/08/13", "13/08/13", "17/08/13")
array Count("2", "1", "5")
The count means how many rows are there in the section.
I have
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [dateArray objectAtIndex:section];
}
But how can make in the first section, 2 rows in the second section 1 row and in the third section 5 rows.
I hope someone can help me. If you have questions just ask.
Thanks.
EDIT: I get my information from my website so the array changes everyday.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// length of your section count array:
return [sectionCountArray count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// the section count from your array, converted to an integer:
return [sectionCountArray[section] integerValue];
}
UPDATE: From your comment is seems that you have a "flat" data source array.
Since the row numbers start with zero in each section, you have to compute the
index into the array from the row number and the section count of all previous sections
in cellForRowAtIndexPath:
NSInteger flatIndex = indexPath.row;
for (NSInteger sec = 0; sec < indexPath.section; sec++)
flatIndex += [tableView numberOfRowsInSection:sec];
cell.textLabel.text = yourFlatDataArray[flatIndex]; // (Just an example!)
You return the number of sections in the numberOfSectionsInTableView:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return [self.countArray count];
}
Then get the number from the array and return that in the numberOfRowsInSection:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSNumber *count = [self.countArray objectAtIndex:section];
return [count integerValue];
}
You need to override the following UITableView datasource method:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (section == 0)
return 2
else if (section == 1)
return 1
else (section == 2)
return 5
}
Say I have this JSON:
[
{"x":"01", "ID":"1"},
{"x":"02", "ID":"2"},
{"x":"02", "ID":"3"},
{"x":"03", "ID":"4"},
{"x":"03", "ID":"5"},
{"x":"03", "ID":"6"},
{"x":"03", "ID":"7"}
]
and I want to create a UITableView like this:
------------
Section 01
------------
ID: 1
------------
Section 02
------------
ID: 2
ID: 3
------------
Section 03
------------
ID: 4
ID: 5
ID: 6
ID: 7
How can I find out how many sections I need and how do I output the correct data in each section?
The first thing to do would be to convert that JSON into an objective-c data structure. I recommend an array of arrays where "X" is the index of each array of "ID" values.
Something like:
NSMutableArray *tableSections;
NSMutableArray *sectionData;
CustomDataObject *yourCustomDataObject;
int sectionIndex;
//Pseudo-code to create data structure
for(data in json) {
sectionIndex = data.X;
yourCustomDataObject = [[CustomDataObject alloc] initWithId:data.ID];
//Do index check first to insure no out of bounds
if(sectionIndex != OutOfBounds)
sectionData = [tableSections objectAtIndex:sectionIndex];
//Create the new section array if there isn't one for the current section
if(!sectionData) {
sectionData = [NSMutableArray new];
[tableSections insertObject: sectionData atIndex: sectionIndex];
[sectionData release];
}
[sectionData addObject: yourCustomDataObject];
[yourCustomDataObject release];
}
The code above is just pseudo-code to help you get started. Creating this array of arrays is probably something you have already done before.
The important part is accessing this data by implementing the UITableViewDataSource protocol. I recommend subclassing UITableView and implementing the UITableViewDataSource protocol in your custom subclass.
You will need these methods:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [tableSections count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[tableSections objectAtIndex: section] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//Implement as you normally would using the data structure you created
NSMutableArray *sectionData = [tableSections objectAtIndex: indexPath.section];
CustomDataObject *dataObject = [sectionData objectAtIndex: indexPath.row];
NSLog(#"\n**** Debug Me *****indexPath: section: %i row: %i \ndataObject%#", indexPath.section, indexPath.row, dataObject);
}
This should be enough to get you started. Figuring out the rest of the details should be good practice.
Assuming that the values of "x" will increase from 1 to n without skipping any values, then the number of sections you need will simply be the last value of "x" (i.e., n).
If not, you will indeed have to iterate through the values of "x" to see how many unique values there are.
Ended up with this solution:
NSMutableDictionary *result = [NSMutableDictionary dictionary];
for(NSDictionary* dict in data) {
NSNumber *x = [dict objectForKey:#"x"];
NSMutableArray *resultsForX = [result objectForKey:x];
if(!resultsForX) {
resultsForX = [NSMutableArray array];
[result setObject:resultsForX forKey:x];
}
[resultsForX addObject:dict];
}
NSMutableArray *newArr = [result allValues];
I am trying to create a UITableView with two different sections. I know I can group them on an attribute of my managed object. For instance if I'd like to group them per name I'd do:
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:_context
sectionNameKeyPath:#"name"
cacheName:#"uploadProperties"];
And I return the number of secionts like:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[_fetchedResultsController sections] count];
}
The problem though is that I do not want to group it per attribute (such as the name). I want to group them for specific values, namely one part that has pud_id = 0 and another section that has pud_id > 0.
How can I achieve this? Is it possible to use kind of a where clause? Or can I create a property on my managed object and use this in the sectionNameKeyPath such as:
- (BOOL) hasPudZero {
if (self.pud_id == 0)
return YES;
return NO;
}
??
Thanks for your input!
Perhaps you could create a transient attribute of type NSString that returns one of two strings?
- (NSString *)sectionIdentifier
{
[self willAccessValueForKey:#"sectionIdentifier"];
NSString *tmp = [self primitiveValueForKey:#"sectionIdentifier"];
[self didAccessValueForKey:#"sectionIdentifier"];
if (!tmp) {
if (self.pud_id == 0) {
tmp = #"SectionOne";
} else {
tmp = #"SectionTwo";
}
[self setPrimitiveValue:tmp forKey:#"sectionIdentifier"];
}
return tmp;
}
and then use sectionNameKeyPath:#"sectionIdentifier" when creating your NSFetchedResultsController. Make sure you set the primitive value of sectionIdentifier back to nil if the value of put_id changes.