I have experienced a strange thing when I use UINavigationController with push method. Let's say we have a ViewController with two buttons (sign in & sign up). When user taps each of the buttons, the app presents the proper ViewController, but the UI elements, placed on that ViewController (for example UITextFields, Buttons) appearing first, while the transition animation still taking place.
I use a function to setup my layout in each ViewController and I'm not dealing with Storyboard.
I tried to use viewWillLayoutSubviews(), viewWillAppear(), etc, but experienced the same thing...
How can I reach smooth transition between the views?
Finally I solved it. The overlapping Viewcontroller did not have background color defined.
Related
As someone who usually used separate xibs in the past I thought I'd give storyboard a go as it seemed a lot simpler to use and much easier to develop with. I've been writing an application where the essential set up is this:
At the top of all this is a UINavigationController (first level). Then I have Multiple UIViewControllers (second level) with buttons in them which you can tap to switch between the second level UIViewControllers.
However a problem occurs when I start switching between the second level UIViewControllers. I first thought this was an initialisation problem with the NSMutableArrays because in my code I have a NSTimer set to loop periodically and found when I set a breakpoint during it, when I went forward to the next timer tick event there appeared to be different instances of the same NSMutableArrays and it seemed a gamble to try and insert new values into these array with it sometimes working, sometimes not (as it may or may not insert into the correct instance).
Then, looking at the memory usage under Debug Navigator I found the issue. Each time I "switched" between the UIViewControllers a new UIViewController was being initiated, along with all new variables.
The code I am using to switch between them is
-(void) perform {
[[[self sourceViewController] navigationController] pushViewController:[self destinationViewController] animated:NO];
}
Or essentially a push segue transition. This also explains why when I tried to switch back to my view, the data on that view was lost as it is a complete new view.
Does anyone know how to switch between multiple ones of these UIViewControllers (say 5) essentially like a UITabViewController would except without the tab bar being present?
First option you can do this: You can use a tabbarcontroller for switching viewcontroller and hidden the tabbar. Then on buttonclick setthe tabbar index.
Second option you can do this: Create one more view controller and in this viewcontroller subview the all switching viewController and when you want to switch viewcontroller just bring that viewcontroller view to front by delegate.
Do you need the navigation bar and other features provided by your top level navigation controller?
If not, you could use a UIPageViewController instead.
You set up all your second level view controllers and then just have to tell the page view controller which one to display.
If you implement the associated delegate methods, it will automatically provide swipe gestures to switch between them and nice animations to get them on and off screen.
You can also get it to put a UIPageControl at the bottom showing a dot for each VC with the dot for the current VC highlighted.
I created a storyboard for my app which contains the following:
Initial view controller on my storyboard is a Tab Bar Controller (let's call it myTabCtrlr)
myTabCtrlr has forward segues pointing to several other controllers:
a. First segue points to a custom UIViewController (let's call it vc1) on which I create an interactive UIView (let's call it popview1) which is initially hidden. There's a button (let's call it showPopView1) on vc1's view which when clicked would show popview1
b. Second segue points to a navigation controller, which embeds a view controller with 3 buttons, each pointing to an (end) controller.
c. Third segue points to another navigation controller with a similar setup as (b)
On several of these (end) controllers, there's a button similar to vc1's showPopView1 that when tapped, I'd like to switch back to vc1 and programmatically bring up popview1, which I'm doing as follows (but it's not working):
myTabCtrlr.selectedIndex = 0;
//I get a handle to vc1 then
vc1.popview1.hidden = NO;
When I do that, it goes back to the first tab and shows vc1 view (which is good) but it does not show popview1. I tried many different ways to do it but no luck.
Note that if I'm actually on vc1 and I tap the showPopView1 button, then popview1 comes up normally.
Does anybody know why that is the case? This only started after I transitioned to using storyboard. thanks.
After spending hours looking at various ways to solve this problem, and focusing on reverse segues and similar methods, I was able to solve it using a totally different method.. I wanted to share this with others so nobody has to waste so much of their time (although I noticed slow response to my post. Maybe I didn't make the subject attractive enough :)..
It's a really simple solution, but quite effective. I used a Singleton pattern object. When coming from the rest of the tabs, I set a flag in the singleton that vc1 checks in its viewWillAppear method, shows popview1, and immediately resets the flag.. works like a charm!
I changed navigation in my application from using UITabBarController to u UINavigationController. I.e. former solution (1st version) was based only on the TabBarController - 4 ViewControllers (one simple TableView, one simple custom view and one MapView with many overlays). The second version is based only on the UINavigationController.
In case of TabBarController it was clear and simple, everything worked fine, especially MapView. I mean: the MapView was loaded once (with a significant number of overlays) and when I went to another view and back to the MapView the MapView was still there with its overlays already loaded and displayed (simple check: MapView`s viewDidLoad was called just once per app run, I had some debug messages there).
Now I changed navigation logic to the UINavigationController. Everything works fine for the first look - but: the viewDidLoad (for each view) is called everytime I navigate to the view. It is annoying especially in the case of the MapView - the loading of overlays is performed everytime, it takes some time and it causes app crash in some cases.
OK, my questions:
Is it some kind of "common" behavior of NavigationController?
Can I change this behavior so viewDidLoad will be called just once?
And more - How can I influence the "display sequence" of some view?
I understand the logic is probably more complicated but I appreciate any answer or hint ;)
Some related circumstances:
TabBar and Navigation controllers are not combined.
I use storyboards, segues are designed in the UIB, no manual calling like perfomSegue or prepareForSegue in my code. One button triggers segue to MapView.
I use push segues.
I also tried to use modal segues but without any change of that behavior.
any of viewDidUnload is never called during segues among the views.
No memory warning received.
No memory leaks measured both on simulator and iPhone 4.
I tried to build a very simple temporary project / app that is concerned just about the Nav. Controller and other views without ANY coding, just storyboard. It was the same behavior.
There was an issue that causes app crash when I fast and periodically tapped to navigation button and back button between one view and the MapView. In most cases the app crashed when I tapped the back button on the MapView before it was fully displayed (i.e. its overlays). It was fixed when I added a 1 sec. delay method call in the viewDidDisappeared in the MapView. It is not a fair fix, I know ;)
A UITabBarController and UINavigationController are based on fundamentally different paradigms.
The UITabBarController is intended for the UIViewController on each tab to exist independently of each other and for the user to choose which they want to view. viewDidLoad only gets called once for each UIViewController because it is intended that each tab still exists in memory even as the user switches to a different tab.
The UINavigationController is a stack of UIViewControllers where each is related to the one above and beneath itself. The top UIViewController in the stack is always the one that is visible to the user. When a UIViewController is pushed to the stack, its viewDidLoad gets called because it is being loaded into memory. When the top UIViewControllergets poped off the stack, it is unloaded from memory, and viewDidUnload gets called on the way out (viewDidUnload is deprecated in iOS6 and won't get called, but the controller will still get dumped from memory). This is why viewDidLoad gets called every time that the user pushes a particular UIViewController onto the UINavigationController stack.
I have a UITabbar inside a UINavigationController base app. On one off the tabs I've some tumbnails of photos, and when I tap on one of the photos, I go to another UIViewController (push to the nav stack) and have the photos in Fullscreen.
I have two problems with that:
1- I can't rotate, or even make a NSLog of the rotation. I have the shouldAutorotateToInterfaceOrientation returning YES, and i've tried the didRotateFromInterfaceOrientation and willRotateToInterfaceOrientation methods, with a simple NSLog, but it doesn't worked at all.
2- When I hit the back button, and come back to the tab of thumbnails, I still have these thumbnails, but i don't have the tabbar any more.
I will just throw my two cents:
When you are using a UITabBar application, all UIViewController's should allow rotation in order for you to rotate one of them. Although there are some workarounds for this behavior, you can check this answer and this article.
I have an app that has four tabs. I have four UINavigationControllers, each of which is bound to the UITabBarController by setting the TabBarItem property of each controller and then adding the controllers to the UITabBarController. That's all standard iOS fare.
One of these tabs loads up a UITableView that displays some data from a remote web service. Because the web service calls routinely take about 1.5 seconds to complete and deserialize, I'd like to display a "loading" UIView with the text "Loading..." when the corresponding tab is selected. I've tried this a couple of ways:
assigning a delegate to the ViewControllerSelected event on the UITabBarController, and checking to make sure that the controller selected is the one I'm interested in. Then I try to show the "loading" UIView. This all happens in AppDelegate;
overriding the ViewWillAppear and ViewWillDidAppear methods on the controller itself. Show the LoadingHUDView in the ViewWillAppear method and hide in the ViewDidAppear method.
In both cases:
The "loading" UIView is a local variable (lhv) and is already setup with the text I want.
lhv.SharedApplication.KeyWindow.Add(lhv);
lhv.StartAnimating(); (the UIView contains a UIActivityIndicatorView)
Perform the service calls and refresh the UITableView
lhv.StopAnimating();
lhv.RemoveFromSuperview();
(And yes, I am aware that I don't need to use lhv.SharedApplication.KeyWindow in AppDelegate because I have the local "window" member available to me.)
The problem is that the "loading" UIView doesn't render until AFTER the UINavigationController has rendered its view. I want the LoadingHUDView to appear IMMEDIATELY, as soon as the UITabBarController is tapped. I've tried using InvokeOnMainThread() as well, but no luck there.
Any ideas?
>.< Figured it out. Just had to spin up the loading UIView in a new thread at the beginning of ViewWillAppear or ViewDidAppear. Then just have to do the cleanup at the end of the respective method by removing the view from the superview. Should've thought of that one a long time ago! D'oh!
declare spinner separatly; and add
MyView.addsubview(_spinner);