In my DetailViewController.h I've declared the property * type as seen below:
#interface DetailViewController : UIViewController
#property (strong, nonatomic) NSString *type;
#end
Then I got a ListViewController which is a UIViewController containing a TableView.
The ListViewController.h looks like this:
#interface ListViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
#end
In my ListViewController.m I want to change the value of * type a soon I switch the View (view switching is working fine).
I'm doing this by adding the following:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UINavigationController * navigationController = [self.storyboard instantiateViewControllerWithIdentifier:#"DetailController"];
DetailViewController * detailController = [[DetailViewController alloc] init];
detailController.type = #"video";
NSLog(#"Type: %#", detailController.type);
[self.navigationController pushViewController:navigationController animated:YES];
}
When I put a breakpoint at the second line in this block/function I get detailController with in it * type.
When I NSLog this a few lines later it return "(null)".
The View is changed but but the type is not set.
I can't figure out what I'm doing wrong?
Someone who has a solution for this or can point me to something we're this is explained?
Because I tried searching for the answer but I couldn't find anything equivalent to my problem, or I used the wrong search terms..
in your didSelectRowAtIndexPath: method change the following lines`
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
DetailViewController * detailController = [self.storyboard instantiateViewControllerWithIdentifier:#"DetailController"];;
detailController.type = #"video";
NSLog(#"Type: %#", detailController.type);
[self.navigationController pushViewController: detailController animated:YES];
}`
in your storyboard select DetailViewController and give storyboard id as "DetailController". Make sure your ListViewController is embed in UINavigationController, you can do that by selecting ListViewController and go to editor->Embed in->Navigation Controller. Follow these steps and your problem will be solved.
OR you can give segue from your ListViewController to DetailViewController and use
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *path = [self.tableView indexPathForSelectedRow]; //create tableview outlet
[segue.destinationViewController setType:#"video"];
}
What is this for?
UINavigationController * navigationController = [self.storyboard instantiateViewControllerWithIdentifier:#"DetailController"];
Why are you pushing a navigation controller inside a navigation controller?
[self.navigationController pushViewController:navigationController animated:YES];
detailController was not used or displayed.
Try this instead:
[self.navigationController pushViewController:detailController animated:YES];
Look at the last line - you are telling the nav controller to push itself!
It should be like this
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
DetailViewController * detailController = [self.storyboard instantiateViewControllerWithIdentifier:#"DetailController"];
detailController.type = #"video";
NSLog(#"Type: %#", detailController.type);
[self.navigationController pushViewController:detailController animated:YES];
}
Is the navigation controller and the alloc-init method both instantiates the save view controller??
If yes, try this
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
DetailViewController * detailController = [[DetailViewController alloc] init];
detailController.type = #"video";
NSLog(#"Type: %#", detailController.type);
[self.navigationController pushViewController:detailController animated:YES];
}
Related
objective-c and ios newbie here but I'm learning!
name is a string in descs. I'm trying to pass it to a new view controller through detailViewController.note. I can see in debugging that the string is being saved in detailViewController.note ,but when I go over to my other view controller it is not there.
here is the code from my table view controller:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:NO];
DetailViewController *detailViewController = [[DetailViewController alloc]init];
detailViewController.view.frame=self.view.frame;
detailViewController.title=#"detail";
detailViewController.note=[self.descs[indexPath.row]name];
[[self navigationController] pushViewController:detailViewController animated:YES];
}
Header for DetailViewController:
#interface DetailViewController : UIViewController
#property (nonatomic) NSString * note;
#end
Implementation for DetailViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
UITextView *description =[[UITextView alloc]initWithFrame:CGRectMake(12,260,300,180)];
description.font=[UIFont fontWithName:#"Helvetica" size:15];
description.editable=NO;
description.text=self.note;
[self.view addSubview:description];
}
Thanks in advance for your help!
You should set up your subview in viewWillAppear:. It's not working in your current implementation because viewDidLoad is called before detailViewController.note has been set.
I want to pass a value to a property from one class to another.
In LAClaimReportViewController, I have a property
#property (strong, nonatomic) LAClaimReport *claimReport;
I want to pass the value to claimReport property (of LAClaimReportViewController)from below code of masterViewController.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([record.submitted isEqualToNumber:#0] && record != _selectedReport)
{
[self.detailViewController.navigationController popToRootViewControllerAnimated:NO];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
LAClaimReportViewController *claimReportViewController = [storyboard instantiateViewControllerWithIdentifier:#"ClaimReportView"];
[self.detailViewController.navigationController pushViewController:claimReportViewController animated:YES];
//Question: i want to assign record to claimReport of LAClaimReportViewController. how to do that.
}
}
#try this
[claimReportViewController setclaimReport:selectedReport];
So I'm using this code... "MFSideMenuDemoStoryboard"
https://github.com/mikefrederick/MFSideMenu/tree/master/Demos/MFSideMenuDemoStoryboard/MFSideMenuDemoStoryboard
And I want to give a name to each cell in the "SideMenuViewController.m" and make each one of these cells show a specific UIView in the "DemoViewController.m". any help with that ?
any help is appreciated, thank you!!
In SideMenuController
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
DemoViewController *demoController = [[DemoViewController alloc] initWithNibName:#"DemoViewController" bundle:nil];
demoController.title = [NSString stringWithFormat:#"Demo #%d-%d", indexPath.section, indexPath.row];
UINavigationController *navigationController = self.menuContainerViewController.centerViewController;
NSArray *controllers = [NSArray arrayWithObject:demoController];
navigationController.viewControllers = controllers;
[self.menuContainerViewController setMenuState:MFSideMenuStateClosed];
}
Replace this code with something like:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//EDIT #1
UINavigationController *navigationController = self.menuContainerViewController.centerViewController;
DemoViewController *currentCenterView = [[navigationController viewControllers] firstObject];
[currentCenterView addAnotherView:indexPath.row]; //This is where you will tell your 'middle view' to load another UIView ... You can use the indexPath.row to determine which option was tapped and act accordingly.
[self.menuContainerViewController setMenuState:MFSideMenuStateClosed];
}
You obviously need to add a method - (void) addAnotherView:(NSInteger ) row; to your DemoViewController or whatever you've called it.
Edit #1 this will reference the current loaded view. This will only work if you are only using 1 view controller on the navigation controller stack. Otherwise grabbing the first object won't work and you'll need to search for the right index.
In ViewControllerA I have a table with three rows of data right now (three messages). Clicking on a row, I'm trying to push to ViewControllerB with the text from one of the rows (the name). Problem I'm having is I'm trying to do everything programmatically, not using storyboard ids or storyboard segues. Is this possible? Here's what I have.
ViewControllerA m.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
Messages *cell = (Messages *)[tableView cellForRowAtIndexPath:indexPath];
NSString *from = [NSString stringWithFormat:#"%#", cell.from.text];
ViewControllerB *VCB = [[ViewControllerB alloc]init];
VCB.from = from;
[self.navigationController pushViewController:VCB animated:YES];
//using this^ creates a new view controller with a black screen but passes the variable
//[self performSegueWithIdentifier:#"pushMessage" sender:self];
using this^ shows my ViewControllerB perfectly but the value isn't set
}
ViewControllerB h.
#property (strong, nonatomic) NSString *from;
ViewControllerB m.
-(void)viewDidLoad{
NSLog(#"%#", from);
}
Is there a way to do this without initiating a new viewcontrollerB in VCA and getting the right push animation?
I believe I understand what you're asking and you are almost on the right track here. You said that it works when you use [self performSegueWithIdentifier:#"pushMessage" sender:self]; but the value is not set.
So, declare an NSString "from" in your header and in the previous code delete the [self.navigationController pushViewController:CVC animated:YES];
//ViewControllerA.h
#property (copy, nonatomic) NSString *from;
//ViewControllerA.m
#synthesize from;
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Messages *cell = (Messages *)[tableView cellForRowAtIndexPath:indexPath];
from = [NSString stringWithFormat:#"%#", cell.from.text];
[self performSegueWithIdentifier:#"pushMessage" sender:self];
}
Then you need to use the prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
//ViewControllerA.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"pushMessage"]) {
ViewControllerB *vcb = [segue destinationViewController];
[vcb setFrom:[self from]];
}
}
EDIT
Well, I feel like this is a really wrong way to do it. But, this will satisfy all of your conditions asked in the question. I have checked and this does in fact work:
First, Go to your storyboard, click on the View Controller B, click on the identity inspector and change the Storyboard ID. Also click the check that says "Use Storyboard ID".
In your ViewControllerA:
//ViewControllerA.h
- (void)callViewControllerWithCellString:(NSString *)str;
//ViewControllerA.m
- (void)callViewControllerWithCellString:(NSString *)str
{
NSString * storyboardName = #"Main";
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle: nil];
SecondViewController * vc = [storyboard instantiateViewControllerWithIdentifier:#"ViewControllerB"];
[vc setFrom:str];
[[self navigationController] pushViewController:vc animated:YES];
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Messages *cell = (Messages *)[tableView cellForRowAtIndexPath:indexPath];
NSString *from = [NSString stringWithFormat:#"%#", cell.from.text];
[self callViewControllerWithCellString:from];
}
So lets see if i understand this.
You want the text from the clicked row passed to the view controller that you want to present
You want to do this without using storyboards
Sounds simple enough. Check this out.
// I actually recommend this option
// Simply initialize your view controller with the text that you want to pass
ViewControllerB * VCB = [[ViewControllerB alloc] initWithText:from];
// For this to work, you'll have to declare a new init method in your .h file like this
-(void)initWithText:(NSSTring *)text;
// You should also declare an iVar to store your text
NSString * fromText;
// and implement it in your .m file like this
-(void)initWithText:(NSString *)text
{
self = [super init];
if (self)
{
fromText = text;
}
return self;
}
// Or, you could do this.
// In ViewControllerB.h, create a property
#property (nonatomic, strong) NSString * fromText;
// In ViewControllerB.m, synthesize it
#synthesize fromText;
// Then you can pass that property a value like this
ViewControllerB * VCB = [[ViewControllerB alloc] init];
VCB.fromText = from;
Variables shoudl be lower case.
Your ViewControllerB should be displaed. Do you create all view elements in there programatically too? If you don't then you better load it from a XIB or from the storyboard. You can do that without segueing to it.
But I guess it is much easer to overwrite the perpareForSegue: method and set the from property there. That is what perpareForSegue is made for when you need to do some customization programatically while using storyboards for all the easy stuff.
First off, you should check your code. You are pushing a viewController named CVC onto the navigation controller. I assume this is a typo.
Did you override ViewControllerB "initWithNibName:bundle:" as I see you are calling init. How would the ViewController know what nib to load if you didn't?
Also, it is recommended that you call [super viewDidLoad] in your viewDidLoad call.
You can push views programmatically as well.
You seem to do following
ViewControllerB *VCB = [[ViewControllerB alloc]init];
VCB.from = from;
[self.navigationController pushViewController:CVC animated:YES];
Which means you want to push VCB and not CVC. This change should fix your problem.
You can also put a breakpoint in viewDidLoad to verify your new ViewController is loaded.
If you are using storyboard for VCB as well, I think you should do following --
ViewControllerB *VCB = [self.storyboard instantiateViewControllerWithIdentifier:#"VCBIdentifier"];
where VCBIdentifier is the identifier to your ViewControllerB marked in Identity Inspector -> StoryBoardID.
With your current init, I think, it is creating a default viewcontroller which is blank/empty.
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.