I am trying to load a single (storyboard id:ClaimReportView) ViewControllers with diff data depending upon the rows clicked on the table view of MasterVC. But the viewDidload is called only once for the VC and hence if i trying to click on diff rows in table view it doesn't call the viewdidload.
.M file
#interface LAMasterViewController ()
{
NSArray *_claimReports;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
LAClaimReport *record = [_claimReports objectAtIndex:indexPath.row];
if ([record.submitted isEqualToNumber:#0] )
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
self.claimReportViewController = [storyboard instantiateViewControllerWithIdentifier:#"ClaimReportView"];
[self.detailViewController.navigationController setViewControllers:[NSArray arrayWithObject:self.claimReportViewController] animated:YES];
self.claimReportViewController.claimReport = record;
}
}
.h file
#interface LAMasterViewController : UITableViewController
#property (strong, nonatomic) LADetailViewController *detailViewController;
#property (strong,nonatomic) LAClaimReportViewController *claimReportViewController;
#end
What am i missing here? i think i haven't pushed the VC in navigation Stack so ViewDidLoad not called? pls guide.
viewDidLoad is called once when your view is loaded in the first time so there will be no affect in tableView:didSelectRowAtIndexPath: method . Its better you can create your own method and call that method in the tableView:didSelectRowAtIndexPath: method that will work for you.
You can not call viewDidLoad method because it is viewController's life cycle method that can not be called manually, it will be called automatically when view is loaded.
Related
I have the following code having capability such that, once I tap on the table cell it will go to another view controller where it has its description. I am trying to do it programmatically.
This is my code that controls the segue part, which is not getting called:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
DetailViewController *detailVC = (DetailViewController*)[mainStoryboard instantiateViewControllerWithIdentifier: #"detailViewController"];
detailVC.navigationItem.title = self.ourNumberOfQuotes[indexPath];
detailVC.indexRow = [NSString stringWithFormat:#"%#",indexPath];
[self.navigationController pushViewController:detailVC animated:YES];
}
Why is this not getting called?
This is my .h file code
#interface QuoteTableViewController : UITableViewController <UIGestureRecognizerDelegate,UITableViewDataSource,UITableViewDelegate> {
int i;
}
#property (strong, nonatomic) IBOutlet UIView *mainMenu;
#property (strong, nonatomic) IBOutlet UIButton *homeworkButton;
#property (strong, nonatomic) IBOutlet UIButton *uploadButton;
#property (strong, nonatomic) NSArray *arrayOfOurQuotes;
#property (strong, nonatomic) NSMutableArray *ourNumberOfQuotes;
From -didSelectRowAtIndexPath: not being called:
I have encountered two things in this situations.
You may have forgot to implement UITableViewDelegate protocol, or
there's no delegation outlet between your class and your table view.
You might have a UIView inside your row that is a first responder and
takes your clicks away. Say a UIButton or something similar.
You should check to make sure you have implemented the UITableViewDelegate protocol and you need to make sure that you are clicking the cell and not a view or gesture recognizer that is in front of it.
Are you switching to a different storyboard that your current viewController is already using? If it's the same, please change the code within your method (see below) - This line here UIStoryboard... isn't needed.
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
isn't needed. You can call self.storyboard
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
DetailViewController *detailVC = [self.storyboard instantiateViewControllerWithIdentifier:#"detailViewController"];
detailVC.navigationItem.title = self.ourNumberOfQuotes[indexPath];
detailVC.indexRow = [NSString stringWithFormat:#"%#",indexPath];
[self.navigationController pushViewController:detailVC animated:YES];
}
Providing the detailVC has been set with the storyboard identifier #"detailViewController" of course.
Okay here is the problem, when I commented out your gesture codes from your viewDidLoad method, the delegate method, didSelectRowAtIndexPath gets called.
Here is what I figured, either the swipe gesture works and cell selection doesn't or if you remove the gesture part, the selection work and didSelectRowAtIndexPath gets called. As I never worked with gesture, I am not sure how to make both actions work together.
Hope this helps a little.
I have followed the tutorial Passing Data between View Controllers section Passing Data Forward. My code is:
MasterViewController.h:
-(void)pushViewController: (UIViewController *)detailVC animated:(BOOL)animated;
MasterViewController.m:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell=[tableView cellForRowAtIndexPath:indexPath];
NSInteger num = indexPath.row;
DetailViewController *detailVC = [[DetailViewController alloc] initWithNumber:indexPath.row];
detailVC.number = num;
[self pushViewController:detailVC animated:YES];
}
I want to pass the integer num to my DetailViewController. But I am getting Thread 1: SIGABRT. What is happening?
MasterViewController doesn't inherit from UINavigationController.
You can't push to the navigation stack if there isn't one.
You can either add one, or present the new VC, depending upon what the design of your GUI is intended to be.
P.S.
WHat's the point in setting the number twice as you are:
NSInteger num = indexPath.row;
DetailViewController *detailVC = [[DetailViewController alloc] initWithNumber:indexPath.row];
detailVC.number = num;
You either set it with init or set it directly, you're doing it both ways
My best guess (without seeing your #interface for DetailViewController) is that it's simply a matter of the detailVC instance going away after you put it on screen.
Simple solution: use an instance variable instead:
#interface WhateverClassYouAreIn
#property (nonatomic, strong) DetailViewController *detailVC
#end
// ...
self.detailVC = [[DetailViewController alloc] initWithNumber:indexPath.row];
The reason this is happening is that without the instance variable, the storage duration of the detailVC is the scope of the method in which it is declared. In general, any VC that goes on screen should be held 'strongly' by something else, all the way back to the root vc.
Is the method "initWithNumber" defined in DetailViewController.h ?
#interface DetailViewController : UIViewController
- (instancetype)initWithNumber:(NSInteger)number;
#end
I have two views a levelComplete view and a levelSelector view. What I would like to do is when the levelComplete shows or the ViewDidLoad occurs on that view I would like to send a delegate to the level selector to show a button in the view and then make that button UserInteractionEnabled so then I will then be able to programme that button to do something if its not hidden.
You want to necessarily do it via a delegate. Coz you can do it in a simpler way as well. When you call your secondView, just tell your button to hide. So your modified code to calling the second view controller becomes:
-(IBAction)passdata:(id)sender {
secondview *second = [[secondview alloc] initWithNibName:nil bundle:nil];
self.secondviewData = second;
sender.hidden=YES;
secondviewData.passedValue = textfield.text;
[self presentModalViewController:second animated:YES];
}
And then you can set it to visible when your view loads up again using viewDidLoad. I can tell you how to do it via delegates if you need. Lemme know what works best.
EDIT - Solution by Delegates
Your secondView's Header file will be as follows:
#protocol SecondViewHandlerDelegate <NSObject>
- (void)viewHasBeenLoaded:(BOOL)success;
#end
#interface secondview :UIViewController {
IBOutlet UILabel *label;
NSString *passedValue;
}
#property (nonatomic, retain)NSString *passedValue;
-(IBAction)back:(id)sender;
#end
Then, in the implementation file of secondView(.m), synthesise the delegate first by #synthesize delegate; . After this, in your viewDidLoad of secondView, add the following line:
[[self delegate] viewHasBeenLoaded:YES];
That should be enough for your secondView. Now onto the firstViewController, perform the following steps:
In the header file (.h), import your second view and implement the protocol:
#interface ViewController :UIViewController <SecondViewHandlerDelegate>{
..
..
}
In the implementation file (.m) of your firstViewController, implement this method:
- (void)viewHasBeenLoaded:(BOOL)success
{
NSLog("Delegate Method Called");
[myButton setHidden:YES];
}
And finally, in your code when you call the secondView, add this line:
secondview *second = [[secondview alloc] initWithNibName:nil bundle:nil];
second.delegate = self;
...
That should solve your purpose. I'd appreciate if you could mark the answer as correct as well. Thanks :)
I have a HomeViewController which has a tableView populated with the array tableViewArray (originally empty). When I tap on a barButton, I segue modally to another View Controller called OutsideViewController which has another tableView populated by a different array.
What I would like to do is the following:
When I tap on a row in my OutsideViewController, I would like to add the selected string value to the tableViewArray so that when I go back to HomeViewController, the tableView has that new item listed in the tableView.
So far, this is what I have tried:
In the -didSelectRowAtIndexPath method of my OutsideViewController.m I have this piece of code:
NSString *selectedRow = [outsideArray objectAtIndex:indexPath.row];
NSMutableArray *temporaryArray = [NSMutableArray arrayWithObject:selectedRow];
HomeViewController *homeVC = [[HomeViewController alloc] init];
homeVC.tableViewArray = temporaryArray;
That code works but the tableView in HomeViewController is still empty when I return. Do I have to reload the tableView data?
Am I doing this right?
This is how I have set up my View Controllers in Storyboard:
HomeViewController -(modal segue)-> Navigation Controller --> OutsideViewController
Also, the return from OutsideViewController to HomeViewController is done by this line of code:
[self dismissViewControllerAnimated:YES completion:^{ }];
What you're doing wrong is you're allocationg a new HomeViewController. What I would do is keeep a reference to your HomeViewController in your OutsideViewController. Here is how.
First, in OutsideViewController.h, create a property, like this :
#property (nonatomic, weak) HomeViewController *homeVC;
Don't forget to add #class HomeViewController; in your .h, and #import "HomeViewController.h" in your .m
In HomeViewController, implement the prepareForSegue: method like this (replace ModalSegueIdentifier with your segue's identifier) :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"ModalSegueIdentifier"]) {
OutsideViewController *modalVC = (OutsideViewController*)segue.destinationViewController;
modalVC.homeVC = self;
}
}
Then, in OutsideViewController.m, instead of doing :
HomeViewController *homeVC = [[HomeViewController alloc] init];
homeVC.tableViewArray = temporaryArray;
Do this :
_homeVC.tableViewArray = temporaryArray;
When you leave your modal VC, your HomeVC will have the correct array. Don't forget to refresh your UITableView !
NB: Of course, there are many other ways, and it's maybe not the best one. But still, it should work.
You can achieve this too using delegation. You have to create a protocol in your OutsideViewController with a method that is responsible for sending the new object to your HomeViewController. Do this in OutsideViewController.h:
#protocol OutsideViewDelegate <NSObject>
- (void)OutsideViewController:(OutsideViewController *)controller didAddObject:(NSString *)object;
#end
In the implementation file you have to change a little bit the didSelectRowAtIndexPath: method:
NSString *selectedRow = [outsideArray objectAtIndex:indexPath.row];
[self.delegate OutsideViewController:self didAddObject:selectedRow];
In your HomeViewController.h you have to make your class conforms to the protocol:
#interface HomeViewController : UIViewController <OutsideViewDelegate>
After, create a property for the delegate:
#property (nonatomic, weak) id <OutsideViewDelegate> delegate;
To finish the process, implement the protocol in your HomeViewController.m to receive the new object from the OutsideViewController:
- (void)OutsideViewController:(OutsideViewController *)controller didAddObject:(NSString *)object
{
if (object != nil)
{
[self.tableViewArray addObject:object];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
The code above depends of if your tableViewArray object is mutable or not. If it's not, you can change the type of the object argument in the protocol method to an inmutable array object and just assign tableViewArray to the new array.
EDIT:
In the prepareForSegue: method don't forget to set the delegate:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"SEGUE_IDENTIFIER"]) {
OutsideViewController *outsideVC = (OutsideViewController *)[segue destinationViewController];
[outsideVC setDelegate:self];
}
}
First of all make sure you alloc, init your tableViewArray in HomeViewController
Second , In this line
HomeViewController *homeVC = [[HomeViewController alloc] init]
you are creating a new reference to your HomeViewController which is not correct, you need to pass correct reference, possibly creating HomeViewController variable in your OutsideViewController
Even though you correctly do first and second suggestion you will still see an empty tableview because you dont reload the tableview, somehow you need to fire [self.tableview reloadData]; method.
That means; you need to learn Delegate or NSNotifications pattern to communicate between child->parent scenarios
How do I set up a simple delegate to communicate between two view controllers?
http://mobile.tutsplus.com/tutorials/iphone/ios-sdk_nsnotificationcenter/
For your question just create a delegate in your Outside;
in your OutsideViewController.h
#import <UIKit/UIKit.h>
#protocol OutsideDelegate;
#interface{}//bunch of interface stuff
// Declare a property for the delegate
#property (weak) id <OutsideDelegate> delegate;
#end
// Protocol Header
#protocol OutsideDelegate <NSObject>
#optional
- (void)dismissPop:(NSMutableArray *)list;
#end
in your OutsideViewController.m
#synthesize delegate;
//run delegate method
[delegate dismissPop:temporaryArray];
[self dismissViewControllerAnimated:YES completion:^{ }];
in your HomeViewController.h
#import "OutsideViewController.h"
#interface OutsideViewController : UITableViewController<OutsideDelegate>
{}
#property (strong, nonatomic) OutsideViewController *pvc;
in your HomeViewController.m
#synthesize pvc;
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"your segue"]) {
pvc = [segue destinationViewController];
[pvc setDelegate:self];
}
}
// delegate callback function
- (void)dismissPop:(NSMutableArray *)list {
self.tableViewArray=list;
[self.tableView reloadData];
}
Another Solution Would be
Change your view stack to this:
Navigation Controller --> HomeViewController -(push segue)--> OutsideViewController
and apply rdurand's answer
and add this to your HomeViewController :
-(void)viewDidAppear:(BOOL)animated
{
[self.tableview reloadData];
}
In this solution since you are just push-pop viewcontrollers in a nabigation stack viewDidAppear will be called in HomeViewController everytime when you pop OutsideViewController.
I want to change the selected row in the table (in MasterViewController) from a button on a detailViewController.
I know that I cannot call didSelectRowAtIndexPath because it is a delegate method. I tried using selectRowAtIndexPath but that does not invoke the didSelectRowAtIndexPath nor the willSelectRowAtIndexPath method. As suggsted here,
https://stackoverflow.com/a/7496926/1050722 it is possible to some new method and then call that method from the other ViewController (detailViewController), but how should that method look like?
Here is some code:
MasterViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self pushDetailViewController:indexPath];
}
-(void)pushDetailViewController:(NSIndexPath *)indexPath
{
if (!self.detailViewController) {
self.detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
}
[self.navigationController pushViewController:self.detailViewController animated:YES];
NSLog(#"pushDetailviewC and index %#", indexPath);
}
DetailViewController.m
-(IBAction)nextItem
{
MasterViewController *mVc = [[MasterViewController alloc] init];
[mVc pushDetailViewController:[NSIndexPath indexPathForRow:0 inSection:0]];
}
Can someone give me an example to fix this? Thanks.
EDIT: Here is the new code
MasterViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self pushDetailViewController:indexPath];
}
-(void)pushDetailViewController:(NSIndexPath *)indexPath{
if (!self.detailViewController) {
self.detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
}
self.detailViewController.mVc = self;
[self.navigationController pushViewController:self.detailViewController animated:YES];
NSLog(#"pushDetailviewC and index %#", indexPath);
}
DetailViewController.h
#import <UIKit/UIKit.h>
#import "MasterViewController.h"
#interface DetailViewController : UIViewController <UISplitViewControllerDelegate, UITableViewDelegate>
#property (strong, nonatomic) id detailItem;
#property (strong, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
#property (nonatomic, retain) MasterViewController *mVc;
DetailViewCOntroller.m
-(IBAction)test{
//MasterViewController *mVc = [[MasterViewController alloc] init];
[self.mVc pushDetailViewController:[NSIndexPath indexPathForRow:0 inSection:0]];
}
It does not call the NSLog part of the method and it is not selecting the row...
This answer is iPad specific as per your question.
The iPad version of the Master/Detail template uses a UISplitViewController two split the screen as you see it. This UISplitViewController instance keeps references to both of your navigations controllers. Which keep reference to your topmost view controllers. Here is sample code, do not use it verbatim except to test since it contains no error checking.
// Picking some arbitrary row
NSUInteger desiredRow = 3;//???
// The Master navigation controller is at index zero in the template, you can check in your app delegate's didFinishLaunching: method
UINavigationController *masterController = [self.splitViewController.viewControllers objectAtIndex:0];
// Next we want a reference to the topmost viewController again you shouldn't assume it's a UITableViewController
UITableViewController *tableController = (UITableViewController *)masterController.visibleViewController;
// Next we get the reference to the tableview in-question
UITableView *tableView = tableController.tableView;
// We translate our desired row to an index path
NSIndexPath *path = [NSIndexPath indexPathForRow:desiredRow inSection:0];
// We select the row , again this will not call didSelectRowAtIndexPath:
[tableView selectRowAtIndexPath:path animated:YES scrollPosition:UITableViewScrollPositionNone];
// We call didSelectRowAtIndexPath: on the tableview's delegate
[tableView.delegate tableView:tableView didSelectRowAtIndexPath:path];
When I put this code in an IBAction method in the detail view controller the linked button performs the appropriate actions, both selects the row and pushes a VC, as if I had selected the row by touch.
In DetailViewController.h add a property to the object that points to the MasterViewController. You will also need to import the MasterViewController.h so something like
#import "MasterViewController"
...
#property (nonatomic, retain) MasterViewController *mVc;
...
Don't forget in DetailViewController.m to #synthesize mVc;
Now in your pushDetailViewController you add a reference to join the two objects
if (!self.detailViewController) {
self.detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
}
[self.detailViewController.mVc = self]; // New line
[self.navigationController pushViewController:self.detailViewController animated:YES];
Then in the DetailViewController you reference the object
-(IBAction)nextItem{
[self.mVc pushDetailViewController:[NSIndexPath indexPathForRow:0 inSection:0]];
}
I think another issue you were having was alloc'ing a new version of mVC each time you click the nextItem button. IOS will happily let you make lots of MasterViewController objects and makes a new one each time you alloc it. You wanted just to get a handle to your original MasterViewController.
Another approach would be to explore using the parent methods to reference the MasterViewController. But I wanted to show you how to do this with an explicit property since I thought it would be clearer.
Also, this may or may not fix all of your issues, but it hopefully at least shows you how to talk back to the actual MasterViewController.