I have this scenario:
I have a navigation controller within a view controller with table view and varius cells.
When cell is tapped, I go to another view controller (with "TEST" label text for testing).
My problem is this:
My app can be able to open from URL and in its query string there is a param that indicate which controller have to open (that with the table view or that with label).
I cant to find a pattern for to achive this in "clean" way.
For example:
I receive an URL with query param that indicate that I have to open the controller with label.
How can I organize the code to indicate that you must first go to the controller with the table and simulate the cell tap then go to the final controller?
Every navigation controller has array of view controllers. In case that you need to add both view controllers you can use next solution:
NSMutableArray *controllers = [self.navigationController.viewControllers mutableCopy];
[controllers addObject:tableVC];
[controllers addObject:labelVC];
[self.navigationController setViewControllers:controllers animated:YES];
This will animate labelVC without the tableVc becoming visible in the process. When the user press the back button, he will be returned to the tableVC
Related
I would like to pass some data from the last Table View to the First View. How can I do grab a hold of the First View object? I'm familiar with delegate pattern.
From the First View, I'm using Style Modal to invoke the Table View.
Create an object to manage your data model outside of your controller structure (singleton or owned by the application delegate). Update it when you have new data and read from it when you want to display something. Then, instead of having to make links between controllers, all you need to do is remove the one or ones you don't want and let the one you go back to decide what to show.
to get your rootViewController of your navigation controller, you can try this
NSArray *viewControllers = self.navigationController.viewControllers;
YourRootViewController *rootViewController = (YourRootViewController *)[viewControllers objectAtIndex:viewControllers.count - 2];
and add your data to rootViewController
I have an application that has a login page. It then moves to a navigation controller that has a collection view as its root view controller. When the app starts and there is only one item in the collection, I want to have the navigation controller automatically push to that item and allow the user to use 'back' to view the collection. This is the same behavior that is in 'Notes' from Apple.
The idea is to allow the user to immediately start to use the app and only 'discover' the need for the collection view after using the app for some time.
I am using IB, storyboards, and segues for my view transitions.
If I programmatically have the root view controller do a performSegue in its viewWillLoad: I get an error about causing a transition while a transition is still in process.
If I move the code calling performSegue into the didLoad, then the user sees a double transition.
A navigation controller usually manages the underlying stack itself. That is, you pass it an initial view controller, and then through segues or pushViewController:animated: the stack is altered. However, there is nothing stopping you from manually altering the stack. In prepareForSegue:sender:, you can check the number of items, and if it's 1, do something like this:
UIViewController *singleItemViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"SingleItem"];
NSMutableArray *viewControllers = [sender.destinationViewController.viewControllers mutableCopy];
[viewControllers addObject:singleItemViewController];
sender.destinationViewController.viewControllers = viewControllers;
Now instead of starting at the first view controller of the stack, you start at the second one, with a back button to navigate back.
Actually, I found that the following worked the best for me.
if (/* reason to change back list*/) {
UIViewController * rootViewController = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"backViewController"];
NSArray * viewControllers = #[rootViewController, self];
[self.navigationController setViewControllers:viewControllers animated:NO];
}
setViewControllers seems to be the best way to manipulate the stack.
I'm currently writing an app with multiple users.
I would like to use the same "Profile" view in storyboard to display info for all the users.
The plan was to set the title of the "Profile" view and then push it.
- (void) pushGeneralProfileViewFrom:(UIViewController *)target usernameAsTitleOfView:(NSString *)title {
UIViewController *myView = [target.storyboard instantiateViewControllerWithIdentifier:#"GeneralProfileView"]; // created in storyboard, Feed View
myView.title = title;
[target.navigationController pushViewController:myView animated:YES];
}
This worked great. However, the "profile" view also has buttons that lead it to other views in the storyboard. Those views also display specific info for the relevant user.
I was planning to use the name of the backBarButtonItem to know the user I need to display the info for. While I can see the back button with the username in the simulator, I'm unable to get it's self.navigationController.navigationItem.backBarButtonItem.title or self.navigationItem.backBarButtonItem.title. Both return null.
What am I doing wrong?
Also, I have a feeling this is not the best practice to handle those kind of things. I've searched the web and so far haven't seen a better way to pass values to a view I'm pushing. Suggestions?
Thank you.
The backBarButtonItem that you see when your view controller is visible in the navigation controller interface is not the backBarButtonItem of this view controller. It is the backBarButtonItem of the second view controller, the one behind this one in the stack.
So what you want is this:
UIInteger c = [self.navigationController.viewControllers count];
UIViewController* vc2 = [self.navigationController.viewControllers objectAtIndex:c-2];
Now get the backBarButtonItem of the navigationItem of that view controller. Or just get its title if they are the same.
I have a view controller which displays a carousel control (iCarousel). The view is rendered correctly and the carousel is displayed. Right after that a modal is displayed which allows the user to agree to certain terms. I want that once they agree I refresh the viewcontroller which contains the carousel control. Basically, I want to rotate the carousel to some random index.
- (IBAction)accept:(id)sender
{
NewsViewController *newsViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"NewsStoryboard"];
[newsViewController loadNews];
[newsViewController.view setNeedsDisplay];
[self dismissViewControllerAnimated:YES completion:nil];
}
The above code does call the loadNews and fetches it but the view is never refreshed.
What happens to the carousel should really be up to the view controller that manages it, not the modal view controller. Make the modal controller do its thing and return whatever data it collects to its parent. The parent (in this case, the carousel's controller) can then look at that data and decide what it needs to do next (refresh, for example).
The problem is this line:
NewsViewController *newsViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"NewsStoryboard"];
That is not the old view controller; it is a new, unused copy of that view controller. You need to create a line of communication from the modal view controller back to the existing view controller.
The typical way to do this is through a delegate, which you set when creating the modal view controller. If you look at the Xcode Utility template, you will see that it illustrates this architecture. The original view controller sets itself as the modal view controller's delegate, and the modal view controller is thus able to talk back to the original view controller as it is dismissed.
This is such an important thing to be able to do that I talk about it at length in my book:
http://www.apeth.com/iOSBook/ch19.html#_presented_view_controller
I'm having a problem getting a UISearchDisplay's text value to be set programatically on load of the view by another view and my question is have I overcomplicated my problem and missed something or am I on the right track of thinking.
Here's the situation: I have a UITabBarController as my root view, there are 2 tabs, both have a UINavigationController setup so I can push views as needed.
Tab 1 has a UITableViewController which is populated with a list of categories.
Tab 2 has a MapView in it's main view but I have done a custom UINavigationItem view to put various buttons and a UISearchDisplay on the rightBarButtonitem area.
The mapview layout and custom navigation item are stored in the same nib as two separate view objects. In Tab 2's viewDidLoad(), I initialise the rightBarButtonItem programatically with:
UIBarButtonItem *btnItem = [[UIBarButtonItem alloc] initWithCustomView:buttonBar];
self.navigationItem.rightBarButtonItem = btnItem;
[btnItem release];
Everything fires up, buttonBar is wired up to an IBOutlet as searchWhat and I can talk to this object from within the mapview's controller class.
If the user is in Tab 1 and taps a cell, I want it to switch to Tab 2 and populate the searchWhat.text and then execute the search code as if someone had typed in the search themselves.
What i'm having trouble with is the order of load and populate on a view.
I can access the 2nd tab from the 1st without any problem and get it to appear with something like:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"Quick Category cell tapped at row %d", indexPath.row);
self.tabBarController.selectedIndex = 1; // change to the search view controller
//[self.tabBarController.selectedViewController viewDidAppear:YES];
UINavigationController *nav = (UINavigationController *)self.tabBarController.selectedViewController;
SearchViewController *srch = [nav.viewControllers objectAtIndex:0];
//NSLog(#"%#", [srch description]);
[srch queueSearchByType:kSearchTypeQuickCategories withData:[catList objectAtIndex:indexPath.row]];
[srch viewDidAppear:YES];
}
Don't worry about catList and SearchViewController, they exist and this bit works to switch tabs.
Here's the problem though, if the user starts the application and selects an item in tab 1, tab 2 appears but the values of the search display text don't get set - because viewDidLoad and viewDidAppear are called in another thread so the execution of queueSearchByType:withData: gets called while the view is still loading and setting up.
If the user selects tab 2 (therefore initialising the subview) and then selects tab 1 and an item, it can populate the search display text.
I can't just change the order of the tabs so that tab2 is first and therefore loads it's subviews to the navigation bar as the project specification is category search first.
Have I missed something very simple? What I need to do is wait for the second tab to fully appear before calling queueSearchByType:withData: - is there a way to do this?
At the moment, i've implemented a queue the search, check for a queue search approach, this seems to be a bit long winded.
Ok, I don't like answering my own question but it appears my fears were right, basically if you want a UINavigationItem that is a custom view (ie, to put a search bar and various other buttons up on the nav controller) and be able to switch to and populate them from another tab on a tab bar controller, then you need to put the subview in it's own class which is a subclass of UIViewController and then make delegation your friend (which it already is), i've provided an example in case anybody needs to repeat it which i've put on my blog HERE.
http://www.jamesrbrindle.com/developer/ios-developer/howto-add-a-custom-uinavigationitem-to-a-uinavigationcontroller-with-delegation.htm
If anyone disagrees and thinks this can be simpler, please let me know or rate this post