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.
Related
I had a UIViewController and a UIContainer View.
I want to simulate the UIAlertView effect, But I don't know how to build the Container View show and hide about the great method.
I had put the UITableView in my UIContainer View, I use delegate to send selected item to the UIViewController(ParentViewController) and show in the label.( segue name with alertSegue)
There have a show button in the ParentViewController,and I need click the selected item ,it will close(hide/dismiss?) the UIContainer View.
Now UIContainer View default is hidden,and storybaord screen shot like below:
My ParentViewController.h
#interface ViewController : UIViewController<ContainerViewBLETableDelegate>
#property (weak, nonatomic) IBOutlet UIButton *btn;
- (IBAction)btnAction:(id)sender;
#property (weak, nonatomic) IBOutlet UILabel *lb;
#property (weak, nonatomic) IBOutlet UIView *containerView;
-(IBAction)unwindSegue:(UIStoryboardSegue *)segue;
#end
.m file:
- (void)viewDidLoad {
[super viewDidLoad];
}
- (IBAction)btnAction:(id)sender {
_containerView.hidden = NO;
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if( [segue.identifier isEqualToString:#"alertSegue"])
{
ContainerViewBLETable *vc = segue.destinationViewController;
vc.delegate = self;
}
}
-(void) ContainerViewBLETable:(ContainerViewBLETable *)vc andSelectedDone:(NSString *)selectedStr
{
self.lb.text = selectedStr;
vc.view.hidden = YES;
}
-(IBAction)unwindSegue:(UIStoryboardSegue *)segue
{
}
Container ViewController .h :
#class ContainerViewBLETable;
#protocol ContainerViewBLETableDelegate <NSObject>
-(void) ContainerViewBLETable:(ContainerViewBLETable*)vc andSelectedDone:(NSString*)selectedStr;
#end
#interface ContainerViewBLETable : UIViewController<UITableViewDataSource,UITableViewDelegate>
#property(nonatomic,assign) id<ContainerViewBLETableDelegate>delegate;
#property (weak, nonatomic) IBOutlet UITableView *tableVW;
#end
UIContainerView .m part:
.....
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.delegate ContainerViewBLETable:self andSelectedDone:[NSString stringWithFormat:#"%ld",indexPath.row]];
}
.....
Although this code can pass the value to the UIViewController(ParentViewController),and the UIContainer View was hidden,but when I click the show button again , it was not any effect(I feel the container view was overlap lead to touch lose efficacy).
( by the way, I had try
[self.parentViewController dismissViewControllerAnimated:YES completion:nil];
it's not effect )
Question:
How to hide the container view use the delegate and that can touch the show button show container again?
And What great method to control the UIContainer view show and disappear in ParentViewController and UIContainerView ?
I really thank everyone and you can help me and better than better.
containerView and show button are in different viewControllers. So you can't just hide it. The simple way to achieve it is when you select the cell in containerView, present the ParentViewController, and the view in ContainerViewController will dismiss automatically.
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.delegate ContainerViewBLETable:self andSelectedDone:[NSString stringWithFormat:#"%ld",indexPath.row]];
ParentViewController *vc = [[self storyboard] instantiateViewControllerWithIdentifier:#"ParentViewController"];
[self presentViewController:vc animated:YES completion:nil];
}
To show the container, you can just dismiss the ParentViewController that presenting.
1.
- (IBAction)btnAction:(id)sender
{
[self.presentingViewController dismissModalViewControllerAnimated:YES];
}
Or
2. Set up segue to dismiss viewController and use prepareForSegue for delegate to communicate with other viewController.
Also REMOVE vc.view.hidden = YES in delegate you had implemented
-(void) ContainerViewBLETable:(ContainerViewBLETable *)vc andSelectedDone:(NSString *)selectedStr.
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.
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 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.