Programmatically created segue crashes the app on performSegueWithIdentifier:, I really don't want to use the storyboard though.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
ViewController *viewController = [[ViewController alloc] init];
self.segue = [[UIStoryboardSegue alloc] initWithIdentifier:#"showInfo" source:self destination:viewController];
//change the background color to white
self.view.backgroundColor = [UIColor whiteColor];
//create the table view
UITableView *tableView = [[UITableView alloc] init];
//initialize the data source and the delegate to self - as the methods are going to be specified in this script
tableView.dataSource = self;
tableView.delegate = self;
//register the class for the tableView
[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"cell"];
//now assign the table view to our our viewController's property
self.view = tableView;}
Segues can't be created programmatically. Apple's documentation says:
You do not create segue objects directly. Instead, the storyboard runtime creates them when it must perform a segue between two view controllers.
The initWithIdentifier:source:destination: method is for subclassing purposes.
That said, if you're not using storyboards you don't really need segues anyway. Just instantiate and present the destination view controller when you need to.
Related
I had created a UITableViewController in IB, named MyTableViewController. The xcode generate MyTableViewController.m, MyTableViewController.h and MyTableViewController.xib which contains a tableview in them.
Then I added the generated table view to another view controller by following code,
MyTableViewController *myTableViewController = [[MyTableViewController alloc] init];
UITableView *myTableView = (UITableView *)myTableViewController.view;
myTableView.frame = CGRectMake(190, 190, 500, 160);
myTableView.delegate = myTableViewController;
myTableView.dataSource = myTableViewController;
As a result, delegate methods in MyTableViewController.m won't get called,
e.g. "numberOfSectionsInTableView", "numberOfRowsInSection", "cellForRowAtIndexPath".
Any suggestion on it? Thanks.
You are not loading the XIB file, so I suspect myTableView is nil. Instead of this:
MyTableViewController *myTableViewController = [[MyTableViewController alloc] init];
Try this:
MyTableViewController *myTableViewController = [[MyTableViewController alloc] initWithNibName:#"MyTableViewController" bundle:nil];
Also, you need to tell your view controller that it has a childViewController, and tell your tableViewController that it has a parent VC. Put the following lines around your addSubview:
[self addChildViewController:myTableViewController];
[self.view addSubView:myTableView];
[myTableViewController didMoveToParentViewController:self];
I'm appending code to an existing app to add a modal loginView, the old project does not use xib, and the new view controller does.
The present project load its rootViewController this way
- (void) loadView
{
// this should take up the entire screen...
UIView * view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
view.backgroundColor = [UIColor blueColor];
self.view = view;
sideBar =
[[SideBarView alloc] init];
sideBar.rootViewController = self;
[self.view addSubview:sideBar];
pageView =
[[PageView alloc] initWithTitle:DataEntryTitle
client:client];
pageView.rootViewController = self;
//I added this to instantiate the new view controller
_aLoginView = [[LoginViewController alloc]initWithUser:aUser];
[self.view addSubview:pageView];
}
To launch the modal view controller I'm using this code.
-(void)viewDidAppear:(BOOL)animated{
_aLoginView.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:_aLoginView animated:YES completion:nil];
}
As long as I understand the reason for the modal LoginView appearing blank is that I'm creating a new instance of it, instead of calling the existing xib definition. Does someone know what should I do to reference the xib? The old project does not use storyboard, nor xib files.
Thanks,
With your info I think I'm doing something wrong in LoginViewController.m, but I don't know exactly what.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (id)initWithUser:(verifyUser *)aUser
{
self = [super initWithNibName:#"LoginViewController" bundle:[NSBundle mainBundle]];
if (self) {
// Custom initialization
_UserModel = aUser;
}
return self;
}
You want to create the view controller by loading it from the nib
[[LoginViewController alloc] initWithNib:#"nib file name" bundle:[NSBundle mainBundle]]];
If you want to keep your initWithUser: initializer you should be able to do
self = [super initWithNib:#"nib file name" bundle:[NSBundle mainBundle]];
The files owner of your xib is not set.
Open your xib and select the file owner option in the top left
Then on the right pane set the files owner to your view controller:
FOOTNOTE
As an aside, you don't need to set the frame of your view controller in viewDidLoad. It isn't a ViewControllers responsibility to evaluate its own size. A view controller is merely responsible for maintaining the relationship between the model and the view. It is the responsibility of the container or parent ViewController to set the size.
Happy coding
I'm trying to populate UITextFields inside a UITableViewController with a click of a cell. So, I created an init method like:
- (id)initWithObjetoFormulario:(ObjetoFormularioGerenciador *)umObjeto
{
self = [super init];
if(self){
self.objetoFormulario = umObjeto;
}
return self;
}
In my ViewDidLoad I put the line if(self.objetoFormulario){ and after that link the UITextFields to my Object. I don't think there's anything wrong at this point.
So, my Second TableViewController is a SearchDisplayController, it finds the Data I need and at didSelectRowAtIndexPath I have:
ObjetoFormularioGerenciador *objeto = [[ObjetoFormularioGerenciador alloc] init];
[objeto RecebeArrayComDadosECriaObjetoFormularioGerenciador:_arrayComDados eEmail: [searchResults objectAtIndex:indexPath.row]];
GerenciarSeriesTableViewController *gerenciador = [[GerenciarSeriesTableViewController alloc] initWithObjetoFormulario:objeto];
[self.navigationController pushViewController:gerenciador animated:NO];
As I don't have the actual objects in my cells, I call a method to retrieve the object according to the cell entry, that is working as well, now, it opens a blank TableView when I click the cell. It should go Back to my tableView with static Cells containing UITextFields and populate them.
When I use this code:
ObjetoFormularioGerenciador *objeto = [[ObjetoFormularioGerenciador alloc] init];
[objeto RecebeArrayComDadosECriaObjetoFormularioGerenciador:_arrayComDados eEmail:[searchResults objectAtIndex:indexPath.row]];
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
GerenciarSeriesTableViewController *gerenciador = [sb instantiateViewControllerWithIdentifier:#"GerenciarSeriesTableViewController"];
[self.navigationController pushViewController:gerenciador animated:NO];
It does open the TableView I want, but it doesn't populate the UITextFields as I didn't pass any object.
Thanks.
Yes, you really don't generally have custom init methods for view controllers, but rather rely upon the standard ones, and then once the view controller has been instantiated, you'd just set the property then, e.g.:
ObjetoFormularioGerenciador *objeto = ... // create and configure objeto
GerenciarSeriesTableViewController *gerenciador = [self.storyboard instantiateViewControllerWithIdentifier:#"GerenciarSeriesTableViewController"];
gerenciador.objetoFormulario = objeto;
[self.navigationController pushViewController:gerenciador animated:NO];
You can also have a segue between the two view controllers, give it an identifier, and then call [self performSegueWithIdentifier:...], and then set the objetoFormulario in the prepareForSegue method. But the above technique should work fine, too.
I have seen several related questions, but mine is different in that I want to do it purely in coding, so without storyboards or interface builder.
Basically I get lost in how to properly display the cells in a TableView. I have created a very simple SplitView test application. In it I try to show the same TableView twice: once in the TableViewController and once in the DetailViewController (for a user this would make little sense, but I try to grasp how to get this done).
In the TableViewController, the 3 cells show correctly, but in the DetailView, the TableView shows without the cells. There must be a simple step to have the cells drawn, but all the example cases I find assume you use storyboards or IB
In the SplitView I have:
- (id) init{
self = [super init];
JBDetailViewController *detailVC = [JBDetailViewController controller];
UINavigationController *detailNav = [[UINavigationController alloc] initWithRootViewController:detailVC];
JBTableViewController *rootVC2 = [[JBTableViewController alloc] init];
rootVC2.title = #"A TableVC title";
UINavigationController *rootNav2 = [[UINavigationController alloc] initWithRootViewController:rootVC2];
self.viewControllers = #[rootNav2, detailNav];
self.delegate = detailVC;
self.tabBarItem.title = #"a Tab title";
return self;
}
In the DetailViewer I have:
- (void) loadView {
[super loadView];
self.view.backgroundColor = [[UIColor blueColor] colorWithAlphaComponent:0.85f];
JBTableViewController *rootVC2 = [[JBTableViewController alloc] init];
[rootVC2 loadView];
rootVC2.title = #"Test van Justus";
rootVC2.view.backgroundColor = [[UIColor yellowColor] colorWithAlphaComponent:0.85f];
[self.view addSubview:rootVC2.view];
}
Both use the same JBTableViewController that is very simple and should display 3 cells, which it does in the VC, but not in the DetailView.
What do I do wrong?
I am using a BookController class which is using pagenumbers to keep track of the current view. Currently I am creating each view controller on demand and writing the code programmatically. I would like to access the view controllers that I have created in the StoryBoard (the xib files) so that when I demand a new page it will access a Second view controller I have created.
// Provide a view controller on demand for the given page number
- (id) viewControllerForPage: (int) pageNumber
{
if ((pageNumber < 0) || (pageNumber > 31)) return nil;
if(pageNumber == 0){
//here is where I want to access the entire xib file that the SecondViewController is connected with
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Storyboard" bundle:nil];
SecondViewController *myVC = (SecondViewController *)[storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
myVC = [BookController rotatableViewController];
return myVC;
}
else if(pageNumber == 1){
// Establish a new controller
UIViewController *controller = [BookController rotatableViewController];
// Add a text view
UITextView *textview = [[UITextView alloc] initWithFrame:(CGRect){.size = CGSizeMake(100.0f,100.0f)}];
textview.text = [NSString stringWithFormat:#"This is dedicated to people"];
textview.font = [UIFont fontWithName:#"Futura" size:18.0f];
textview.center = CGPointMake(475.0f, 700.0f);
[controller.view addSubview:textview];
// Add a label
UILabel *textLabel = [[UILabel alloc] initWithFrame:(CGRect){.size = CGSizeMake(200.0f, 200.0f)}];
textLabel.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
textLabel.text = [NSString stringWithFormat:#"1"];
textLabel.font = [UIFont fontWithName:#"Futura" size:18.0f];
textLabel.center = CGPointMake(475.0f, 985.0f);
[controller.view addSubview:textLabel];
// Add it as an image
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"icon#2x.png"]];
imageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
imageView.center = CGPointMake(160.0f, 230.0f);
[controller.view addSubview:imageView];
return controller;
}
Just not sure how to make a call to access that xib file i've created and make it into the first page (page=0). The second page (page =1) is an example of how i have drawn all the other pages in my book programmatically. Thanks!
Remember the Storyboard is just a collection of NIBs which simply instantiate the hierarchy of each view and connect the outlets to the owning view controllers. You do not want to instantiate the Storyboard yourself to just create a single view controller. What that is doing is creating new instances when the application has already been launched and is running with different instances. Even if you did have them wired up they would be wired to instances which are redundant and not the actual instances you want.
What I would do instead is create an individual NIB file for SecondViewController which you will use separately. Then you will need to wire it together. If this code is within the instance you need to access you would simply pass it along to a property on SecondViewController. Or maybe you just pass along values but most likely you will want to set a delegate property and define a protocol for SecondViewController to call back to the instance which created it.
For your code you can simply load the NIB with the following code.
SecondViewController *vc = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
vc.delegate = self;
You just need to define that delegate and possibly any properties you need to give data to the newly created view controller.
Below is an example of a delegate setup which I recently created for a SideBar interface using a Storyboard. I have a container view for the Header VC which is in the Home VC. This Header VC could be like your SecondViewController because I could not connect it in the Storyboard so I did it with code. First I created a delegate property on the Header VC.
#protocol IFHeaderDelegate;
#interface IFHeaderViewController : UIViewController
#property (nonatomic, assign) IBOutlet id<IFHeaderDelegate> delegate;
#end
#protocol IFHeaderDelegate <NSObject>
- (void)headerViewDidToggleSideBar:(IFHeaderViewController *)sender;
#end
Then when a button is tapped I use the delegate for the callback. (Notice I use an NSAssert to verify the delegate is defined just to give me a heads up if I missed it.)
#import "IFHeaderViewController.h"
#interface IFHeaderViewController ()
#end
#implementation IFHeaderViewController
- (IBAction)siderBarButtonTapped:(id)sender {
NSAssert(self.delegate != nil, #"Delegate must be defined!");
if (self.delegate != nil) {
[self.delegate headerViewDidToggleSideBar:self];
}
}
#end
But in order to wire it up I had to set the delegate from the Home VC which I could not do from the Storyboard. What I did was set it in the Home VC when the embed segue was fired in prepareForSegue.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
DebugLog(#"segue.identifier: %#", segue.identifier);
if ([#"HomeHeader" isEqualToString:segue.identifier]) {
NSAssert([segue.destinationViewController isKindOfClass:[IFHeaderViewController class]], #"Destination VC must be the Header VC");
IFHeaderViewController *headerVC = (IFHeaderViewController *)segue.destinationViewController;
headerVC.delegate = self;
}
}
You can find the full project on GitHub: https://github.com/brennanMKE/Interfaces/tree/master/SideBar