I'm currently working on refining an app I made for (currently only) IOS. In this app, I have 15 different UIViewControllers, each one of them shows different data and uses different objects.
My menus have a hierarchical structure (not binary). I have 4 "parent" ViewControllers. These parent ViewControllers each have 1 or more "child" ViewControllers:
Roster
EventDetails
Directions
MapView
ChangeRequests
NewChangeRequest
ChangeRequestDetails
Contacts
ContactDetails
ProgressReport
NewReportEntry
DoubleChecks
NewDoubleCheck
DoubleCheckDetails
DoubleCheckPhotoDetails
On the parent ViewControllers I use a FlyoutMenu (with datasource) to be able to navigate to other parent ViewControllers. On the child ViewControllers I have a custom back button, with a delegate attached to it, to take me back to the previous menu. Here come the problem.
I've been given the assignment to link some menus to each other, to improve user-friendliness. an example:
I'm currently at the EventDetails menu. In this menu, I want a button to take me to the NewDoubleCheck menu. Both of these menus have a back button, that uses PopViewController to navigate back to the previous menu. If I'd accessed NewDoubleCheck from DoubleChecks, it would take me back to DoubleChecks. But if I'd accessed it from EventDetails, it takes me back to EventDetails, because it's on the top of the stack. This means I end up in an endless loop of EventDetails --> NewDoubleCheck --> EventDetails --> NewDoubleCheck.
Long story short: I want to be able to search the stack of ViewControllers and be able to select the right ViewController to be loaded, using PushViewController.
I was thinking about writing a method at the start of my app (somewhere near the initialisation of my FlyOutMenu, I'd reckon, that would make me an array of Dictionary<string, UIViewController> with ALL of the ViewControllers in my project, so I can search and navigate more easily. Is this possible?
I know this is a long text, but I'd be glad to hear any opinions and solutions for my problem.
Thanks in advance.
Dear regards,
Björn
I encountered the same problem in one of my apps.
Funny thing is that I checked some of the 'famous' apps on the store, and I noticed that they have this 'endless loop' issue.
My solution was:
Before navigating to NewDoubleCheck, I'd search if it already exists in the navigationController stack.
If that's the case, then I pop to that viewController instead of pushing a new one.
Something like this:
if ([self.navigationController.viewControllers[[self.navigationController.viewControllers count]-2] isKindOfClass:[NewDoubleCheck class]]) {
// ViewController already exist, so we need to get back to it
NewDoubleCheck *viewController = (NewDoubleCheck *)self.navigationController.viewControllers[[self.navigationController.viewControllers count]-2];
[self.navigationController popToViewController:viewController animated:YES];
} else {
// Push to NewDoubleCheck
}
Yes you can get all view controllers like this
NSArray *controllerArray = [[self navigationController] viewControllers];
for (UIViewController *controller in controllerArray){
NSLog(#"%#",controller.title);
}
Related
Suppose, I have two XAML page: MainPage.xaml & Page1.xaml.
For navigating to Page1.xaml, I always use this code:
Page1 mynewPage = new Page1();
this.Content = mynewPage;
But I see people using other codes for navigating purpose. Am I doing this in an inefficient way? What's the most efficient way to do this?
use this.NavigationService.Navigate(new Uri("/Page1.xaml", UriKind.Relative));
In that case you have not control about, for instance going back,in your case have to create code to manage the navigation by yourself, and you might to need to store the information of each page at a time, that is automatic when you do it.
Apart there are transitions between pages that are great to show the user that the page is changing. Of course you can create your own transitions but at the end you are creating the navigation by yourself.
With navigation you can control the events of Navigating and navigated and use them to initialize the page, etc.
In windows phone 8, you should use "NavigationService". NavigationService contains methods, properties, and events to support navigation and implemented by Microsoft.
Your code just updates content. It doesnt keep navigation history. So, you cant use back button unless override. You cant pass parameter to another view or you cant know navigation sucessfully ended.
To sum up, navigation service offers all of these features and mores. For detail information , you can checkout in app navigation model from here: In-app navigation for Windows Phone 8
I am looking for ideas as to how I should implement this:
I have an app with a tab bar controller, and off of this come navigation controllers for each "Screen" (which is accessed by its own tab on the tab bar nav at the bottom.
Each screen is then a DialogViewController where I have various elements and items to input and view data with. On one screen the user has a choice of items using a RadioGroup of RadioElements
MyRadioElement _customRadioElement = new MyRadioElement (lines [0], lines [5], setRPMElementData); // set profile name and calculated IOPS for custom disk to this custom element
// Disk Speed Root Element
_diskspeed = new RootElement ("Disk Speed: ", new RadioGroup (0)){
new Section ("Disk Speed:"){
new MyRadioElement("5400 RPM","5400",setRPMElementData),
new MyRadioElement("7200 RPM","7200",setRPMElementData),
new MyRadioElement("10K RPM","10K",setRPMElementData),
new MyRadioElement("15K RPM","15K",setRPMElementData),
new MyRadioElement("SSD","SSD",setRPMElementData),
_customRadioElement // this is the MyRadioElement defined above (our custom disk profile)
}
};
The last element (_customRadioElement) is a RadioElement that is created by loading some info out of a saved disk object. I read the lines of the text file and use this to create my RadioElement. It is originally created by using another Tab on the Tab Bar Controller to access another screen (also a DialogViewController) which allows the user to specify their "custom disk" and save it to disk/file.
My issue is when you are in the app, and want to modify this custom disk, you go to the custom disk editing tab, make the changes, and then flick back to the main tab where the RadioGroup is - however the custom disk RadioElement at the end of the RadioGroup list does not update. (Well the caption does not update, but the actual data behind it does). Even after pressing the back button to go out of the RadioGroup and back into it again. The only way I can get it to update is by killing the app, and restarting - the custom disk object is read in from the file again when the DialogViewController is created and then shows up fine. I tried using ViewWillAppear to create the whole table I need, but the issue with this is that everything is reset each time I flick between tabs, where I only need the custom disk RadioElement or the RadioGroup itself to be updated.
How can dynamically refresh this RadioGroup and it's associated RadioElements whilst still in the app and flicking between tabs?
I have tried using ViewDidAppear however this is on the DialogViewController itself, and doesn't seem to affect the RadioGroup. E.g.
public override void ViewWillAppear (bool animated)
{
base.ViewWillAppear (animated);
Console.WriteLine ("Forcing table reload...");
LoadDiskInfo(); // Reload disk info from local app storage
this.ReloadData(); // Force refresh of dialog viewcontroller table data - need to refresh the custom disk RadioElement somehow...
}
Any help would be much much appreciated!
You may find my answer to this post helpful:
Monotouch Dialog - Reload data list after modifying row detail not updating list when returning
Hope this Helps
Vassilis
Im developing a small file explorer, how do i set Back button.
i have:
txtAddress.Text which is the address bar of the explorer.
string currAddress which has the current address.
List<string> prevAddress which should hold some previous addresses which had been visited for Back Button
and i use:
Root(); to get My Computer items.
Open(string Address); to get Files/Folder from an address.
Search(string Address, string keyword); to get the search result items.
i need the back button because when i do search in a path, i can't press up button (which go to the parent path) because i need to get back to the path i was searching in, so how do back button works in explorer? and when should i add/remove addresses from it?
Suggest keeping/managing this state yourself in the application.
Each time the user navigates/forces a new/different path in the address bar, then add the new directory to a list/collection in your app.
When the back button is clicked, you can find the 'previous' entry in your list/collection. That's your directory to display.
Key Val
1 D:\
2 D:\Foo
3 D:\Foo\Bar
4 C:\ (here the user may have manually typed into the addr bar)
You may run into issues where the directory no longer exists, is renamed, is unavailable, etc. Perhaps you've already got these cases handled in your code. You could use Directory.Exists before attempting to navigate.
To implement a back button well I would suggest using a stack of some kind which maintains the locations which the user has been. Each time a navigation is performed, push the old location onto the stack. When the back button is pressed, pop the top item on the stack and navigate to that location. If the stack is empty, make the back button unusable, as there is nowhere to go back to.
Back (and forward) is very close to Undo which is more commonly discussed (i.e. mentioned in the "Design Patterns" book).
Common implementation - state (as pointed by #p.campbell answer) for each operation stored in "current state" and 2 stacks: one for undo/back, another for redo/forward. Whenever user makes a change (i.e. by typing something in or actively navigating somewhere) redo/forward stack is cleared and previous state is pushed to undo/back stack. Back/forward correspondingly put current state into one of the stacks and pop next state from the other one.
I am sure that the answer to this has been posted before. Forgive me as I think I am just not thinking of the right search string.
What I have is a context menu strip assigned to my tray icon for my dialer. The idea is for the user to set various numbers and select the user defined numbers from the menu and initiate the dial.
So the menu pops up with Presets, Setup, & Exit. I want the Presets menu to open a new tree listing the user defined number. I also want this to populate from an xml file every time the application is loaded.
My problem is that I have no idea how to dynamically populate a sub menu item and give it a function.
So how would I at start up add user defined numbers to preset -> (userNumber1, usernumber2, userNumber3) and then call the dial() function when clicked?
So I found how to add to the list... I now feel silly for asking that. For anyone else who wants to know that one, The list item is given a name. Im my case the name attribute is " presetsToolStripMenuItem"
So to add an item to it call the name
presetsToolStripMenuItem.DropDownItems.Add(string text)
No to move on. I am stuck now trying to figure out how to assign an event to that newly added function. I did find
presetsToolStripMenuItem.DropDownItems.Add(string text, image, eventargs)
I am struggling with this one. Maybe I need to stop and come back to it later. Perhaps if someone could provide me with an example of using this line to call a function(); I would be most appreciative.
For anyone that is interested I figured out the solution to adding a context menu item at run time with the ability to call a function.
As stated before, to add a sub menu item to a a parent category, use the parent.name. So in my case the preset menu item name was "presetsToolStripMenuItem"
To add function I used the 3 argument method.
ToolStripMenuItem.DropdownItems.Add("string name", image, eventargs);
so my code looks like this:
presetsToolStripMenuItem.DropDownItems.Add("added2", null, disp);
void disp(object sender, EventArgs e)
{
MessageBox.Show("It works!");
}
I m having a hard time with this issue.
My MainWindow.xib, has a NavigationController, the view for which is inherited from another xib.
Now, i push a DialogViewController from the main view, but i cannot see a back button on the second view's navigation bar.
Is there anything specific that i need to set for the DialogViewController when it is being pushed from a UIViewController.
Thanks and Regards
Abhishek
The constructor for DialogViewController has a parameter called pushing which you should set to true:
new DialogViewController(rootElement, true); // true will show the back button
Without seeing your code, I'm not sure exactly what's going wrong here. However, from what I know of UINavigationController, the view controller stack starts empty. When you push the first view controller, it gives the navigation controller a view to display, but it has nothing to go 'back' to, so it does not display a back button. If you push a second view, you may get a back button.
Also, be sure that the Title property is set on your child view controllers if you want the back button to reflect the view you will be going back to.
I have a tab bar controller which then hands off to a nav controller (in a storyboard with flyoutnavcontollers too). One of the viewcontrollers from here launches into a dialogviewcontroller for MT.D stuff.
I wanted a lovely pointed/tapered back button from monotouch dialog back to my calling point in the navigation controller.
But launching into MT.D loses the navigation even when im using the current navigation controller for some reason i.e. the button is not displayed and no way to get back. Subsequent mt.d screens give a back button.
Apparently your supposed to pass a true boolean into call to enable back button whilst pushing onto existing stack but this didnt work for me:
this.NavigationController.PushViewController (dv, true);
Dan's above solution didnt work for me. But popping the current dialogviewcontroller whilst at the root MT.D screen helps to get back to my previous position in the original nav controller in the storyboard (or flyoutnav controller).
Not sure if this hack is the correct way but it works.
dv.NavigationItem.RightBarButtonItem = new UIBarButtonItem("Back",UIBarButtonItemStyle.Bordered,delegate(object sender,EventArgs e)
{
NavigationController.PopViewControllerAnimated(true);
});
* update
I manged to get a back button by adding the dialogviewcontroller to current viewcontrollers subview:
dvc = new MyDvcController(this.NavigationController);
this.View.AddSubview(dvc.TableView);
the corresponding MyDvcController mainly loooks like this:
public partial class MyDvcController : DialogViewController
{
public MyDvcController (UINavigationController nav): base (UITableViewStyle.Grouped, null)
{
navigation = nav;
Root = new RootElement ("Demos"){
new Section ("Element API"){
new StringElement ("iPhone Settings Sample", DemoElementApi),
}
};
}
}
this allowed the monotouch.dialog to be part of the current navigation controllers stack and achieve automatic back button with the tapered look ..yay
You can also implement by yourself
dialogViewController.NavigationItem.RightBarButtonItem = new UIBarButtonItem("Back",UIBarButtonItemStyle.Bordered,delegate(object sender,EventArgs e)
{
NavigationController.DismissModalViewControllerAnimated(true);
});