How Can I Change Tableview Array with Segment Control - ios

I Have a one table view and i have two array. My arrays name AllItems and SpecialItems. I Use segment control. I wantto if segment value is 0 tableview load AllItems Array, When change segment value and value is = 1 than mytableview reload tada but SpecialItems array. Can u help me please. Thanks.

I solved this problem with table tag.
- (IBAction)segmentControlChanged:(UISegmentedControl *)sender {
if (sender.selectedSegmentIndex == 1) {
mytable.tag = 1;
}
else
{
mytable.tag = 0;
}
[mytable reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(tableView.tag==1)
{
return [specialItems count];
}
else
return [allItems count];
}

You could create two data source classes that implement all the UITableViewDataSource methods: one for AllItems and one for SpecialItems. To switch between the two, connect a valueChanged action. In the method that is called, set the data source and reload the table view.
- (void)valueChange:(UISegmentedControl *)sender
{
if (/* condition for all items */) {
self.tableView.dataSource = self.allItemsDataSource;
} else {
self.tableView.dataSource = self.specialItemsDataSource;
}
[self.tableView reloadData];
}

I would personally create an array which the data is loaded from. Put this in your implementation:
NSArray * _tableData
Then in your viewDidLoad just allocate this for the array which we want it to start on.
_tableData = [[NSArray alloc] initWithArray:allItems];
This initially loads the data we will always see as the segment control starts on index 0. We have to set the initial data somewhere so the tableView loads with some data in it.
Then set the number of rows and the cellForRowAtIndex to pick up from the _tableData array
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _tableData.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView_ cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell * cell = [tableView_ dequeueReusableCellWithIdentifier:bCell];
// Here we use the specific array as we would normally
return cell;
}
This step means the tableView will load with the array. Even if the array is empty the view will still load as the number of cells will be zero.
Now in our value changed function we can reset the array as we need to:
- (IBAction)segmentControlChanged:(UISegmentedControl *)sender {
if (sender.selectedSegmentIndex == 1) {
_tableData = allItems;
}
else {
_tableData = specialItems;
}
[self.tableView reloadData];
}
You just need to make sure the segment control changed is linked up in the XIB file (or programatically) and that you reload the table after choosing the array.
This kind of thing is actually really easy to do. I would definitely recommend working it through step by step if you're having trouble. Make sure each step is working before applying the next:
Get the tableView loading with both sets of data individually
Confirm that the segment control is calling the change function when clicked
Then that should do it

Related

Objective c - Some array items are not shown in my UITableView (some shown twice)

I have my main view controller that shows a UITableView.
Each cell of this are custom (I've created a UIView for custom presentation).
For showing these items in my tableView, I populate an array with the content of the "allFilesFolderPath" folder with this code:
- (void)configureView {
_itemArray = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:allFilesFolderPath error:nil];
}
and
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.itemArray count];
}
and I create my custom cells for showing them with :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger row = [indexPath row];
myItem = [self.itemArray objectAtIndex:row];
NSLog(#"My Item : %#", _itemArray.description);
static NSString *CellIdentifer = #"cardCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifer];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifer];
}
return cell;
}
When I print the array with the NSLog, I get the correct list of item and in the alphabetical order (like how they are stored in the Documents location on my iPhone):
My Item : (
Music,
Music10,
Music2,
Music3,
Music4,
Music5,
Music6,
Music7,
Music8,
Music9,
Photos,
Videos
)
But when I run the app in my iPhone (or in the simulator), the cells are correctly displayed (in the order) until the eighth item. After this number, in my case, instead of having "Music8", "Music9", "Photos", "Video" I come back to the beginning of th array so "Music", "Music10", "Music2" and "Music3"
To better understand what I get, here is the screenshots :
I'm really lost! I've searched (and search again) what I'm doing wrong but I don't find anything, everything is correct for me.
Please help me to find my issue so that I can sleep normally.
EDIT: here is the method I've set to retrieve the myItem string from my other class :
+ (NSString *)getItemName {
return myItem;
}
And here is how I retrieve it from my other class :
NSString *test = [ViewController getItemName];
_itemName.text = test;
EDIT2 : Here is the code used for setting my custom TableViewCell
(sorry for missing these informations
#import "TableViewCell.h"
#implementation TableViewCell
- (void)awakeFromNib {
// Initialization code
[self cardSetup];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (void)cardSetup {
_cardView.layer.masksToBounds = NO;
_cardView.layer.shadowOffset = CGSizeMake(0, 1);
_cardView.layer.shadowRadius = 1;
_cardView.layer.shadowOpacity = 0.3;
NSString *test = [ViewController getItemName];
_itemName.text = test;
}
#end
There is this call named "dequeueReusableCell...". Table view cells are reused. If 8 cells fit on the screen, and you scroll the view up, your ninth row will reuse the cell that was used for the first row. That's why you have to set up your cell in cellForRowAtIndexPath, which apparently you refuse to do.
Cells are used just for display. They are not used for storing data. You should have a data model, accessed by everyone. cellForRowAtIndexPath reads from that data model. And then if something happens (for example by tapping on a button in a cell) that changes the data model, then you change the data model, and the data model should tell all the interested parties that the model has changed.
Your cell in one view and a UILabel elsewhere should definitely not be connected at all. Any changes should propagate through your data model.
You're not using myItem anywhere in cellForRowAtIndexPath: Your cells seem to be getting their text from some other method, when they should be getting it from celForRowAtIndexPath:

how to create single tableview with two nested tableview cell xibs using iOS storyboard?

I am trying to implement accordion tableview with parent and child custom tableview cells. I am using below mentioned open source code.
Source code : https://github.com/singhson/Expandable-Collapsable-TableView
In that code having single tableview with single tableview cell. It will show for parent and child cells but I want to make:
Main storyboard Tableview
Parenttableviewcell separate class and xib
Childtableviewcell separate class and xib
It should apply on main controller tableview with accordion. Right now in this code there is no separate custom cells (parent and child using same tableview cell and changing data only).
You can implement it by trying to manage the sections and rows of a table view, thats what i assume to be the simplest way to implement it without using any third party code. For example
All the section headers will contain their own number of rows.
Take a array or dictionary or array that will store the state of expanded rows in sections. (Say '1' for expanded state and '0; for collapsed state)
If in the initial state, all the UI is expanded then set all the objects '1' in array or dictionary.
On tap of individual headers (I am assuming section will be collapsed on click on individual header of section) you can get which section need to be collapsed. Retain this state as '0' for collapsed section rows in the array or dictionary.
Reload the table and check in heightForRow delegate method for the '0' entity in your array or dictionary. Wherever you find it to be '0', return height as 0 in the delegate method.
Perform the opposite for reverse functionality.
UPDATE FOR CODE
- (IBAction)btnMenuViewTypeTapped:(id)sender{
UIButton *btnSender = (UIButton *)sender;
if(menuViewType == MVTCollapse){
[btnSender setImage:[UIImage imageNamed:#"MenuCollapse"] forState:UIControlStateNormal];
menuViewType = MVTExpand;
for(NSInteger intAtIndex=0; intAtIndex<[mutArrSearchMenuItems count]; intAtIndex++){
[mutArrSectionOpened replaceObjectAtIndex:intAtIndex withObject:#"1"];
}
} else{
[btnSender setImage:[UIImage imageNamed:#"MenuExpand"] forState:UIControlStateNormal];
menuViewType = MVTCollapse;
for(NSInteger intAtIndex=0; intAtIndex<[mutArrSearchMenuItems count]; intAtIndex++){
[mutArrSectionOpened replaceObjectAtIndex:intAtIndex withObject:#"0"];
}
}
[tblViewRestaurantMenu reloadData];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return [mutArrSearchMenuItems count];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
NSInteger intNumOfRow;
if([mutArrSectionOpened[section] isEqualToString:#"1"]){
intNumOfRow = [mutArrSearchMenuItems[section][strMenuType] count];
}
else{
intNumOfRow = 0;
}
return intNumOfRow;
}
You can implement it by using SKSTableview.
for that Please refer the below link:
SKSTableView
Let make two kinds of custom cell.
+Parent cell is tableview sectionHeader
+Child cell is normal cell
/*your datasource like this
arrData = #[section, section, section ....]
section.name
section.image
section.arrayChild
arrayChild = #[child, child, child....]
child.name
child.image
*/
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return arrData.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
section = arrData[section];
child = section.child;
return child.count;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
//Cell is Custom of UITableViewHeaderFooterViewCell
//load your Parent Cell here
}
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//load your custom chill cell here
}

Multiple UITableViewController and one model class to hold data

I'm having trouble deciding.
I created a model class, and another one as a singleton,
the singleton uses a NSMutableArray and returns a NSArray as a copy.
One of the UITableViewController will show the array of data.
The question is, if I want to show different data on each UITableViewController should i create multiple arrays and methods that save and edit the arrays or is there a better way to do this?
On your UITableViewDataSource methods, you can decide what information to return based on which TableView is calling the method. For example:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
//Check which tableview is calling
if (tableview == self.tableView1) {
// Return the number of sections for each possible Table
return 1;
} else {
return 2;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
// If you're serving data from an array, return the length of the array:
if (tableview == self.tableView1) {
// Return the number of sections for each possible Table
return [dataArray1 count];
} else {
return [dataArray2 count];
}
}
And the same for any DataSource/Delegate methods you may need to implement.
Hope that helps!!

numberOfSectionsInTableView for two UITableViews

I have two UITableView which appears according to UISegmentControl.selectedSegmentIndex. The first table is grouped style and the second is plain.
NSFetchedResultsController is for the first UITableView, and NSMutableArray for the second.
In my numberOfSectionsInTableView:
return [_fetchedResultsController.sections count];
In this case the second UITableView's data displays for 3 times (because FirstTableView section count = 3).
I would like for the second tableView numberOfSectionsInTableView
return 1;
You should check which tableview you are displaying before returning the count. You can use the tableView input param for this.
For eg:-
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if (tableView == self.firstTableView) //or (tableView == firstTableView)
return [_fetchedResultsController.sections count];
else
return 1;
}
You can use the following condition...
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
if(tableView == firstTableView)
return 1;
else if (tableView == secondTableView)
return 2;
//... and so on you can do like this....
}
All the best !!!
I guess you are setting the same data source for both table views. If that is the case, then in your:
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
if(tableView == firstTableView){ //assuming you have a reference to the first table view
return [_fetchedResultsController.sections count];
}else{
return 1;
}
}
You will have to do this in all your datasource methods. Instead it would be easier to make the datasource of the tableviews different.
To do this you can drag an object (the blue cube ) from the object library and set its class as say, SecondTableDatasource. And then set this object as the datasource for your second table, and implement the corresponding datasource methods in that class.
You can add tag for each tableView and check for each tag in numberOfSectionsInTableView

Uitableview cell changes value when scrolling

The code below is creating a search for many strings. Initially there are 5 rows, when you reach row five, it adds another row. Instead of just directly editing the row, i load a filter controller (another view controller that as you type it completes words for you). When the user finishes finding a word he clicks it and comes back to this view controller. Now i want to fill the cell that was originally tapped with the text from the filter.
I tried asking earlier and didn't get any concrete answers.
I am running into a problem where when i scroll (after adding a new row), it starts filling in those rows with info already in the table, (as opposed to staying blank)
Please help me where i am going wrong
//global indexpath to remember which cell tapped
NSIndexPath *globalPath;
#interface SearchViewController ()
#end
#implementation SearchViewController
//Load implementation once per launch
- (void)viewDidLoad
{
[super viewDidLoad];
[self linkInputTableToDelegate];
_temporaryResultsArray =[[NSMutableArray alloc]init];
_flurryArray=[[NSMutableArray alloc]init];
_numberOfSections=6;
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:NO];
[InputTable reloadData];
textFromUserDefaults=[[[HelperMethods alloc]init]getObjectUserDefault:#"textFiltered"];
[self addTextToFlurryArrayForFlurryAndSavedLists:_textFromUserDefaults];
}
-(void)viewDidDisappear:(BOOL)animated{
}
- (IBAction)searchButtonPressed:(UIButton *)sender {
self.tabBarController.selectedIndex = 1;
}
//Makes the input table respond to delegate table view methods
-(void)linkInputTableToDelegate{
_inputTable.dataSource=self;
_inputTable.delegate=self;
}
-(void)performSearch:(NSString*)text{
//do search
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
int numberOfRows=_numberOfSections;
//Rows for iPhone 4
if ([[UIScreen mainScreen]bounds].size.height==480) {
numberOfRows=numberOfRows;
//Rows for iPhone 5
}else if ([[UIScreen mainScreen]bounds].size.height==568){
numberOfRows=numberOfRows+1;
}
return numberOfRows;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//In reality groups are created with 1 row inside, this is to allow spacing between the rows
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *kCellID = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellID];
if (!cell) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellID];
}
//Is the cell the same as the one clicked when going to ingredient filter
BOOL cellIndexPathSameAsSelected=[self isCellIndexSameAsPreviousClicked:indexPath];
cell.textLabel.textColor=[UIColor blackColor];
if (cellIndexPathSameAsSelected && _textFromUserDefaults!=nil) {
if (![cell.textLabel.text isEqualToString:_textFromUserDefaults]) {
cell.textLabel.text=_textFromUserDefaults;
[self performTextSearch:_textFromUserDefaults];
}
}
return cell;
}
//Compares the previous clicked cell with the cell now selected
-(BOOL)isCellIndexSameAsPreviousClicked: (NSIndexPath*)cellPath{
if (cellPath.row == globalPath.row && globalPath.section==cellPath.section) {
return YES;
}
else{
return NO;
}
}
- (void)updateTableViewWithExtraRow :(NSIndexPath*)rowSelected{
NSLog(#"number of sections =%i",_numberOfSections);
if (rowSelected.section == _numberOfSections) {
_numberOfSections ++;
}
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellText = [tableView cellForRowAtIndexPath:indexPath].textLabel.text;
[[[HelperMethods alloc]init]saveObjectToUserDefaults:cellText :#"textFiltered"];
globalPath = indexPath;
[self updateTableViewWithExtraRow:indexPath];
}
-(void)addTextToFlurryArrayForFlurryAndSavedLists:(NSString*)text{
if ([_flurryArray count]==0 &&[text length]>0) {
[_flurryArray addObject:text];
}
for (int i=0;i<[_flurryArray count];i++) {
NSString *textInArray=[_flurryArray objectAtIndex:i];
if (![textInArray isEqualToString:text]) {
[_flurryArray addObject:text];
}
}
NSLog(#"Total number of saved items = %i",[_flurryArray count]);
}
// Dispose of any resources that can be recreated.
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
I have a couple of reactions looking at the code:
A couple of observations about the proper use of the UITableViewDataSource methods, specifically numberOfRowsInSection, numberOfSectionsInTableView, and cellForRowAtIndexPath:
These really should be driven by some model data structure (e.g. a NSMutableArray) and nothing else;
These methods should be stateless. They should not relying on the value of some NSString instance variable, like _textFromUserDefaults) but rather always look up the value in the NSMutableArray model structure on the basis of the value of the indexPath parameter. You simply cannot make any assumptions about when cellForRowAtIndexPath will be called. This may well account for your duplicate values.
None of these should be doing anything besides responding to the UITableView inquiry. For example, your cellForRowAtIndexPath is invoking performTextSearch. It really shouldn't do anything except return the cell.
Your cellForRowAtIndexPath currently has conditional logic and only updates the cell if certain conditions holds. Because cells are reused, you really want to make sure that you initialize the cells regardless. You can't be assured that the cell is blank when you get it, nor that the previous contents are the previous values for that indexPath. Because cells are reused, it could be for an entirely different row. This could also account for your duplicative entries.
Regarding the interaction of the master view controller and the details view controller, there are more elegant ways than passing data back and forth via NSUserDefaults. For example when you initiate the details view controller, you could just pass it the information it needs. And when it's done, it should call a method in the master view controller to update the data in the master view. To do that, the master view controller should conform to some protocol of your own creation. If you see the example I shared via chat, you can see what that might look like. Anyway, by having some delegate method in the master view controller that the detail view controller calls when it's done, that eliminates the rather fragile technique of using viewDidAppear to control the updating of the master table view.
You might want to contemplate employing "edit" (which allows you to delete, possibly also edit a particular row) and "add" buttons like the standard "master-detail" template that Xcode provides. There are a number of standard conventions here that might be better than having an array of blank cells that you can then tap on. Clearly, your user experience is entirely up to you, but you can always contemplate whether there are existing, familiar conventions that you might employ.
Rob's feedback is good. In broader terms, you can't rely on the cells in a UITableView to hold onto their data. For efficiency, it will be creating, using, and destroying cells at will, and using cellForRowAtIndexPath to figure out what they should look like. Instead of testing what's in a cell, you need to have your own set of data which describe the value of each cell, and just set the value based on the indexPath. I'd recommend storing all your cell information in an NSMutableArray which contains NSStrings or something more complicated if necessary. It will be easy to set default values when you add cells to the array. Then cellForRowAtIndexPath can just access the array rather than attempting its own logic based on current cells.

Resources