I have a UIViewController which has another UIViewController added as a subview:
#import "ListViewController.h"
#interface SearchViewController ()
#end
#synthesize listController;
- (void)viewDidLoad {
[super viewDidLoad];
…
self.listController =[[ListViewController alloc] init];
[self.view insertSubview:listController.view belowSubview:mainTabBar];
self.listController.view.frame = CGRectMake(0, tabHeight, screenWidth, (screenHeight-tabHeight));
}
#end
The ListViewController has a TableView in it. On the click of an item in the TableView, I want to add a UIViewController to the Navigation Controller:
#import “PlaceViewController.h"
#interface ListViewController ()
#end
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
PlaceViewController *vc = [[PlaceViewController alloc] init];
[self.navigationController pushViewController:vc animated:YES];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
#end
This works if the ListViewController is added to the Navigation Controller normally. But, in this case, when the ListViewController is basically "nested" within another, the newly added PlaceViewController is not opening. Is there a way to make this work? Thanks
You should create a parent/child relation between the container and the controller which has its view added. Some informations can be found in the intro of https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/ and more on the internet. Just look at custom container view controller.
Then you can get the navigationController property of your container from your ListViewController using the parentViewController property of ListViewController.
Related
I have a view controller that has two table view controllers as subviews.
When I click one of the cells in the table view controller, I would like it to push to a new view controller. However, it says self.navigationController and self.parentViewController.navigationController are (null).
Does anyone know how I can push a new view from a subview? Thanks!
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
ProductClass *productClass = [arrProducts objectAtIndex:indexPath.row];
ProductSingleViewController *productSingleViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"ProductSingleViewController"];
productSingleViewController.prod_title = productClass.title;
NSLog(#"VC1: %#, VC2: %#", self.navigationController, self.parentViewController.navigationController);
[self.parentViewController.navigationController pushViewController:productSingleViewController animated:YES];
}
One way to handle this is by setting a delegate method inside the UITableViewControllers. Add the delegate method to your parent view controller. It will get triggered whenever someone taps a cell inside the tables. From this delegate method you will be able to push a new view controller onto the stack.
In MyTableViewController.h:
#protocol MyTableDelegate <NSObject>
#required
- (void)selectedCellWithInfo:(NSDictionary *)info
#end
#interface MyTableViewController : UITableViewController {
id <MyTableDelegate> delegate;
}
#property (strong) id delegate;
In MyTableViewController.m:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[delegate selectedCellWithInfo:[myTableDataSource[indexPath.row]];
}
In MyMainViewController.h:
#import "MyTableViewController.h"
#interface MyMainViewController : UIViewController <MyTableDelegate>
In MyMainViewController.m:
- (void)viewDidLoad {
[super viewDidLoad];
myTableViewController.delegate = self;
}
- (void)selectedCellWithInfo:(NSDictionary *)info {
// Do something
}
The navigationController property of a view controller will return a valid navigation controller object only if the view controller is in a navigation controller's navigation stack.
If the PRoductSingleViewController is your rootviewcontroller
1. Drag and drop the navigation controller
2. set the navigation controller as rootviewcontroller.
3. Replace the navigation child Viewcontroller with your PRoductSingleViewController
Or you can manage it in app delegate.m class
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
_productSingleViewController = [[PRoductSingleViewController alloc]initWithNibName:#"PRoductSingleViewController" bundle:nil];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController: _productSingleViewController];
[self.window addSubview:navController.view];
[navController release];
[self.window makeKeyAndVisible];
return YES;
}
What I did:
I am using scrollview to view some UIView(s), with paging enabled.
In last page in subview of scrollView I added a button.
I also wrote a function which is in subclass of UIView ,which is invoked when we press that button.
I want to view storyboard when I press that button.
You need to implement protocol method for custom UIView class.
For example;
#import <UIKit/UIKit.h>
#protocol YourCustomViewDelegate <NSObject>
- (void)buttonTapped;
#end
#interface YourCustomView : UIView
#property (nonatomic, strong) id< YourCustomViewDelegate > delegate;
#end
And the .m file you can call buttonTapped delegate method.
- (void)myCustomClassButtonTapped:(id)sender
{
[self.delegate buttonTapped];
}
For proper work;
set your custom view delegate to self in your custom view controller.
add buttonTapped method to that view controller
And do what you want in that method. Present/Push view controllers like the other answers explained.
Edit
I will try to illustrate very simple usage of protocols, hope it will help you.
#import "MyViewController.h"
#import "YourCustomView.h"
#interface MyViewController ()<YourCustomViewDelegate> // this part is important
#end
#implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
YourCustomView *customView = // initialize your custom view, I suppose that you already did it
customView.delegate = self;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)buttonTapped
{
YOUR_VIEW_CONTROLLER_CLASS *viewCon =[storyboard instantiateViewControllerWithIdentifier:#"mainVC"]; //mainVC is just a example and u will have to replace it with your viewController storyboard id
//Pushing VC
[self.navigationController pushViewController:viewCon animated:YES];
}
Take a look at this
YourViewController *obj=[[YourViewController alloc]init];
UINavigationController *nav=[[UINavigationController alloc]initWithRootViewController:obj];
[[[UIApplication sharedApplication]keyWindow].rootViewController presentViewController:nav animated:YES completion:NULL];
try like this
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
UIViewController* initialView = [storyboard instantiateInitialViewController];
Firstly you need to set a storyboard Id to your ViewController, namely "mainVC". Then you could pus/present it using below code :-
YOUR_VIEW_CONTROLLER_CLASS *viewCon =[storyboard instantiateViewControllerWithIdentifier:#"mainVC"]; //mainVC is just a example and u will have to replace it with your viewController storyboard id
//Pushing VC
[self.navigationController pushViewController:viewCon animated:YES];
//OR presenting VC
[self presentViewController:viewCon animated:YES completion:nil];
Also in case if you want your another storyboard to be initiated then below,
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil]; //Change MainStoryboard with your storyboard id
hope this will help
the way i perceive your question is
->Rightnow your inside you are in UIView class and inside it there is a button by clicking it you want to push or present a ViewController and offcourse from inside your UIView class you cant push or present your view Controller. So
you can do one simple thing first inside action method make a ref(forward declaration) or you can make a whole new object of your ViewController class in which your UIView is present right now , then call a function(which exist in your ViewController) like
//function inside your uiview calss
- (void)btnaction
{
[_objCSInformationViewController pushViewController];
}
// function inside your view controller
-(void)pushViewController
{
yourViewController *objViewController = [[yourViewController alloc]initWithNibName:#"yourViewController" bundle:nil];
[strong.navigationController pushViewController:objViewController animated:YES];
}
I have a ViewController1.h/m and SettingsViewController.h/m. In ViewController1.m I have this:
ViewController1.h
#property (nonatomic, weak) UIView *settingsView;
ViewController1.m
//alloc init _settingsView
SVC = [[SettingsViewController alloc]init];
[self addChildViewController:SVC];
SVC.view.frame = self.view.bounds;
[_settingsView addSubview:SVC.view];
[SVC didMoveToParentViewController:self];
I don't remove ViewController1 from parent view when I add the settings. It's just off to the side and slides in when I'm toggling the settings view with a button. Inside SettingsViewController I have a table and on a row click I want to slide the SettingsViewController back off the screen and perform a selector from ViewController1...that's where my problem is right now. Here's what I try to do:
SettingsViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if (indexPath.section == 0) {
ViewController1 *VC1 = [[ViewController1 alloc]init];
[VC1.settingsView setFrame:CGRectMake(320, 0, VC1.view.frame.size.width, VC1.view.frame.size.height)];
//NSLog(#"%f", VC1.settingsView.center.x);
//tried:
//[VC1 performSelector:#selector(...)]; I have the method declared in VC1.h
}
}
Logging the VC1.settingsView.center.x always gives me 0...when it should be 160. Adding a log statement in VC1 methods when running performSelector DOES log but the rest of the method doesn't run, doesn't slide the SettingsViewController off the screen. Am I not understanding parent/child relationship???
Crap, found the answer after hours of trying...thanks to this
if([self.parentViewController isKindOfClass:[SomeViewController class]]) {
SomeViewController* viewController = (SomeViewController*)self.parentViewController;
[viewController foo];
}
I have a Splitview with a Tabbar with 5 buttons, in the left side.
Every tab has a navigation controller with a tableview.
Some premises:
App is not built with storyboard
I do not understand the architecture of the Apple sample code "MultipleDetailViews.xcodeproj"
Button 5, header file.
#import <UIKit/UIKit.h>
#class DetailViewiPad;
#interface TAB5 : UIViewController <UITableViewDataSource, UITableViewDelegate>
{
DetailViewiPad *detailViewController;
}
#property (nonatomic, strong) IBOutlet DetailViewiPad *detailViewController;
#end
Button 5, implement file.
#implementation TAB5
#synthesize detailViewController;
[some code...]
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if ([key isEqualToString:#"A"]) {
[detailViewController setDetailItem:[websites objectAtIndex:row]];
} else if ([key isEqualToString:#"B"]) {
TableView2 *controller = [[TableView2 alloc] initWithNibName:#"TableView2" bundle:nil];
[self.navigationController pushViewController:controller animated:YES];
}
Option "A" WORKS PERFECT, the Detail View (right side) shows its content!
If you select Option "B" (you navigate to TableView2) and then you select some row:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
DetailViewiPad *detailViewController =
[[DetailViewiPad alloc] initWithNibName:#"DetailViewiPad" bundle:nil];
[detailViewController setDetailItem:[websites objectAtIndex:row]];
DETAIL VIEW NOT RESPOND. If you set a breakpoint you can see that program flow goes to detail view and it passes data but you can not see updates in the view (iPad device).
Repeat: program flow goes to detail view and it passes data but not refresh the screen.
It seems like some problem of connection between new class (TableView2.h, TableView2.m, TableView2.xib) and the detail view. Some kind of delegate problem.
I found it !!!!
Fantastic !!!
Premise: my detail view it calls DetailViewiPad
DetailViewiPad.h
DetailViewiPad.m
DetailViewiPad.xib
That is the key, the magical code:
DetailViewiPad *detailViewController = (DetailViewiPad*)self.splitViewController.delegate;
[detailViewController setDetailItem:[websites objectAtIndex:row]];
It is INCORRECT the sentence:
DetailViewiPad *detailViewController = [[DetailViewiPad alloc] initWithNibName:#"DetailViewiPad" bundle:nil];
The key to obtain the delegation from subsequents Tableviews to detail View (in iPad Splitview) is:
DetailViewiPad *detailViewController = (DetailViewiPad*)self.splitViewController.delegate;
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.