UItableView with MagicalRecord huge data set - ios

I have a table that stores some data. Let's say data is too big to load into memory all at once. I want to show this data in UItableView.
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [Item MR_numberOfEntities];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// How to get object for this row ???
return cell;
}
The only way i knew is to load all data into array with
NSArray *items = [Item MR_findAll];
But i don't want to do that. User will be shown first 10 rows, and why should i load all them from CoreData. Is there any way to fetch them one by one using MagicalRecord?

According to docs you need to init fetch request and also you may want to set the fetch offset depending on scroll progress.. but you will have to trace it manually. Here is a basic approach how it can be achieved.
PS. I haven't tested this code . I just wrote it in the text editor :). but it should work as you requested. e.g loading items with limit 10.
#property (nonatomic) int itemCount;
#property (nonatomic, strong) NSMutableArray * items
static const int FETCH_LIMIT = 10;
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _itemCount;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Classic start method
static NSString *cellIdentifier = #"MyCell";
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell)
{
cell = [[MyCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MainMenuCellIdentifier];
}
MyData *data = [self.itemsArray objectAtIndex:indexPath.row];
// Do your cell customisation
// cell.titleLabel.text = data.title;
if (indexPath.row == _itemCount - 1)
{
[self loadMoreItems];
}
}
-(void)loadMoreItems{
int newOffset = _itemCount+FETCH_LIMIT; // Or _itemCount+FETCH_LIMIT+1 not tested yet
NSArray * newItems = [self requestWithOffset: newOffset];
if(!newItems || newItems.count == 0){
// Return nothing since All items have been fetched
return;
}
[ _items addObjectsFromArray:newItems ];
// Updating Item Count
_itemCount = _items.count;
// Updating TableView
[tableView reloadData];
}
-(void)viewDidLoad{
[super viewDidLoad];
_items = [self requestWithOffset: 0];
_itemCount = items.count;
}
-(NSArray*)requestWithOffset: (int)offset{
NSFetchRequest *itemRequest = [Item MR_requestAll];
[itemRequest setFetchLimit:FETCH_LIMIT]
[itemRequest setFetchOffset: offset]
NSArray *items = [Item MR_executeFetchRequest:itemRequest];
return items;
}
Hope you find it helpful :)

Related

How to create multiple columns in UITableView i.e CustomCellView?

I am just starting iOS development. I am trying to display a multiple columns in UItableview from NSArray? So please help me anyone? I have created one column but i don,t know how to create another column?
I have done the following.
// This is my code
- (NSArray *)numbers
{
NSArray *col1 = #[#"1", #"2", #"3", #"4", #"5"];
return col1;
}
- (NSArray *)numberCodes
{
NSArray *col2 = #[#"Roy", #"Ankit", #"Jhon", #"Kem", #"Pawan"];
return col2;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.numbers.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
//cell.textLabel.text = [self.numbers objectAtIndex:indexPath.row];
cell.textLabel.text = [self.numberCodes objectAtIndex:indexPath.row];
return cell;
}
Step-1
on your viewController.h create the one Global Array like
#property (nonatomic,retain) NSMutableArray *tableData;
on your ViewController.m allocate the memory of that array like
- (void)viewDidLoad
{
[super viewDidLoad];
tableData = [[NSMutableArray alloc] initWithObjects:#"One",#"Two",#"Three",#"Four",#"Five",#"Six",#"Seven",#"Eight",#"Nine",#"Ten",nil];
}
on that tableview Datasource method do like
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [tableData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"XXXX";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell.textLabel.text = [tableData objectAtIndex:indexPath.row];
return cell;
}
for sample see this
1. You may create costume cell like having multiple fields/column in table. 2. Place multiple table view, one for each column. Use auto-layout for better look.

A uitable view with 3 groups in it

i am a developing a ios app with a view controller and a table view in it. i am trying to load list of items in 3 groups but when i compile it it shows correct count but not showing all the items jus repeating items. please help. let me post my code here.
#interface ViewController ()
#end
#implementation ViewController {
NSArray *menuItems;
NSArray *menuItems2;
NSArray *dash;
}
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor colorWithWhite:0.6f alpha:1.0f];
self.tableView.backgroundColor = [UIColor colorWithWhite:0.6f alpha:1.0f];
self.tableView.separatorColor = [UIColor colorWithWhite:0.15f alpha:0.2f];
menuItems = #[#"itm1", #"itm2", #"itm3", #"itm4"];
menuItems2 = #[#"itm1", #"itm2", #"itm3", #"itm4"];
dash = #[#"itm1", #"itm2"];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 3;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
if (section == 0) {
return [menuItems count];
}
if (section == 1) {
return [menuItems2 count];
}
if (section == 2) {
return [dash count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = [menuItems objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
return cell;
}
#end
Your cellForRowAtIndexPath... method needs to be written so the cell is populated with proper data based on the cell's section and row. Right now you don't do anything but use an empty cell.
You don't configure your cells. After dequeuing a cell, you have to set its title.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = [menuItems objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Get the correct array, depending on the current section.
NSArray *items = nil;
if (indexPath.section == 0) {
items = menuItems;
}
else if (indexPath.section == 1) {
items = menuItems2;
}
else if (indexPath.section == 2) {
items = dash;
}
// Get the string from the array and set the cell's title
NSString *title = items[indexPath.row];
cell.textLabel.text = title;
return cell;
}

iOS - Simple Table Sections with Array Data

I'm just starting with iOS/Xcode and have been Googling/Youtubing for an hour and can't find a matching tutorial. All I'm trying to do right now is display a table with a list of exercises (rows) that are grouped by bodypart (sections). The bodypart sections will never change, but the user will be able to add a custom exercise to a bodypart.
Now, I'm assuming that I need an array for the sections and also an array for exercises...creating those is simple enough. I'm running into a problem assigning exercises to specific sections. Here's an example of the faulty code that when rendered, displays both exercises under both sections...also there aren't any section names being generated in the table so I'm not sure where that comes into play either.
Here's a screenshot of the result (as a side note, not sure why my nav controller isn't rendering): http://i.imgur.com/icoJgEq.jpg
Create the individual items:
#property NSString *exerciseName;
#property NSString *exerciseCategoryName;
Create/Allocate the arrays:
#property NSMutableArray *exerciseCategories;
#property NSMutableArray *exercises;
self.exerciseCategories = [[NSMutableArray alloc]init];
self.exercises = [[NSMutableArray alloc]init];
Fill the arrays with some default data:
- (void)loadInitialData {
FNTExerciseCategories *category1 = [[FNTExerciseCategories alloc]init];
category1.exerciseCategoryName = #"Chest";
[self.exerciseCategories addObject:category1];
FNTExerciseCategories *category2 = [[FNTExerciseCategories alloc]init];
category2.exerciseCategoryName = #"Biceps";
[self.exerciseCategories addObject:category2];
FNTExercises *exercise1 = [[FNTExercises alloc]init];
exercise1.exerciseName = #"Bench Press";
[self.exercises addObject:exercise1];
FNTExercises *exercise2 = [[FNTExercises alloc]init];
exercise2.exerciseName = #"Barbell Curl";
[self.exercises addObject:exercise2];
}
Load the data:
[self loadInitialData];
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [self.exerciseCategories count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [self.exercises count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ExercisePrototypeCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
MFTExercises *exercise = [self.exercises objectAtIndex:indexPath.row];
cell.textLabel.text = exercise.exerciseName;
return cell;
}
Thank you very much to anybody that can chime in!
Actually in tableView:numberOfRowsInSection: you are returning the count of the entire exercises array. So with your sample data you would have two rows per section. Try making an array of exercises for every section and then code something like the following:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
if (section == 0) {
return [self.chestExercises count];
}
else if (section == 1) {
return [self.bicepsExercises count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ExercisePrototypeCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
MFTExercises *exercise;
if (indexPath.section == 0) {
exercise = [self.chestExercises objectAtIndex:indexPath.row];
}
else if (indexPath.section == 1) {
exercise = [self.bicepsExercises objectAtIndex:indexPath.row];
}
cell.textLabel.text = exercise.exerciseName;
return cell;
}
In this case the chestExercises array would only contain the "Bench Press"-exercise and the bicepsExercises would only contain the "Barbell Curl"-exercise. So you would get one row per section.
For achieving that the sections have titles you would need to implement the method
- (NSString *)tableView:(UITableView *)aTableView titleForHeaderInSection:(NSInteger)section {
return [self.exerciseCategories objectAtIndex:section];
}
which gives the sections the title according to the names stored in the array.
A more sophisticated way to build your datasource would be to create a NSDictionary with the section names as the keys (bodyparts) and the values being arrays containing the exercises for the bodypart. For instance if your categories are merely strings you could build such a dictionary with your sample data (for the purpose of demonstration I added another exercise):
FNTExerciseCategories *category1 = [[FNTExerciseCategories alloc]init];
category1.exerciseCategoryName = #"Chest";
[self.exerciseCategories addObject:category1];
FNTExerciseCategories *category2 = [[FNTExerciseCategories alloc]init];
category2.exerciseCategoryName = #"Biceps";
[self.exerciseCategories addObject:category2];
FNTExercises *exercise1 = [[FNTExercises alloc]init];
exercise1.exerciseName = #"Bench Press";
FNTExercises *exercise2 = [[FNTExercises alloc]init];
exercise2.exerciseName = #"Barbell Curl";
FNTExercises *exercise3 = [[FNTExercises alloc]init];
exercise3.exerciseName = #"Another Exercise";
// the instance variable self.exercises is a NSMutableDictionary now of course
self.exercises = [[NSMutableDictionary alloc] init];
exercises[category1.exerciseCategoryName] = #[exercise1];
exercises[category2.exerciseCategoryName] = #[exercise2, exercise3];
The advantage here is that you now have one dictionary containing all arrays that contains all your data. So as you're adding more data you don't have to change your implementation of the tableView datasource. BTW I am using Modern Objective-C syntax for the dictionary and arrays.
Having created a dictionary like that you could then simply implement your table view data source like so:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
// This gives the name of the category at the current section.
// It is then used as a key for the dictionary.
NSString *currentCategory = [[self.exerciseCategories objectAtIndex:section] exerciseCategoryName];
return [self.exercises[currentCategory] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ExercisePrototypeCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
NSString *currentCategory = [[self.exerciseCategories objectAtIndex:indexPath.section] exerciseCategoryName];
MFTExercises *exercise = [self.exercises[currentCategory] objectAtIndex:indexPath.row];
cell.textLabel.text = exercise.exerciseName;
return cell;
}
Using a NSDictionary may or may not benefit your app but you don't have to create an array as instance variable for every body part you have. It may also be more easy to save a single dictionary to disk for persistence.
First of all, you should practice it with WWDC UITableView section. There are many source code that uses UITableView, UICollectionView and UIScrollView.
What you need in that code is you need to return section header for exerciseCategories, you only defined number of section in - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView this delegate function but you are returning all nil value for the section header at the moment.
- (NSString*)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
FNTExerciseCategories *category = [self.exerciseCategories objectAtIndex:section];
return category. exerciseCategoryName;
}
this will display your section. but you need to think about the structure of your data because right now you are not returning correct number for each section you are just returning [self.exercises count] for all section.
And to render the UINavigationController, you need to push the view rather than present view as modal.
[self.navigationController pushViewController:exerciseView animated:YES];

populating a grouped UITableView

I'm trying to populate a grouped UITableView using an NSMutableArray. I want each element in the array to have its own section. i.e : One element (one row) per one section.
This is my code I have written so far.
#import "MailViewController.h"
#interface MailViewController ()
#end
#implementation MailViewController
NSMutableArray *mailboxes;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
mailboxes = [[NSMutableArray alloc] initWithObjects:#"Inbox", #"Drafts", #"Sent Items", nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return mailboxes.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1;
}
- (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];
}
cell.textLabel.text = [mailboxes objectAtIndex:indexPath.row];
return cell;
}
#end
Currently this is how it looks.
How can I get this the way I described above? (first section: Inbox, second section: Drafts, Third section: Sent Items etc.) I've gotten close but not quite there yet.
Thank you.
You should change:
cell.textLabel.text = [mailboxes objectAtIndex:indexPath.row];
to
cell.textLabel.text = [mailboxes objectAtIndex:indexPath.section];
These lines should go in the cellForRowAtIndexPath: method.
Your array index should be based on the sections and not the rows. Row is always fixed at zero.
Replace
cell.textLabel.text = [mailboxes objectAtIndex:indexPath.row];
With
cell.textLabel.text = [mailboxes objectAtIndex:indexPath.section];
If you want section headings as well implement the - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section and return a string based on the section..
With the code from other answers, you will be able to display one cell inside each section. What I prefer to do is have a multi-dimensional NSArray of pre-built cells and when I need them they're in index [section][row].
You currently have one array with just sections and trying to access them as rows (as other answers suggest). Your code won't work if you have more mailboxes in one table group..
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [mailboxes count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection: (NSInteger)section
{
return 1;
}
- (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];
}
switch(indexpath.section){
case 0:
cell.textLabel.text = [mailboxes objectAtIndex:indexPath.section];
case 1:
cell.textLabel.text = [mailboxes objectAtIndex:indexPath.section];
case 2:
cell.textLabel.text = [mailboxes objectAtIndex:indexPath.section];
default:
break;
}
return cell;
}

UITableView Indexing

I have an indexed table view that organizes cities in a state by their first letter. What I have working is an NSMutableDictionary is created with the keys of A,B,C,etc. and the corresponding cities are added to their respective arrays. ex:
Y = (
Yatesboro,
Yeagertown,
York,
"York Haven",
"York New Salem",
"York Springs",
Youngstown,
Youngsville,
Youngwood,
Yukon
);
Z = (
Zelienople,
Zieglerville,
"Zion Grove",
Zionhill,
Zionsville,
Zullinger
);
now, my table view loads with the correct number of sections, and rows within the sections and the indexing control works fine with this:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [cities count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if([searchArray count]==0)
return #"";
return [searchArray objectAtIndex:section];
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[cities objectForKey:[searchArray objectAtIndex:section]] count];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
NSLog([NSString stringWithFormat:#"clicked index : %i",index]);
if (index == 0) {
[tableView scrollRectToVisible:[[tableView tableHeaderView] bounds] animated:NO];
return -1;
}
return index;
}
My issue is now populating the text of the table cell with the text for each section...Any thoughts on how I can grab this info?
the cellForRowAtIndexPath gets a parameter of type NSIndexPath passed which contains both the required row and section. See NSIndexPath UIKit Additions for more details.
With that being said, this might work in your particular case:
- (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] autorelease];
}
NSString *letter = [searchArray objectAtIndex:indexPath.section];
NSArray *city = [cities objectForKey:letter];
cell.text = [city objectAtIndex:indexPath.row];
return cell;
}
Not sure if I got it right (haven't tried on a compiler), but you may get the general idea by now :)

Resources