conditional refreshing of view in tabBarController - ios

i have implemented a tabbarcontroller with 5 tabs each connected to a view.
the tabBarcontroller is created and default view allocation happens in another view.
everythine looks fine but i have a bug.
each view in the tab have buttons that trigger other views. these views don't have tabs so to get back to a tab view i use a back button.
when i press the back button i don't want the view ( with the tab ) to be created from scratch. so i have put the relevant code in viewdidload rather than viewwillappear.
( this choice is because this view downloads data from server and it becomes time consuming to put the code at this point in viewwillappear )
However if i am returning from current tab to a tab that i had previously touched and viewed. I want view to be loaded from scratch because data should be downloaded again at this point. but since i am using viewdidload rather than viewwillappear the old view is not refreshed.
how can i achieve this conditional refreshing of view depending upon whether i am coming from another tab or a from a view with back button

One way to do this is the isMovingToParentViewController method in your view controller. This will enable you to detect if your current stack has been popped from navigation controller.
There's an answer here that explains how to use it, and links to Apple's documentation.
As you can see from that thread, you can also use NSNotificationCenter to do this.

Related

Using a UITableView to populate a UITabBar

I am converting my single view application into one that is based on tabs. I have most of the layout done so I'm trying to wire up all the components now and I'm stuck trying to figure out how to do two things:
When I pick a bonus from my UITableView, I want it to open my 2nd tab with the info for the selected row. I'm not sure how to do that.
If I go straight to one of the other tabs, I want to have it just display the details for the first visible row of the UITableView. I'm not sure how to set such a default value.
I'm not exactly sure what code you would need to see for the above, but I am using Xcode 9 and Swift 4. I've googled and searched YouTube and Stack Overflow, and the answers are either all for Objective-C or are about the UITabBar obscuring the last row of UITableView data (which is not an issue I'm having).
EDIT: I seem to have semi-gotten it to work by changing my prepare (for segue) and then via Interface Builder, deleting the segue from the UITableView to the tab's viewController. However, this still doesn't light up the proper tab in the UITabBar. Also, this is still wrapped in the original Navigation Controller. Which allows me to move back and forth, but isn't the intent. When I tried removing the Navigation Controller, I had to use a "Show" or "Modal" type segue, which covers up the UITabBar, and offers no way to get back out of the detail view. I want to use the UITabBar to provide the back and forth that the Navigation Controller used to handle so I can gain the space at the top of the screen back.
The simplest is probably to register a Notification Center observer in your second tab's view controller. Whenever you have an item to show, simply trigger the notification from your first tab's view controller attaching the object you need to show to the notification.

State Restoration in Tab Bar and Navigation Controller App

Goal: I'm trying to restore state on a tab controller-based app (with navigation controllers on each tab).
Problem: On relaunch, the selected tab seems to be restored as expected, but the navigation hierarchy inside said tab is not.
Development:
I first started with the project template "Tab based app".
Next, I added restoration IDs to both child view controllers and the tab bar controller.
In the app delegate, I implemented application(_:shouldSaveApplicationState:) and application(_:shouldRestoreApplicationState:).
I run then app, switch to the second (right) tab, hit home, terminate. o relaunch, the right tab is displayed (as expected). So far so good.
Next, I go to the storyboard and embed both child view controllers in respective navigation controllers, and assign restoration IDs to those too.
I run the app, and restoration still works. Still good.
Next, I add a "detail" view controller; its class is a custom subclass of UIViewController to the storyboard, with properties to configure the contents of a debug label and its view's background color.
I placed a "Show Detail..." button on each of the tabs' top view controllers, and create a segue from each into the (shared) detail view controller. So now my storyboard looks like a hexagon (also, both segues have identifiers set in Interface Builder). So, both left and right top view controllers share the same type of "detail" view controller. On show, it is configured so as to distinguish from where it has been pushed (see next point).
On each of the top view controllers' prepareForSegue(_:sender:) method, I configure the pushed detail view controller differently: Different text and background color ("left" and blue, and "right" and red, respectively).
I added code to the detail view controller to save and restore the state of the text and background color properties: encodeRestorableStateWithCoder(_:) and decodeRestorableStateWithCoder(_:). Also, I implemented viewDidLoad() so as to reflect those properties' values in the view. Whenever it is instantiated and pushed into the navigation through a segue, the properties are first set and then used to configre the view in viewDidLoad(). Whenever it is instantiated during restoration, the properties are set in decodeRestorableStateWithCoder(_:) and similarly used in viewDidLoad().
...but when I run this code, the last selected tab is restored but only up to the top view controller -left or right-, not the detail. Interestingly, the background color last set to the detail view controller flashes for an instant.
I placed break points in encodeRestorableStateWithCoder(_:) and decodeRestorableStateWithCoder(_:), but only the first of them is executed (encode).
Wondering what might be missing, I went ahead and implemented the app delegate's application(_:viewControllerWithRestorationIdentifierPath:coder:)(returning always nil, but logging the path components passed).
The documentation is not very clear on whether this method is needed or not, and in any case all view controllers except the detail seem to be restored perfectly even without it. I added code to instantiate each view controller based on the last path component (i.e., that controller's restoration ID) and returning it.
Now, the decodeRestorableStateWithCoder(_:) is called, but the navigation still goes back to the tab's top view controller after a split second.
So, what is going on? What am I missing to implement State Preservation and Restoration in a Tab Bar + Navigation Controller app?
FIXED: So, there were several problems with my code...
It turns out that in my case, I do not need to implement application(_:viewControllerWithRestorationIdentifierPath:coder:). (see the comments of this answer)
My implementations of encodeRestorableStateWithCoder(_:) and decodeRestorableStateWithCoder(_:)
were not calling super (as suggested in the accepted answer to the question above).
finally, I got the right view controller (detail) to appear, but its subviews' state (text label contents and main view background color) were in the initial, empty state (not being restored to their last state -i.e., text label contents and bg color). As mentioned in this question, the viewDidLoad() is not called right after decodeRestorableStateWithCoder(_:) (like I assumed), so instead I call a common method from both viewDidLoad() and decodeRestorableStateWithCoder(_:) to update the UI.
As usual, I rushed to post a question before searching or trying enough modifications in my code (my apologies...).
I hope this at least helps someone else.
As usual, I'll wait a couple of days before accepting my own answer, in case somebody sheds additional light.

The TabBarItem disappear when I push in other view

I have a TabBarApplication with four views in the main TabBarItem. The problem comes when I go to any of these views and click in any button to go to another view and when I go back by a button linked to the main view, the TabBarItem of the app disappear!!
For example, one view of the app is a tableView in which each element of the list is linked to his external view and it has a back button that should return to the tableView. All the segues are by modal, not push because push segue crash the application and by modal it runs correctly but the problem comes when I returned by clicking the back button of the NavigationItem in the header of the view to his main view and the TabBarItem of the app is not there, is empty.
Each tab should have the view controller set to a navigation controller, with the view controller you want set as the root view controller of the navigation controller. Now you can use push segues and the standard back button that will be added for you. This will bypass the issue (and work much better for you and users).
You current issue is likely related to not really ever going back. Instead, just always presenting new modal view controllers which replace any existing content on screen.

Data disappears when switching between view controllers

I am writing an iPhone application using the storyboards for an initial mockup. The problem I have right now is switching view controllers.
I have a table view controller and another view controller. All I want to do is use a back button to go back to the original screen, and I can do that, except the data disappears. The storyboard that I have is shown below.
I have the Back button going back to the original navigation controller. I have also had it going back to the Card view controller.
I have hard coded some example cells to just see how things look and they show up just fine when I run the simulation. When I click the back button though, it goes back to the All Cards screen and the cells that were there are now gone.
If I need to post some code just ask for what part would be helpful, I have done all of this through storyboards though.
I'm sure it's something stupid I've done, any point in the right direction would be greatly appreciated.
Basically: you pushed where you should have popped.
What you are seeing on the Storyboard does not exist yet. By segue-waying during runtime to a view controller it gets instantiated.
When you segue-wayed during runtime from the Add Card view controller "back" to the Card View Controller - here is what happened: instead of popping the navigation stack all the way back to the Card View Controller you already had, you just instantiated a new Card View Controller and pushed it onto the navigation stack. You could verify that by going all the way back to the original Card View Controller by tapping the back button several times.
What you could do to accomplish your task is this:
Instead of using the Storyboard for your back button use an IBAction in code:
- (IBAction)popToRoot:(id)sender {
[self.navigationController popToRootViewControllerAnimated:YES];
}

Creating an initial view before displaying views with UITabController

I am creating an app that uses a UITabController with 5 tabs for navigation. Right now my app loads the first tab as the initial view upon app loading.
I want to be able to change that so I have a view that doesn't use my UITabController as the initial view, and once they click the one button on it, it brings them to the First View and displays the TabController.
I thought I'd simply set up a new view, change that one to the initial view controller and have a segue from the button to the TabController, but when I tried that and the view I wanted to with the button loaded first, but when clicking button it said something about needing to set up a NavigationController? Not sure what to do from here.
I think it's best to leave your tab bar controller as the window's root view controller. You can present the initial view controller from the viewDidAppear method of the controller in the first tab, using presentViewController:animated:completion:. Do this with the animated parameter set to NO, and the initial view will be the first thing the user sees. When you're done with that view, just dismiss it, and you'll be back to the first tab's view.

Resources