I've a problem, when I'm scrolling up and down in the table view highlighted in the view below it can be fine for a few scrolls up and down and then seemingly randomly it pops back to the view before (but doesn't update the screen so it just looks stuck in the table view) Sometimes on the first scroll it pops the view too.
I don't really want to post my whole class unless someone thinks it's necessary but I would be grateful if someone could point me in the right direction as to why this would be happening.
- (void) viewWillDisappear:(BOOL)animated
{
NSLog(#"Is this being dismissed: %d", [self isBeingDismissed]); // returns 0
NSLog(#"isMovingFromParentViewController %d", [self isMovingFromParentViewController]); // returns 0
NSLog(#"viewWillDisappear occured on bookmark table");
}
-(void)willMoveToParentViewController:(UIViewController *)parent {
NSLog(#"This VC has has been pushed popped OR covered"); // this is happening
if (!parent)
NSLog(#"This happens ONLY when it's popped"); // this is happening
}
Could anyone tell me a way to find out what is calling the willMoveToParentViewController or viewWillDisappear? I've had a look in the debug log with a pause but it just says before
A breakpoint on willMoveToParentViewController shows this in Thread 1
0 [bookmarkTableViewController willMoveToParentViewController:]
1 _popViewControllerNormal
A breakpoint on viewWillDisappear
0 [bookmarkTableViewController viewWillDisappear:]
1 [UIViewController _setViewApppearState:isAnimating:]
So as summary, I can use it fine for 10 scrolls up or down and then it happens, or maybe it will happen on the first scroll so it looks random to me. If anyone thinks it'll help me sharing my entire class it'll help but it's pretty much just a generic table view.
EDIT
I've found that it's something to do with the segue.
When I segue with just 'Show (e.g Push)', it lets me have the navigation back button but crashes. When I do 'Show Detail (e.g replace)', it never crashes which leads me to think I could just fake the navigation back button but really i'd like to know why this is happening.
EDIT 2:
I've noticed that the problem occurs even with a blank table list so it has nothing to do with that. When I spam scrolling up and down on the totally empty new table view controller it does the exact same thing.
For some reason scrolling up and down in scroll area eventually locks the tableview UI and calls the previous view controllers viewwillappear
EDIT 3:
I think it might have something to do with the navigation controller, I just stripped hte project to its bare bones and i noticed sometimes instead of table scrolling it's doing the side-swiping navigation back gesture thing
EDIT 4:
Yep it was that, I can't even fix it apparently with objective-c as of yet it says https://stackoverflow.com/a/19019924/4056064
I found the reason. this navigation gesture turn off thing doesn't work so occasionally a back navigation sweep was detected rather than a scroll and it called my previous view and then returned it to the current one. source: https://stackoverflow.com/a/19019924/4056064 happy to hear if anyone has any work arounds that.
Edit: found a work around
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
return NO;
}
if ([self.navigationController respondsToSelector:#selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
self.navigationController.interactivePopGestureRecognizer.delegate = self;
}
Related
I had issue that every time when I enter a view controller - which is the "MessageListViewController" in the following screen-shot, the keyboard will be automatically popped out.
The thing is I used a UITextView in this MessageListViewController for text input, and when I pop this view controller from navigationController stack, if I leave the keyboard open, then the next time when it was pushed again, this keyboard will be popped out automatically.
If I make the text view resignFirstResponder before I popped out this view controller, then the next time enter it will be fine. However resignFirstResponder manually will make the keyboard dismissing with an ugly animation (manually dismiss keyboard it will make the keyboard always go downing the screen vertically, however the view controller popping transaction is horizontal, so it will look very wired)
I tried to build a simple sample to test the keyboard dismiss behavior - there are two view controllers, one is the rootViewController of the navigationController, and it will push another into the viewController stack, so the pushed view controller contains a UITextView, if I highlight the text view of the pushed one, then go back to the root view controller, the animation works just fine, also the keyboard won't be automatically popped out. So I just don't know why in my project, this keyboard always shows up unexpectedly if I just left the page without manually dismiss the keyboard.
BTW, the following call stack happened between viewWillAppear and viewDidAppear. It looks like triggered by iOS to restore the last state of the UITextView.
- (UIView*)findFirstResponder {
if (self.isFirstResponder) {
return self;
}
for (UIView* subview in self.subviews) {
UIView* ret = [subview findFirstResponder];
if (ret) {
return ret;
}
}
return nil;
}
NSLog(#"%#", [[[UIApplication sharedApplication] keyWindow] findFirstResponder]);
I also tried to track with the key window's first responder when this call stack triggered by keyboard will show notification, and the out put is just "nil".
Thanks guys for helping, I just realized where the problem is, actually I did not declare that the messagelistViewController which I used here is actually a singleton, so it never release even I pop the this out of the viewController stack.
Under that scenario, when the next time this view controller was appear again, the UIKit will be smart enough to restore the previous view state if it's not dealloced.
And in my test sample, I did not keep the test viewcontroller when it was popped out of stack.
so that's why this works fine for my test sample.
Then my final solution is kinda simple, just remove the textview from it's superview when the view did disappear, and then re-add this back when the view will appear.
It turns out my question is a little bit stupid, however it do let me know two things:
1) The view controller will restore to it's previous state every time when it appears if you not delete it
2) The keyboard dismiss animation will always be going down if manually resign the input from the first responder, if we don't want this effect, we shall never dismiss it manually.
I created a demo in iOS 7 as per you said in your question, it was working fine for me even I didn't have to call resignFirstResponder method for resigning keyboard. So you could look in your code as you might have been calling becomeFirstResponder somewhere making the keyboard to be active again or simply post your code here so I could have a look at it.
I want the keyboard to slide up as the view controller slides up. But for one of my view controllers that I present modally, the keyboard appears instantly when the view controller is presented, so the keyboard appears then the view controller slides up from under it, causing an ugly effect.
Oddly enough, this instantaneous behaviour happens when it's in viewDidLoad, but having it there works fine for another view controller. (But in the instantaneous one it appears for a UITextField, while the proper one is for a UITextView.)
Here's what the code looks like:
- (void)viewDidLoad
{
[super viewDidLoad];
[self.URLTextField becomeFirstResponder];
}
How do I make it present alongside the view controller? I don't have to do an ugly dispatch_after do I?
If it's loading too quickly with some methods (ViewDidLoad/ViewWillAppear) and too slowly with others you could try doing something in the middle.
I wouldn't suggest it as I'm sure theres a way to have it do what you like but I imagine that in viewDidLoad you can set the view up to respond to to keyboardWillShow and then become first responder and in the notification delay for a few milliseconds
I'm having a weird issue with a navigation controller's back button animation:
my app has a tab bar control with 3 tabs, one of them has a navigation controller with two subsequent view controllers, the first one just show a master table and the second one details, the problem comes when I tap the back button from the detail view controller, instead of slide back to the master view controller it just pops the view without animation.
I've noticed that if I first go to another tab, and then return again to this one, the animation will trigger normally.
I've rebuilt the whole app navigation from scratch but the problem still persist, any help is appreciated.
Thanks in advance!.
Edit: More info added
This is how my storyboard looks like in that particular branch:
Here's the prepareForSegue from "Partidos Activos" view controller:
#pragma mark - Segues
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"PartidosEnDia"]) {
PartidosActivosEnFecha *paf = segue.destinationViewController;
CalendarCell *senderCell = (CalendarCell *)sender;
paf.datos = senderCell.dataDic;
}
}
Both viewController viewDidLoad methods are calling super at the start of the method.
As I told before, if I just tap on other tab and then come back to this one, the slide back animation from "Partidos Activos En Fecha" viewController works as expected, it's only when I start the application and go directly to this viewController when the slide back animation doesn't work, and it just gets to the caller viewController without animation.
Hope I added enough info, if not just tell me and I will add it again.
I finally found where the problem was, I was missing a call to super in the viewDidAppear method but in UITabBarController!, I was checking only viewControllers for the tabs but not the tabbarviewcontroller. #rdelmar was right.
I had the exact same problem. The cause for me was an empty viewDidAppear:animated method in my UITabBarController. Once I deleted that method, the animation worked normally again.
I think this it's what you want. If I understand, your problem is handle the stack of the navigation controller right? So, check that link.
I created regular buttons in .xib file and I added a gradient effect to them and shadows in the code in this section:
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
... my customized buttons code here
}
When I modally switch to another view controller and then go back to the original one the xib file gets redrawn but all the gradient effects and shadows disappear. Any ideas?
I'm not sure entirely what's going on, but note that after you dismiss the modal view controller, viewDidAppear: gets called again. If you only want to make these buttons once, you could move your custom button code to viewDidLoad.
I haven't experienced this problem before to know what's going on, so it would be helpful to see the code itself. But I suspect that moving the code to viewDidLoad would solve the problem.
When modal View is dismissed, ViewDidLoad is not called, But ViewWillAppear and ViewDidAppear is called, But you say that the code is written in ViewDidAppear, and still it doesnt work. I suggest you to write that code in ViewWillAppear and check.
I have 3 view controller in a tab bar controller. Clicking on any tab loads its root view controller in the navigation stack.
e.g. tab1, tab2, and tab3.
The 2nd view controller in the navigation stack (tab2VC2), has a tableView.
Click on tab2 show VC in tab2, then tap on tab1, tries to go to its rootVC. Then the app is crashing saying
[UserDetailVC
tableView:cellForRowAtIndexPath:]:
message sent to deallocated instance
0xe0a23b0
If I popToRootVC with animation then its okay. I found viewDidAppear in the tab2VC2 is called where the tableView.reloadData is called, then dealloac, seems in the meantime reloadData starts working, the table is released. in case of animation, it gets some time, so it dont crash. But without animation, it is crashing.
Do you think, its an iPhone bug? or I am doing wrong? Since pop to root controller have an option without animation, it should work, not it?
#pragma mark Tab bar controller delegate
- (void)tabBarController:(UITabBarController *)tbController didSelectViewController:(UIViewController *)viewController {
int i = tbController.selectedIndex;
NSArray *mycontrollers = tbController.viewControllers;
[[mycontrollers objectAtIndex:i] popToRootViewControllerAnimated:NO];
}
I consider this a bug or at least a weakness in UIKit, but I've already blown half my day on it, so I'm not going to write it up with example code and report it to Apple right now. If someone else wants to do that, I would appreciate it.
Here's what I think is going on under the hood. You have a UITableViewController, let's call it myTable, on the stack of a UINavigationController, and that navigation stack is hidden because it's on an unselected tab or whatever. Then, you call [myTable.tableView reloadData], and iOS cleverly optimizes by not reloading the data right away, because the user won't be seeing it anyway if it's on a hidden tab. Instead, the reload request is deferred and stored somewhere for when the view is shown. But before it can be shown, you pop myTable off the navigation stack. When myTable's original tab is shown, the reload request gets executed, but its dataSource is no longer there, so it's a bad access.
Now from my tests with a subclass of UITableViewController that uses the automatically provided tableView property (not loaded from a NIB file), the UITableView is not being deallocated when myTable deallocates as in the situation above. That would be fine, except the default dealloc implementation for UITableViewController does not clear the dataSource property of the UITableView (which was set by the default implementation of init).
So, there are probably a couple good workarounds, like deferring the request to reloadData yourself, but the simplest one I can think of is putting this in the implementation of your UITableViewController subclass:
- (void)dealloc {
...
self.tableView.delegate = nil;
self.tableView.dataSource = nil;
[super dealloc];
}
Any additional wisdom would be most welcome.
Jesse's answer works perfect. I just made a slight modification for ARC Support
- (void)dealloc
{
self.tableView.delegate = nil;
self.tableView.dataSource = nil;
}
Double-click your executable in the Groups&Files list; click Arguments tab; click "+" in lower pane, enter "NSZombieEnabled", set value to YES, check the box, click red dot to dismiss.
Now run your test case again, and it will tell you which object has been deallocated.
I suspect it's the array backing your tableView in tab2V2.
Make sure your memory handling is correct (is it retained and released correctly?).
You can try this to avoid the problem:
-(void)viewWillAppear:(BOOL)animated
{
if (animated)
{
[self.tableView reloadData];
}
}
When navigating from a tab bar controller, the view will not be animated. So if the view is appearing just for an instant before popping to the root, it won't attempt to reload the table data.
Had a similar problem and Im pretty sure it has something to do with the new SDK to be honest. The following code worked fine before. I have one navigationcontroller and a new one pushed on to that with 3 different views that gets put onto it (Think: changing password where you have 3 different steps: control, change, repeat.) If the user types the wrong password you will first be pushed of the "changing password controller" and then be pushed to the first page (logged out).
This worked before:
-(void)logout {
[self.presentedViewController dismissModalViewControllerAnimated:TRUE];
self.localNavigationController = nil;
[self.navigationController popToRootViewControllerAnimated:TRUE];
}
The way you could solve this is by change ViewWillAppear to ViewDIDAppear. You might have some lagg but atleast it doesnt crash :)
While Jesse's answer provided insight into the semantics around this problem, in my case the underlying cause was different. Just thought I'd mention it in case someone finds himself in the same situation.
I was seeing the error message
* -[UIAnimator removeAnimationsForTarget:]: message sent to deallocated instance ...
where the object in question was a UITableViewCell. On inspection, there were no memory allocation issues in my code.
The problem turned out to be that I was calling one of the popViewController:animated: family of methods from a non-UI thread. This thread had been created to run some network operations and database processing tasks. I simply moved the invocation of the pop method to the UI thread and found that I stopped getting the error.