I currently have 7 rows in my UITableView and I'm using segues to push in a detail view. However, I currently have two different view controllers, and want something like this:
If row 1 and 2 are selected push in: "showMenuDetail1"
If row 3 through 4 are selected push in: showMenuDetail2"
This is what I currently have: I'm not sure how to exactly assign rows to a specific one.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showMenuDetail1"]) {
NSIndexPath *indexPath = [self.menuTableView indexPathForSelectedRow];
HomeDetailViewController *HDViewController = segue.destinationViewController;
HDViewController.theName = [[menu objectAtIndex:indexPath.row] name];
}
}
Any help would be appreciated! I know it's an if and else statement but I'm not sure how to write out the exact condition. Thanks!
Create two segues. The important idea is to not attach either to the table view cells, instead drag them both from the view controller containing the table to their respective destinations. Give them each an identifier.
Then in tableView:didSelectRowAtIndexPath:, examine the indexPath.row and trigger the appropriate segue.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row < 2) { // or whatever
[self performSegueWithIdentifier:#"SegueForRows0and1" sender:self];
}
// etc...
}
You can still implement prepareForSegue if you need it, including, if you need it, referring to the tableView's indexPathForSelectedRow.
Related
I have an UISearchBar in on top of my TVC. If the search is active it displays another tableView on top of the normal tableView (this is normal). Now i need to get the "searchTableView" in prepareForSegue() because I need to call:
var newIndexPath = table.indexPathForSelectedRow!
and this fails if you search something.
I also can't do the decision in didSelectRowAtIndexPath() because the segue is called to fast because it is 'linked' directly to the UITableViewCell. I also tried to create the segue from the ViewController but this also fails because the segue needs to end on the same ViewController again.
So basically I want to ask if anyone knows how to solve the error in the Code line above will doing a search.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *indexPath;
if (self.searchDisplayController.active)
{
indexPath = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow];
}
else
{
indexPath = [self.tableView indexPathForSelectedRow];
}
// Use the indexPath...
}
So this issue is similar to the one described here UITableView - IndexPath always returns 0 first & takes 2 clicks to pass correct data to view controller however the answer they suggested does not help to fix the problem so I'm asking specifically with my code. This is my prepareForSegue method:
//send to a detail version of each of the chatters allowing a report a user
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"pushToDetail"]){
ChatterDetailViewController *chatterDetail = [segue destinationViewController];
UITableViewCell *cell = (UITableViewCell*)sender;
long indexPathSelected = [self.tableView indexPathForCell:cell].row;
Chatter *detailChatter = [_arrayWithoutME objectAtIndex:indexPathSelected];
NSData *fbImageData = [NSData dataWithContentsOfURL:[NSURL URLWithString: detailChatter.url]];
_detailProfile = [UIImage imageWithData:fbImageData];
chatterDetail.detailProfile = _detailProfile;
}
}
Which obviously looks different from the answer suggested on the other question, however I tried that and it did not change anything. Whenever I click on a tableView cell it completes the segue and I see the the profile picture from the first cell (index 0) and not the cell where it was clicked. I tried using
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
however it didn't pass the image value like I needed and also when I tried to return using an unwind segue it said the navigation tree was corrupted (doesn't happen with the current code). Basically I can't seem to find why selecting any cell returns only the first cell's information.
EDIT:
here is some code from Chatter Detail's view did load method:
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.hidesBackButton = YES;
_detailProfilePicture.contentMode = UIViewContentModeScaleAspectFill;
_detailProfilePicture.image = _detailProfile;
[_detailProfilePicture sizeToFit];
}
Again, it does return a picture, however it is only the picture from cell at index 0.
There is an easier way to extract the indexPath for your selected row in a UITableView:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"pushToDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
// section and row return an int like this:
NSLog(#"index path: section %i, row %i", indexPath.section, indexPath.row);
}
}
However, your code does give the correct row value as long, and a long can indeed be used to get an object from an NSArray. I would put a breakpoint or log message at the end of this method and see if the correct value is actually in your array.
I would also check what value you get in your indexPathSelected - this should reflect the selected row.
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.
I have a UITableView which has 3 rows. the first row calls a phone number when pressed, which is working fine.
I have two other UIViewControllers which I would like to be displayed when the respective row is selected in the UITableView.
Below is my didSelectRow method.
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//deselect row
[tableView deselectRowAtIndexPath:indexPath animated:YES];
switch (indexPath.row)
{
case 0:
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"tel:1800333000"]];
break;
case 1:
break;
case 2:
break;
default:
break;
}
}
As can be seen, I've set up my switch expression. Im trying to figure out how to call the other two UIViews.
Could someone please provide the code required to display these views?
Your question is not exactly clear about what exactly you are doing but I think what you need is to make a segue and specifically a segue from a UITableViewCell.
First thing to do is control-click and drag a connection from your UITableView cell to the other view in the storyboard. This should create a line between the table view cell and the next view with a little circle. Click on that circle to set and edit the segue properties in the properties inspector. Then in the UITableViewController class you need to have a prepare for segue method. Here is some example code that I've written, its got an extra if statement in there because I actually had 2 UITableViews - one from the UITableViewController and one from the searchbarviewcontroller (which was within the UITableViewController).
// In a story board-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
if ([sender isKindOfClass:[UITableViewCell class]]) {
NSIndexPath * SBidxPath = [self.searchDisplayController.searchResultsTableView indexPathForCell:sender];
NSIndexPath * TBidxPath = [self.tableView indexPathForCell:sender];
NSIndexPath * idxPath = [[NSIndexPath alloc] init];
if (!SBidxPath) {
idxPath = TBidxPath;
}
else{
idxPath = SBidxPath;
}
if ([segue.identifier isEqualToString:CELL_PUSH_SEGUE_ID]) {
if ([segue.destinationViewController isKindOfClass:[YTViewController class]]) {
[self prepareYTViewController:segue.destinationViewController withDictionaryEntry:[self.dictionaryEntries objectAtIndex:idxPath.row]];
}
}
}
}
The other thing is that you also need to look into what a NSIndexPath is. Typically the property that you use from that structure is the .row property.
I am just beginning to learn objective-c and programming in general, and I am really stuck on this issue. I am using storyboards to create an app that keeps scores for different types of games. I have a master view controller that is a table view and it has prototype cells with detail labels. There is an AddKeeperViewController that allows the user to input player names and the game type. The game type is then used as the detail label for the prototype cells.
I want to then have each cell push to a different view controller, depending on what the detail text label is. I only have 2 game types at the moment, I know I need to set up logic in tableView didSelectRowAtIndexPath that will choose which segue to take. I set up my segues from the view controller, not the cells, and they each have a unique identifier. I just don't know how to set up my if statements to use the gameType as the deciding factor for which segue to take.
Here is some of my code from my MasterViewController.m file:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"scoreKeeperCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
scoreKeeper *keeperAtIndex = [self.dataController objectInListAtIndex:indexPath.row];
[[cell textLabel]setText:keeperAtIndex.name];
[[cell detailTextLabel] setText:keeperAtIndex.gameType];
return cell;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if(cell.detailTextLabel) {
//statement
}
if ([[segue identifier] isEqualToString:#"ShowKeeperDetails"]) {
MADDetailViewController *detailViewController = [segue destinationViewController];
detailViewController.keeper = [self.dataController objectInListAtIndex:[self.tableView indexPathForSelectedRow].row];
}
}
It seems like you're on the right track. However, I think you're waiting a bit too long to choose which segue to take. Perhaps something like this would work for you:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if ([cell.detailTextLabel.text isEqualToString:#"TEXT"])
{
[self performSegueWithIdentifier:#"segueTypeOne" sender:nil];
}
else
{
[self performSegueWithIdentifier:#"segueTypeTwo" sender:nil];
}
}
You may also create different cell's prototypes, each one connected with a different segue leading to different views, and then use the right prototype to obtain cells in dequeueReusableCellWithIdentifier.