Unbalanced calls to begin/end appearance transitions - objective-c

I'm currently pulling my hair out solving this bug :/ I have already tried the solutions from other SO threads regarding this topic but had no luck so far.
Here's what's wrong:
I have a UINavigationController that pushes View A, from View A I can press a button to push View B - works fine. But when I push View B, then rotate the screen into landscape mode and then click the back button, I get the following output in the console and the view switching is not animated, just switches from B back to A:
2012-01-02 20:50:42.866 [13345:f803] Unbalanced calls to begin/end appearance transitions for <DimensionConversionViewController: 0x68831f0>.
2012-01-02 20:50:42.868 [13345:f803] attempt to dismiss modal view controller whose view does not currently appear. self = <UINavigationController: 0x6b541a0> modalViewController = <UISnapshotModalViewController: 0x6da5190>
This is how I push the View B into the stack:
- (void) showConverter:(id)sender {
[self.navigationController pushViewController:converter animated:YES];
}
-viewDidLoad of View B:
- (void) viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateInterface) name:#"UIDeviceOrientationDidChangeNotification" object:nil];
// ... Update text fields ...
[self updateInterface];
}
-viewDidUnload of View B:
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"UIDeviceOrientationDidChangeNotification" object:nil];
}
If you have questions or need more code samples, please let me know.
Thanks in advance for any help :-)

Turned out that in my case the root cause of the problem was, that I forgot to update all the shouldAutorotateToInterfaceOrientation: methods in the different view controllers to return YES for all UIInterfaceOrientations (or let's say they should all return the sam). Doing this solved the issue.

Related

objective-c reload UITableView after back navigation

I have a TableViewController embedded in a NavigationController, at some point I use a Segue to push to a new ViewController. When I navigate back from the ViewController I want to reload the tableView in the TableViewController.
I followed the solution posted here and here. For some reason it does not work. I miss something but I can't see it at the moment.
Has anybody a glue, why the code does not work? What do I have to do to get it work?
TableViewController.m
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// notification when comeing back from the cameraView
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(reloadMagazine:) name:#"reloadTable" object:nil];
}
return self;
}
//[...]
// notification reload
-(void)reloadMagazine:(NSNotification *)notification {
NSLog(#"notification reload");
[self.tableView reloadData];
}
//[...]
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
[[NSNotificationCenter defaultCenter] postNotificationName:#"reloadTable" object:self];
}
ViewController.m
-(void)uploadData:(id)sender {
// Upload and navigate back to the tableViewController
[[NSNotificationCenter defaultCenter] postNotificationName:#"reloadTable" object:self];
[self dismissViewControllerAnimated:YES completion:nil];
[self.navigationController popViewControllerAnimated:YES];
}
This does not answer your question "why the code does not work?", but the easiest
method to reload the table view when navigating back to it could be to
call reloadData in viewWillAppear.
I suspect that the code above does not work because initWithStyle is not being called. Put a breakpoint there to test my theory. If that is the case, then move that code to viewDidLoad.

Setting UIScrollView's Position from another ViewController

I am trying to set the position of a UIScrollView by using contentOffset as such:
- (void) navigateToTableViewPosition:(CGPoint)contentOffset {
NSLog(#"Position set method gets called...");
NSLog(#"%#", NSStringFromCGPoint(contentOffset));
[mainScrollView setContentOffset:contentOffset animated:YES];
}
I call this method from another view controller before I dismiss it, and everything checks out. I pass the argument correctly, and the method gets called (checked it with NSLog), but the scroll view does not move...
What is funny is that when I call this method from the view controller, in which it is located, it works fine. Only when I call it from another view controller, it stops working.
Just for future reference, here is the calling method:
MainViewController *mainView = [[MainViewController alloc] init];
[mainView navigateToTableViewPosition:contentOffset];
Content offset is a CGPoint I set beforehand. It doesn't matter here; besides, it gets passed correctly anyways.
Try this, You have to send notification from other viewcontroller when you want to change ..
[[NSNotificationCenter defaultCenter] postNotificationName:#"changepostion" object:NSStringFromCGPoint(CGPointMake(contentOffset.x, contentOffset.y))];
in mainviewcontroller
-(void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(navigateToTableViewPosition:) name:#"changepostion" object:nil];
}
- (void) navigateToTableViewPosition:(NSNotification *)notification
{
contentOffset =CGPointFromString([notification object]);
NSLog(#"Position set method gets called...");
NSLog(#"%#", NSStringFromCGPoint(contentOffset));
[mainScrollView setContentOffset:contentOffset animated:YES];
}
You can't set the properties of a view which is not visible. If you are using iOS5+ you can implement the offset setting in the completion in the view dismiss completion block.
Use delegate for backward messaging in view controllers.
Refer Basic Delegate Example link for more reference.
Your are making new instance of viewcontroller which will call method but will have no effect.

Dismissing Multiple View Controllers in iOS 5

In iOS 4, if you want to dismiss two nested modal view controllers, the following code works:
[[[[self parentViewController] parentViewController] parentViewController] dismissModalViewControllerAnimated:YES];
However in iOS 5 this method no longer works. Does anybody know how to achieve this result in iOS 5?
If you call dismissViewControllerAnimated: on the view controller that presented the first modal, you'll dismiss of both modals at once. So in your 2nd modal you would do the following:
[self.presentingViewController.presentingViewController dismissViewControllerAnimated:YES completion:NULL];
I have an app that closes nested modal view controllers through NSNotificationCenter. The notification is received by the VC that I want to navigate back to and all the VC's in between are gone.
In the deeper VC...
NSNotification * notification = [NSNotification notificationWithName:#"BACKTOINDEXNOTE" object:nil];
[[NSNotificationCenter defaultCenter] postNotification:notification];
In the VC I would like to go back to
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dismiss) name:#"BACKTOINDEXNOTE" object:nil];
// more init code
}
return self;
}
-(void)dismiss
{
[self dismissModalViewControllerAnimated:YES];
}
This works on iOS 5 device with a project deployed for 4.0+
I hope it helps. If you use this, it will scale to support more VC's in between your current VC and the one you want to dismiss to, without changing this code
For a stack of two modals call this baby from your delegate method on the initial presenter to jump back down the stack and nuke the presented VCs.
[self.presentedViewController.presentedViewController dismissViewControllerAnimated:NO completion:nil];
[self.presentedViewController dismissViewControllerAnimated:YES completion:nil];
Obviously it's a bit brittle because if you start adding more modals then things will break. Generally if you're doing a stack of controllers you would use UINavigationController, but for a couple of modals this does the trick and is a lot less complex than setting up notifications or even more delegates!

Refresh UIView from Subview

in my universal app, I have a UIControl View inside of a UIScrollView.
On pressing a setup Button, I add another View as subview, like this:
SetupController *setupview = [[SetupController alloc] initWithNibName:#"SetupView-iPad" bundle:nil];
[mainview addSubview:setupview.view];
The subview is displayed like expected.
In this SubView I have some Buttons, which allows the user to switch between settings.
The performed actions are saved in a local Database.
The problem is: On ButtonClick in the SubView, I have to refresh the mainview, to apply the changes. I've tested many ways to make this happen:
In the Subviews class:
[self.parentViewController.view setNeedsDisplay];
No result.
Then I tried to refresh the mainview by notification:
I added this to my subviews classfile, in the function that changes my settings.
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc postNotificationName:#"setEmoticon_NOTIFICATION" object:self];
}
Then I added the observer to my mainview in the ViewDidLoad method:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(refreshView) name:#"setEmoticon_NOTIFICATION" object:nil];
and created the function for this observer:
-(void)refreshView{
NSLog(#"Notification!");
[self.view performSelectorOnMainThread:#selector(setNeedsDisplay) withObject:nil waitUntilDone:YES];
}
In Log I get the "Notification!" on changing the settings. But whatever I try, no refresh.
I tried setNeedsDisplay, resignFirstResponder, [self viewDidLoad:] , but still nothing works.
Any ideas how to refresh my mainview?
What exactly do you need to do when refreshing? Do you need to redraw elements on screen, etc?
Do you have a custom refresh method you've implemented in the main view? I would setup a custom protocol in your sub view and before adding the sub view to the main view, I would add the sub view's delegate as the main view
SetupController *setupview = [[SetupController alloc] initWithNibName:#"SetupView-iPad" bundle:nil];
setupView.delegate = mainview;
[mainview addSubview:setupview.view];
Then handle the refresh in the main view call back.
Hell yeah, seems like I found a solution to refresh my superview from subview.
You only have to add the following code snippet to the desired action/method, that should perform the refresh, in your subview controller.
for (UIView* next = [self.view superview]; next; next = next.superview)
{
UIResponder* nextResponder = [next nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]])
{
[(UIViewController*)nextResponder viewWillAppear:YES];
}
}
In this case I reload the superview, using it's viewWillAppear method.
You can replace it with any method that fits your needs (viewDidLoad, viewWillDissapear etc.).
It may not be the best way to do this, but it works great for me.
Thanks for your answers. ;)

Dismiss modal view controller on application exit

I have a view controller (view A) presenting a modal view (B) when the user pushed a button and the view B has itself a button to present view C. My problem is that if the user exits the application when the view B or C is shown, the same view will appear next time the application is launched. Is there a way to dismiss the views B and C on exit or to show view A when the application starts?
Thanks for your help
I assume by close you mean when the application enters the background.
In your app delegate you can via the applicationDidEnterBackground: method dismiss your controller.
Best way would probably be to add an observer in your view controller class:
- (void) viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appClosing) name:#"appClosing" object:nil];
}
- (void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"appClosing" object:nil];
[super dealloc];
}
- (void) appClosing
{
[self dismissModalViewControllerAnimated:YES];
}
And post the notification in your app delegate:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"appClosing" object:nil];
}