I have 3 view controllers that I'm navigating between. When I open my app, I start at Controller1, which I can then use to navigate to Controller2 or Controller3. I can navigate to each of them fine individually, however, after I go to Controller3, return to Controller1, then try to navigate to Controller2, I get an EXC_BAD_ACCESS with code = 1. There is no exception or error message given at all, it just takes me to my AppDelegate file and gives me that error code.
I don't know what the issue is, but something that seems relevant is that I'm setting Controller3 as the navigation controller's delegate. I have a fourth navigation controller that also is set as a delegate as well, and causes the same behavior when I from 1->4->1->2, just like with 1->3->1->2. I have no issue going from 1->4->1->3 or 1->3->1->4, only when 2 is involved. I'm not sure if the issue is the delegates, and the fact that 2 isn't being set as one. Once again, I can navigate to it fine by itself, but not after navigating to one of the other 2 sub-view controllers.
If you set Controller3 or Controller4 as the navigation controller delegate then you need to clear it in the viewWillDisappear function of those classes otherwise you will end up with an invalid reference and that is what is causing your crash
I ran into this same kind of crash, so I will share what the issue was for me:
I had created a subclass of UIWebView.
In my storyboard, I dragged a UIWebView onto the canvas and changed it's class to be my subclass.
That was all working. The app had already passed through our QA team, was "accepted" by the business owner, and we were ready to push it to the app store.
Then I was told, "You can't use UIWebView. You must use WKWebView."
Fine, I changed my subclass to inherit from WKWebView, tweaked my internal class logic, and....splat. Trying to segue to that view controller would crash just as noted by the OP.
The problem was my storyboard: Because it was trying to instantiate my subclass, it was basically attempting to create a WKWebView which is NOT necessarily supported in Interface Builder (my friend says it might be ok in later versions, but I didn't verify).
TL;DR
The moral of the story is that if you have a subclassed object on your story board whose ancestor can't be dragged from the toolbox, then you will probably crash when you segue.
Related
I have a new iMessage Extension project where I tried 2 ways of structuring the navigation stack:
In my storyboard I set the entry point to a UINavigationController that has my MSMessagesAppViewController as the root controller.
Or I set my MSMessagesAppViewController directly as the Entry point in my storyboard. (No UINavigationController that owns it).
For scenario #1 above, the navigation controller works fine, and I can push new screens on the stack. (with the exception of the whole nav bar being hidden in Expanded view, which is a separate issue that I still have to figure out). However, NONE of the delegate methods of my MSMessagesAppViewController get called with this configuration. Such as:
willTransitionToPresentationStyle
didTransitionToPresentationStyle,
willBecomeActiveWithConversation,
didSelectMessage
(none of these get called)
For scenario #2 above, the MSMessagesAppViewController methods DO get called. (because the UINavigationController is not the entry point in the Storyboard).
So my question is: How can I have a UINavigationController be at the root of my iMessage Extension application, so I can perform Push navigation, but at the same time have the methods of MSMessagesAppViewController get called, as described by the Apple API?
Although it doesn't seem to be documented, it looks like message extensions expect the entry point to be a subclass of MSMessagesAppViewController. Those methods aren't delegate methods, they're superclass overrides, so there's no way to arrange for them to go anywhere else. The message extension system could handle the case you describe but-- aparently-- does not.
What I'd try is:
Make the entry point a subclass of MSMessagesAppViewController.
Early in that object's life cycle (maybe in viewDidLoad) create a UINavigationController and add it as a child view controller of your MSMessagesAppViewController subclass. Make it fill the entire screen.
Now-- in effect-- your navigation controller is the root of the extension. It's not really the root since message events like willTransitionToPresentationStyle will still go through the MSMessagesAppViewController subclass. But everything else starts there. It's the root of the UI and the navigation.
In the meantime it might be good to file an enhancement request with Apple. It's reasonable to think that the message extension system would check the root VC of a navigation controller to see if it's the right class, and maybe they'll add that in the future.
I have been searching all over the web but I can't seem to find the answer to this.
Currently i am using presentViewController to start new ViewControllers, but on certain view controllers i do not dismiss it and call over it. I currently am not using any navigation controllers or anything like that.
I am just worried that if I call the same viewController again via presentViewController, that the same viewController would have 2 running instances.
Is it possible? Or does the iOS framework automatically reuse the idle viewController?
If so, how do i remove the idle view controllers?
Thank you! (I was holding back my question and tried to find it all over the web, so if you can point me in the right direction, it would be very helpful thanks!)
iOS will not reuse your view controller, you can easily check it yourself by printing your view controller in viewDidLoad, you will notice first that viewDidLoad is called every time, and next that all objects have different addresses.
Unless you create thousand of them, or the navigation of your app doesn’t let you come back to an “idle” view controller, I would not say this is an issue though.
I don’t see any clean way to remove a view controller from the memory without calling “dismiss”. You could try to:
- “refresh” your view with new data.
- use something like UIPageViewController if the workflow of your app allows this kind of behaviour.
- rework the navigation so you can dismiss the view before calling another one
Good luck
I'm experiencing something really weird :
Create an extremely basic single view project, and add a second view controller to the storyboard, along with a modal segue from the first to the second. Initiate the segue from the view controller and trigger it programmatically with performSegueWithIdentifier:.
In the viewDidLoad of the modally presented view controller, add this log :
NSLog(#"%#", self.presentingViewController);
Now run the app on iOS 7, you should get a log like this one :
<ViewController: 0x7fa8e9530080>
Which is just the reference of the initial view controller of the app, which presented the modal view controller.
Now run the exact same thing on iOS 8, and you will get :
(null)
What's going on here ? Is it a known issue ? Of course I'd expect the exact same behavior on both systems.
Thanks ... Formalizsed as answer.
viewDidLoad should really be used for initialization, At this stage, there is not guarantee that the receiver's controllers view hierarchy has been placed in the navigation tree. If that is your intent, you should override viewWillAppear or viewDidAppear. Whilst it works in earlier versions, the docs clearly state that it should be used for additional initialization. It certainly sounds as though in iOS 8, the receiver's initialization is being performed earlier
I've been dealing with this (somewhat) random bug for a while and can't figure out the problem. The context: I'm creating an UISplitView iPad app that have a UINavigationController inside the Master view:
Main menu in red, submenu in green and main content in purple.
This UINavigationController does not fill the entire Master view because I need some space to have a vertical menu. When a user select a button on the vertical side menu, it sets something new to the UINavigationController to show a UITableView with options. What I'm doing on every menu selection is:
[self.subMenu setViewControllers:#[subMenuViewController] animated:YES];
What happens is that I don't need to keep the menu history, so what I do is I set a new root view controller to the subMenu every time.
The issue is when I start messing with the device orientation. It doesn't have a clear pattern, but sometimes, when rotating, my app crashes. Now when I run it with Instruments, this is what I get:
167 Zombie -1 00:32.101.527 UIKit -[UITableView _spacingForExtraSeparators]
And the interesting thing is that the bad access happens on the previous root view controller of the subMenu. So if I tap "Events" and then after that I tap "Podcasts", the bad access happens on trying to access "EventsViewController".
So I'm guessing something is not right on my way of replacing the root view controller of the subMenu UINavigationController, but I'm not sure what it is. Maybe I need to make sure the current root view controller is released before setting a new one?
Any help is much appreciated. :)
It's not uncommon to crash in system library code due to something you didn't set up quite right. This might be that your UIWindow, UIApplicationMain or its content view or your view controller instance was not retained or got released somehow.
That shouldrotate method won't help if your controller isn't still around.
This is to identify which object got released.
For particularly thorny problems, you could add release, retain, and dealloc methods (that log and call super) to a suspect class of yours and see what's releasing it. Log the -retaincount to keep track (I only use this for diagnostic purposes,)
Or you can try this, set a breakpoint on -[UIDevice setOrientation:] and step through your code in the debugger.
To make debugging easier, you can type call (void)instrumentObjcMessageSends(YES) right in the debugger console to begin logging objc_msgSends to /tmp/, then continue execution and it will trace all the messages that are sent right up until the crash.
So first, you should implement the methods willRotateToInterfaceOrientation, willAnimateToInterfaceOrientation and didRotateToInterfaceOrientation (check the actual signature of these methods) in your view controllers that contain a UITableView.
In each of these methods check the your table view dataSource and delegate. I think this crash is caused by a release of the delegate or table view's datasource.
Also you should check what table view delgate/datasource methods are called during rotation.
And the last thing, make sure you discard the old instances of subMenuViewController and they are properly removed from the parent view controller.
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.