Currently I have an array of objects right now
- (void)viewDidLoad
{
[super viewDidLoad];
self.webSites = [NSMutableArray arrayWithObjects:#"Website", #"iOS", #"Android", nil];
}
I am trying to access the one at index:0 which should be Website with the following code to perform a segue to another table view controller where there will be categories to select from.
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) {
[self performSegueWithIdentifier:#"viewCategories" sender:nil];
}
}
Every time I run the app I click on Website and nothing happens, but when I click on iOS the segue performs. Any answer as to why I'm running into this problem?
You are using wrong method.
didDeselectRowAtIndexPath // Used for deselecting the cell row
should be
didSelectRowAtIndexPath // Used for selecting the cell row
Related
i. I have tried a lot but could not execute successfully .
ii. In tableview cell, there are 3 3 fields need to display. One image view, button1 -->taking photo button, button2---> browse button.
iii. First time tableview should display a custom cell with one row.
iv. When a user clicks "add new button" , which lays outside of tableview, a new row will create with all above 3 fields (image view, button1, button2)
v. Number of clicks of "add new button" , will create new rows with all above 3 fields.
vi. I could do all above things created dynamically successfully with a simple image view which contains above 3 fields but could not succeed to work on custom cell.
vii. Again I need to set the tag of each cell, broswe button, take photo button so that when clicks, will take the tag value.
The table view works by adding a delegate and a data source. Assuming your table view has an owner as a view controller and both delegate and data source are the view controller itself. All you need to do is implement those data source methods to return an appropriate data then you should call reloadData on the table view or if you want a bit of extra work to look nicer check how to add rows animated around the web.
This is a very simple and not optimized example but is very short and easy to read. I hope it helps you get on the right track:
#interface MyViewController()<UITableViewDataSource, UITableViewDelegate>
#property UITableView *tableView;
#property NSArray *myCells;
#end
#implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.delegate = self; // could be done in storyboard
self.tableView.dataSource = self; // could be done in storyboard
[self addACell];
}
- (void)addCellButtonPressed:(id)sender {
[self addACell];
}
- (void)addACell {
MyCell *cell = [[MyCell alloc] init];
[cell.button1 addTarget:self action:#selector(cellButton1Pressed:) forControlEvents:UIControlEventTouchUpInside];
[cell.button2 addTarget:self action:#selector(cellButton2Pressed:) forControlEvents:UIControlEventTouchUpInside];
self.myCells = [self.myCells arrayByAddingObject:cell];
[self.tableView reloadData]; // will call the delegate again and refresh cells
}
- (void)cellButton1Pressed:(id)sender {
MyCell *cellPressed = nil;
for(MyCell *cell in self.myCells) {
if(cell.button1 == sender) {
cellPressed = cell;
break;
}
}
// do whatever
}
- (void)cellButton2Pressed:(id)sender {
MyCell *cellPressed = nil;
for(MyCell *cell in self.myCells) {
if(cell.button2 == sender) {
cellPressed = cell;
break;
}
}
// do whatever
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.myCells.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
return self.myCells[indexPath.row];
}
#end
I have two table views, one called mainTableViewController (mtvc), the other called detailTableViewController (dtvc). It's very typical click the accessory button on the main tableview cell bring you to the detail tableview kinda thing.
In the prepareForSegue method, the data passed from the main tableview to detail tableview is a NSMutableArray called item.
And this is how I got it displayed: cell.detailTextLabel.text = self.item[indexPath.row];
The cool thing is I managed to do in-place editing on the detail table view cell (overwrote the NSTableViewCell, added a UITextField as subview to each cell).
everything works, the last thing I spent whole day cannot figure out is how do I update the NSMutableArray item after in-place editing taken place, the ultimate goal is in-place editing, and the main tableview data shall reflect the change.
I tried to use delegation and protocol but it does not work (the in-place edited content didn't got passed back, part of the reason is I don't know how to capture the edited content, it's not like it's a text field with a name, I can't just do updatedContent = self.myTextField.text to grab the change)
I'm running out of ideas, any help would be highly appreciated, thanks.
Here's the prepareForSegue in the main tableview controller
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"toInventoryDetail"]) {
NSMutableArray *selectedItem = nil;
if (self.searchDisplayController.active) {
selectedItem = _searchResults[[sender row]];
} else {
selectedItem = _appDelegate.items[[sender row]];
}
UPFInventoryDetailTableViewController *idtvc = segue.destinationViewController;
idtvc.item = selectedItem;
}
}
and here's the cellForRowAtIndex at the detail tableview controller
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UPFEditableUITableViewCell *cell = [[UPFEditableUITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:CellIdentifier];
cell.textLabel.text = _appDelegate.title[indexPath.row];
cell.detailTextLabel.text = self.item[indexPath.row];
[cell showEditingField:YES];
return cell;
}
I wrote the delegation but delete them after cause they didn't work.
I had an idea, still using delegation and protocol obviously: when the 'done' button in the detail tableview hit, I go grab all the row contents and build a new array, using delegation to pass this new array back to the main tableview controller, add this new array into the model meanwhile delete the old one. The tricky thing is still HOW CAN I GRAB ALL THE CONTENTS in the detail tableview?
update:
Haha! I think solved half of the puzzle !
here's the solution for the detail tableview controller
- (IBAction)doneUpdate:(UIBarButtonItem *)sender {
[self.delegate addItem:[self newItem]];
}
- (NSMutableArray *)saveItem
{
NSMutableArray *newItem = [[NSMutableArray alloc] init];
NSArray *indexPathes = [self.tableView indexPathsForVisibleRows];
for (NSIndexPath *indexPath in indexPathes) {
UPFEditableUITableViewCell *cell = (UPFEditableUITableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
[newItem addObject:cell.editField.text];
}
return newItem;
}
and here's the main tableview controller
- (void)addItem:(NSArray *)item
{
//take the updated item then insert the items array as new item
[_appDelegate.items addObject:item];
//remove the selected item (the one being updated) from the items array
[_appDelegate.items removeObject:_appDelegate.selectedItem];
[self.tableView reloadData];
[self.navigationController popViewControllerAnimated:YES];
}
When you creating a cell - give tags to your UITextFields
You can collect data entered by its delegate methods - you can either make NSDictionary/ key value pairs or you can add it to NSArray.
- (void)textFieldDidEndEditing:(UITextField *)textField {
if(textField.tag == 11) {
// you can add it to your desired array/dictionary
}
}
OR
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if(textField.tag == 11) {
// you can add it to your desired array/dictionary
}
}
You can use Delegation/Protocol or store this values in NSUserDefault and get it back on mainViewController.
Do you have a separate data model class(classes) for your selectedItem? That would be the appropriate way to persist data between the two TableViewControllers. It can be Core Data or simply a NSMutableArray that lives in memory. The DetailViewController updates the item and saves the changes, then the mainTableViewController reloads the TableView (or even just the data backing the previously edited cell.
Perhaps even consider the Model-View-Controller-Store pattern promoted by BigNerdRanch.
How can I deselect a cell when returning to a view?
I have an orange down state which is applied to a cell when selected - the cell navigates to a modal view when clicked - when i click back button the cell is still selected.
I have tried applying this code -
[tableView deselectRowAtIndexPath:indexPath animated:YES];
to my cellForRowAtIndexPath method - but it doesn't do anything!
Update - Having done a bit of research - It appears Ive missed some valuable information out of this question! - my table view is a UITableView embedded in a View Controller - not a UITableViewController - so it sounds like it doesnt have the available methods which are required for the suggestions so far..
You could use UITableViewController's clearsSelectionOnViewWillAppear property.
You should not call deselectRowAtIndexPath in cellForRowAtIndexPath method.
you can do this in viewWillAppear
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear: animated];
NSIndexPath *selectedIndexPath = [tableViewObj indexPathForSelectedRow];
if (selectedIndexPath != nil) {
[tableViewObj deselectRowAtIndexPath:selectedIndexPath animated:YES];
}
}
Or you can write in didSelectRowAtIndexPath as well
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath: indexPath animated:YES];
}
This is the right approach
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath: indexPath animated:NO]; // first line in this method
// rest of code
}
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.
I have created a viewbased application, having modal linked viewcontrollers for an Iphone storyboard without a Navigation Controller. For One of my View Controllers i have designed a tableview with dynamic cells:
- (void)viewDidLoad {
[super viewDidLoad];
tabledata =[[NSArray alloc] initWithObjects:#"Data Mining",#"Software Quality",#"Project Management",#"Data Base", nil];
tablesubtitles =[[NSArray alloc] initWithObjects:#"Test parsing and KDD",#"Management of Software Creation Cycle",#"Roles in a Project",#"Database Lifecycle process", nil];
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [tabledata count];
}
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell=nil;
cell= [ tableView dequeueReusableCellWithIdentifier:#"Cell1"];
if (cell == nil) {
cell= [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell1"];
}
cell.textLabel.text= [tabledata objectAtIndex:indexPath.row];
cell.detailTextLabel.text =[tablesubtitles objectAtIndex:indexPath.row];
return cell;
}
Next thing i would like to implement is to link tableview rows to a ViewController which has a couple of textboxes available on it. The big idea is that on selecting a TableItem i want to load a ViewController and populate automatically textboxes based on selected items, also it would be good to have an edit/save possibility.
For Example: I have a Tableview with items
Dodge
Chrysler
Chevrolete
Ford
On pressing "Dodge" a new ViewController appears having multiple TextBoxes and data is filled in with some details like "Since 1900".
On editing the "Since 1900" textbox and pressing Save button, the control returns to viewcontroller with TableView in it.
After pressing "Dodge" the second time previously saved items are displayed.
I've tried a lot of variants, combinations how to achive it, I am new to Xcode and any help would be much appriciated.
Thank you in advance.
It's not hard. Basically you just need to implement didSelectRowAtIndexPath in your UITableView delegate. To see how to do this, create a new project in XCode and make it a MasterDetail application. That gives you the boilerplate code for a table view with selectable rows. The Master view is your table, the Detail view is how you handle the selected row.