I'm new to iPhone development. I've done a couple of android apps now, so thought I'd try my hand on iPhone.
However, I really find the storyboard navigation to be counter-intuitive. I've created a basic tabbed app that has 2 screens, both of which contain a table view displaying various bits of data. When a user clicks on a cell of a table, I want it to open a new screen. Simple? Apparently not.
I've tried adding a new viewController and ctrl-clicking a table cell in one of my existing tableViews that is linked from the root tabbed view controller to add a seague to the new controller. Clicking a cell has no effect in the simulator.
I tried to link the new controller with a custom class to see if this was where I was falling down but even if I create a new Objective-C class that inherits from a UIViewController it won't appear in the drop-down box.
Unless I'm mis-understanding how the heirachy of view calls is supposed to work?
Any help appreciated!
Edit:
OK, so with the below advice I've managed to get further, but I'm still not quite there. I added navigation controllers for each of my tab view options. Then I ctrl-clicked the cell row to link to my new webview controller, choosing a selection seague set to push. I ctrl-clicked the webview to it's controller to make it an outlet delegate and added the following code to the row:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (_webViewController == nil)
{
self.webViewController = [[[RSSWebViewController alloc] initWithNibName:#"WebViewController" bundle:[NSBundle mainBundle]] autorelease];
}
RSSEntry *entry = [_allEntries objectAtIndex:indexPath.row];
_webViewController.entry = entry;
[self.navigationController pushViewController:_webViewController animated:YES];
}
I also added:
#property (retain, nonatomic) IBOutlet UIWebView *webView;
to the new view controllers header file. When I click a row in the simulator, it throws the following exception:
2013-03-23 17:21:17.241 RSS[1330:c07] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Could not load NIB in bundle: 'NSBundle with name 'WebViewController''.
Thanks for the comments so far btw guys. :)
You need to have a UINavigationController as the view controller that is linked to the tab bar and then you have to link the UINavigationController to your current controller with the table as the root view controller and then link a cell to another view controller with a push segue. So you will have a navigation controller for each tab. The navigation controller is what enables it to "push" otherwise nothing happens because it manages the stack of view controllers as they are pushed and popped and the animations required to do them. I had this issue when I started as well.
Unless your cells are all static, you will probably need to transfer information from the current view controller to the new view controller such as which item was selected so you can tell the next view controller what to display. One way of doing this is having the next view controller be of a custom class with a property: #property (strong, nonatomic) NSString* message; and then in the view controller that has the push segue now can tell the next view controller right before it gets pushed.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"your-segue-name"]) // The identifier in the Storyboard for segue (click on the segue)
{
// Sender will be cell
NSString* message = [NSString stringWithFormat:#"cell %d!", [[self.tableView indexPathForCell:sender] row]];
YourViewControllerClass* destination = [segue destinationViewController];
destination.message = message;
}
}
To do what your suggesting, you will not be ctrl+clicking from cells.
You need to set up UITableViewDelegate.
See https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableViewDelegate_Protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40006942
Then, as mentioned above by Mike D, you need to use didSelectRowAtIndexPath.
This should point you in the right direction. I will check back when I am on my computer (on phone now) and if you've not got your problem solved, I can explain in more depth.
Related
I have a dynamic cell UITableViewController and I want to push a specific View Controller when someone taps a cell. So I'm trying to use the pre-made method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
But when I go into the simulator and tap a cell, this is what I get:
"NSInternalInconsistencyException', reason: 'Could not load NIB in bundle: 'NSBundle [...] with name 'AffichageVotesQuestionDuMomentViewController'.
Within the method, I have this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
AffichageVotesQuestionDuMomentViewController *detailViewController = [[AffichageVotesQuestionDuMomentViewController alloc] initWithNibName:#"AffichageVotesQuestionDuMomentViewController" bundle:nil];
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
}
AffichageVotesQuestionDuMomentViewController is a custom view controller I created myself. I also tried creating another random one and I get the same type of error. If I set the initWithNibName: to nil, it segues to a view controller that has the navigation bar on top, but that's completely black.
Now, I'm a beginning iPhone programmer and have a very poor understanding of nibs, nib names and such. Any help is appreciated. Also, I didn't do any segue in the storyboard or anything, since it seems I'm programmatically creating the view controller and pushing that way. If there's a good way to do it with segue though, I'm okay with that too (but I need to know the row of the tapped cell).
Thank you!
Because you're using IB storyboards you should do something lik this i think:
UIStoryboard* sb = [UIStoryboard storyboardWithName:#"mystoryboard" bundle:nil];
UIViewController* vc = [sb instantiateViewControllerWithIdentifier:#"AffichageVotesQuestionDuMomentViewController"];
... instead of using the "initWithNibName"-initializer which is used for the classical xib-based approach.
I have following problem.
I have created a tab based Application with three Views and Viewcontroller.
FirstView(Start screen stuff), SecondView (Detailpage), ThirdView (Table for listing items).
(Connections from storyboard were set automatically).
In the third view a table is integrated and the content is displayed fine.
Now I would like to call the SecondView , when a row in the table is selected.
I also tried to add a forth View , outside the tabBar Controller, to get the Detailview, but this also did not help.
I have tried several tutorials and forum tips, but cannot get it working.
The class is set to the right ViewController, the identifier is set to detail.
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
DetailViewController *detail = [self.storyboard instantiateViewControllerWithIdentifier:#"detail"];
[self.navigationController pushViewController:detail animated:YES];
detail.label1.text = #"It is working";
}
When clicking on the row, it becomes blue, but nothing happens. No error Message , nothing.
Hope that you can help :-)
OK, I have tried to "optimize" my design.
Still have the tab based Views, but when clicking on a row in the table, a new (not linked in Storyboard) view should appear to display the details of the selected quote.
I have created a view in the storyboard and called it "detailzitat"
I have created a DetailViewController.h/m as UIViewcontroller class
I have set the custom class to DetailViewController
I import the DetailViewController.h in the ThirdViewController.h
I have modified the didSelectRowAtIndexPath method in ThirdViewController.h accordingly.
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
DetailViewController *detailVC = [self.storyboard instantiateViewControllerWithIdentifier:#"detailzitat"];
[self.navigationController pushViewController:detailVC animated:YES];
}
and my DetailViewController.m looks like
#import "DetailViewController.h"
#implementation DetailViewController
#synthesize label1, labeltext;
- (void)viewDidLoad
{
[super viewDidLoad];
labeltext=#"JUHU";
label1.text=labeltext;
}
But again, nothing happens, besides the row gets blue.
I do not understand. If I am using this code in a Non-Storyboard project, it is working.
What am I doing wrong ? Is there any tutorial for this combination within Storyboards ? Have not found one for this approach yet.
Try to learn from the different tutorials on the web, but the biggest problem is, most ones are not for iOS5 and I am not so good to transfer then.
Hope to get some hints :-)
You might want to think about your design. If I am understanding your description correctly, the user will be on the third tab, tap on a row in a table, and then you will be switching them back to the second tab. A navigation controller might be a more natural, less confusing, choice there.
But in any case, something like this will work sometimes:
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
DetailViewController *detailVC = [self.tabBarController.viewControllers objectAtIndex:1];
detailVC.label1.text = #"It is working"; // <- this will not work consistently!
self.tabBarController.selectedViewController = detailVC;
}
The problem with this is that while the user is on that third tab, it's possible that the second tab view controller's view is unloaded (due to memory pressure for example).
It's also possible the user went from tab 1 to tab 3 immediately and therefore the 2nd tab's view isn't even loaded yet at all. (To even test the above code you would have to select tab 2 and then tab 3.)
If the second tab's view hierarchy is not loaded, the label1 property will be nil, and so this will not work. A better strategy would be to create a new #property of type NSString* on the DetailViewController. And set that property instead of trying to set the label1 directly.
Then in your viewWillAppear: for the DetailViewController you can update your labels as needed. At that point of course you can be sure that label1 is loaded and connected to the correct UILabel.
I hope that helps.
I think problem is at self.navigationController. If your view is not inside the navigation controller this will not work. So what you do is create a new navigation controller object there and then use it to show your detail view.
I am new to iphone development. I am developing an iphone application which contains four tabs. I have implemented it using tab bar controller. But now i need to show a login screen without tabs before tab bar controller. I have tried so many methods but didnt get the one i wanted.
Can anyone pls explain how to do this with a code snippet??
Create a new class LoginViewController. When your application launches then add the view to the window. Now when login is successful then remove it from superview and add the MainController.
Create a subclass of UITabBarController (though it's not advised by apple), but for this purposes it should be ok and do this in viewWillAppear
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
BOOL isLogged in = //do something to determine if you're logged in
if(!loggedIn){
LoginViewController *loginViewController = [[LoginViewController alloc] initWithNibName:#"LoginViewControllerNibHere" bundle:nil];
[self presentModalViewController:loginViewController animated:YES]; //or NO if you don't want it animated
[loginViewController release];
}
}
Or add this to a category for UITabBarController and import it in the app delegate or wherever you're using the UITabBarController
Create a new UIViewController subclass with a nib that represents your login screen (I'll refer to it as SignInViewController).
Open your MainWindow.nib file and add a new UIViewController
Set the new UIViewController's class type to SignInViewController
Set the UIWindow's rootViewController outlet to the new SignInViewController
Now create a new nib file and copy your existing UITabBarController to it (it's best to split nibs rather than having a single-mega nib)
Back in your MainWindow.xib change the existing UITabBarController's attributes to specify the nib name that you just created
Check this Link's source code,
it uses Login Controller as modal view with 4 tabs
http://code.google.com/p/tweetero/source/checkout
Also i tried this way ,
in my first tab view - in viewDidAppear - i'll check Login = YES then
show the LoginController
- [self.tabbarcontroller presentMOdalViewcontroller:LoginView animated:YES];
so every time u click on first tab - if u need to Login put a flag - check it & show Login View
Hope this Helps.
The best way to do this is to create a new LoginViewController as other people have mentioned and then set your rootviewController to tabBarcontroller as soon as you authenticate the user. Here is how you can do this in swift, this is snippet to put as soon you authenticate your user in LoginViewController
let tabBarController = self.storyboard?.instantiateViewControllerWithIdentifier("TabBarController") as! UITabBarController
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window?.rootViewController = tabBarController
Where TabBarController is the storyboard id of your tab bar controller. It could be anything whatever name you have given it.
I created a new Navigation Based Application project.Then in MainWindow.xib I added a button to the navigationbar. I would like to push a new View onto the screen where I can enter information, which will be added as an object to the array of the UITableView.
But I don't know where to write the IBAction to link the button to (Appdelegate or the RootViewController)? Because as you see in the screenshot, it resides in MainWindow.xib because the RootViewController is merely a Table and doesn't contain the navigation. But in the document view of MainWindow.xib it is located under the RootViewController.
Do I have to create a new View Controller inside the XIB as well and create an IBOutlet for it?
I tried putting the code inside my AppDelegate and reference the button to the delegate but it doesn't work.
Please help. Thanks in advance.
See the screenshot here: http://i56.tinypic.com/5djbcm.png
When you ask yourself a question "where does this action belong" it's most probably a controller because controllers handle event flows in your app. Next question - "What controller is in charge when this action happens? What controller is most interested in this action?". Answer in your case is root table controller (RootViewController instance). Create an IBAction method in it which will push form controller (one you use to enter information) to navigation controller.
// somewhere in RootViewController.m
- (IBAction)addNewEntry {
NewEntryFormController *c = [[[NewEntryFormController alloc] init] autorelease];
// ...
[self.navigationController pushViewController:c animated:YES];
}
I am developing for iPad and have created a standard UISplitViewController application using the template provided in Xcode - a standard UITableView on the left and a detail view on the right.
I have modified the template so that when a user selects a table cell from the left view it pushes a new table view in it's place (still on the left side). This works without issue, but I would like to be able to update the existing detail view from the new table view - kinda like how Apple's Mail application works.
- I am not trying to create multiple views on the detail view (right hand side) - I've read the documentation and seen the sample code provided by Apple.
I read/followed many tutorials, but can't seem to get this relatively simple view hierarchy to work.
More detail:-
Using detailViewController.detailItem = #"Test"; in the RootView didSelectTableRowAtIndexPath delegate method updates the Detail view label. Using the exact same code in the newly pushed Table View does not update the label - am I missing a reference point or something??
Since posting I've tried to use protocols & delegates to update a label on the detail view. The label updates correctly when changed from the Root View using the new methods, however, when I push a new view onto the root view (left hand side) I can no longer update the label.
At some point after creating the RootViewController (or maybe even in a custom init method) you are setting the delegate for the DetailViewController, its a common mistake that when a new rootViewController is pushed onto the NavController that you forget to set the delgate again.
You probably are creating a new controller in the:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
method and recording/incrementing the level of the new controller before you push it onto the navController. After you create this new controller, set the delegate again.
newRootController.myDelegate = self.myDelegate;
Before you do this If you NSLog the delegate just before you use it, you will probably find its nil.
Try the viewControllers property of your UISplitViewController
#property(nonatomic, copy) NSArray
*viewControllers Discussion The array in this property must contain exactly
two view controllers. The view
controllers are presented
left-to-right in the split view
interface when it is in a landscape
orientation. Thus, the view controller
at index 0 is displayed on the left
side and the view controller at index
1 is displayed on the right side of
the interface.
The first view controller in this
array is typically hidden when the
device is in a portrait orientation.
Assign a delegate object to the
receiver if you want to coordinate the
display of this view controller using
a popover.
Please beware of the detailViewController! You have to pass this instance variable to your new root view. So something like this:
newRootViewController.detailViewController = self.detailViewController
Otherwise your new root view will never know about the detailView. For your new root(table)view you have to do things like:
#import <UIKit/UIKit.h>
#class DetailViewController;
#interface VorhersageTable : UIViewController {
UITableView *vorhersageTableView;
DetailViewController *detailViewController;
}
#property (nonatomic, retain) IBOutlet UITableView *vorhersageTableView;
#property (nonatomic, retain) DetailViewController *detailViewController;
#end
to declare the property of the detailViewController in your new class.
Add this in your RootViewController.didselectRow, before you push the second table (e.g SubRoot)
SubRoot *subController = [[SubRoot alloc] initWithNibName:#"SubRoot" bundle:nil];
subController.detailViewController = self.detailViewController;
And create the SubRoot.h and SubRoot.m similar to RootViewController.
#class DetailViewController;
#interface SubRoot : UITableViewController {
DetailViewController *detailViewController;
}
#property (nonatomic, retain) DetailViewController *detailViewController;
#end
then synthesize detailViewController.
Hope it helps.