Dismissing modalView - ios6

i came across of an issue with dismissmodalview. it is apparently depreciated in ios 6. can anyone suggest a fix. i tried this code but still brings up the warning.
if ([[self parentViewController] respondsToSelector:#selector(dismissModalViewControllerAnimated:)]){
[[self parentViewController] dismissModalViewControllerAnimated:YES];
} else {
[[self presentingViewController] dismissViewControllerAnimated:YES completion:nil];
}
can anyone suggest anything. basically i am trying to dismiss the modal view which i am using in my view controller.
adrian

Have you tried? I'm using this in one of my projects with success.
[self dismissViewControllerAnimated:YES completion:nil];

You receive a warning because the selector is deprecated in iOS 6 and you use it. If you are targeting iOS 5 and above, you should not be using dismissModalViewControllerAnimated:.
If you really need to use it, you can call performSelector: like so:
if ([[self parentViewController] respondsToSelector:#selector(dismissModalViewControllerAnimated:)]){
[[self parentViewController] performSelector:#selector(dismissModalViewControllerAnimated:) withObject:#YES];
}
A word of advice: it is not considered good practice for a view to close itself. You should create a delegate protocol for your modal view controller, and have the presenting view controller by the modal's delegate.

Related

ViewController added as a subView getting released too soon

I am trying to optimize the code in my app. I have quite a few ViewControllers which all use a common "keypad".
I wanted to extract the keypad into a separate ViewController and then include it into the existing ViewControllers.
This way I could obliviate duplicate code (which was needed to deal with the reactions from the keypad) in the separate ViewControllers.
So in the KeyPadVC I have methods set up that look something like this.
-(IBAction)keyPadKeyPressed:(id)sender
{
[self.delegate interpretKeyPressed:[[sender titleLabel] text]];
}
In my "parent" ViewControllers I include the keypad by adding a subview to a plain UIView that I placed in Interface Builder (so that I have a visual placeholder) and hooked up to the variable keypadView.
-(void) viewDidLoad
{
[super viewDidLoad];
KeyPadViewController *kpVC = [[KeyPadViewController alloc] init];
[kpVC setDelegate: self];
[[self keypadView] addSubview:[kpVC view]];
}
This displays fine, but when I press a button on the KeyPadViewController I get a zombie object because the object was already released. I then tried to declare KeyPadViewController *kpVC in the #interface and tried a self instantiating method like:
-(KeyPadViewController *)kpVC
{
if (!kpVC) {
kpVC = [[KeyPadViewController alloc] init];
}
return kpVC;
}
I obviously modified the viewDidLoad method, but the result was always the same. The object gets released too soon. If I add NSLogs I can see that -(IBAction)keyPadKeyPressed from the KeyPadVC never gets called, because it KeyPadVC was already released.
What am I doing wrong? I am using ARC and iOS6 SDK.
Thanks
PS: this is pseudo-code to make things shorter - hope there are no typos - if so then that is not the issue. :)
KeyPadViewController *kpVC = [[KeyPadViewController alloc] init];
[kpVC setDelegate: self];
[[self keypadView] addSubview:[kpVC view]];
self.kpVC = kpVC;
That retains the view controller.
However, what you are doing is totally illegal because you are not using parent-child architecture. See my answer here: https://stackoverflow.com/a/15962125/341994
You can add a subview to your view, but you must not add a view controller's view as a subview to your view without going through the elaborate parent-child architecture. And you are not doing that.
I explain the parent-child architecture here:
http://www.apeth.com/iOSBook/ch19.html#_container_view_controllers

iOS 6 rotation issues with UINavigationcontroller from a XIB

I'm trying to allow some views to rotate in my app (just two), I've done this before successfully, subclassing UINavigationcontroller and overriding the corresponding methods. The problem this time is that I'm using a third party project that creates the navigation controller from a .xib file (not programmatically). I changed the class in the .xib file in order to use my custom navigation controller and it does, but for some reason is ignoring override methods like shouldAutorotate and supportedInterfaceOrientations
Any ideas?
Thanks
I'm still looking for a more elegant way to handle this, but I'll share a little hack that has worked for me. If you find something better, please let me know.
An approach that has worked ok for me in apps with complex navigation trees, is to install the custom subclass for the very first navigation controller or very first view controller if you don't have a nav controller that is in your app. that is the one that is going to receive the shouldAutoRotate calls.
It sounds like you have done this already.
Now you need to insert your own logic in the shouldAutoRotate section. What I've done is to use a BOOL in the appDelegate as a place to store if a view controller should autorotate. Basically it works like this:
appDelete:
#property (nonatomic,assign) BOOL allowAutoRotationForThisViewController;
custom navigation controller at head of stack:
- (BOOL)shouldAutorotate {
AppDelegate *a = [[UIApplication sharedApplication] delegate];
return a.allowAutoRotationForThisViewController;
}
rotatable view controller:
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
AppDelegate *a = [[UIApplication sharedApplication] delegate];
a.allowAutoRotationForThisViewController = YES:
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
AppDelegate *a = [[UIApplication sharedApplication] delegate];
a.allowAutoRotationForThisViewController = NO:
}
I'm not sure why they changed this in ios6, it seems much more difficult, especially when you have complex navigation structure. I have an app with "slide" type controller at the top of the stack, a tabbar controller and stacks of navigation controllers. Trying to pick through all of those to get a web view on the bottom of the stack to rotate, or get them to respond to the top level controller is very complicated. So I've used this approach.
Now, one thing you have to consider - is that if the BOOL is turned to NO, then no rotation will occur - so one gotcha is if your rotatable view controller pops back to its parent while its rotated. Then the parent will be rotated and the rotatable view controller will have set the rotation value back to no. I solved this in my approach by preventing it to dismiss unless it was in portrait mode - basically I disabled the "back" button in landscape mode.
this works like this:
- view comes on screen - sets rotation to YES
- rotation occurs
- at this point, the view that is being rotated will get the following selector call:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
so, inside of that call, you would configure your local view and do anything you need to do to setup the view for the orientation change - like self.navigationController.hidesBackButton = YES;
hope that helps, and like I said, its a bit of a hack and I'm looking for something more elegant.
best of luck

How to handle UINavigationControllers and UITabBarControllers iOS 6.1

I need a good explanation how can I handle the UINavigationControllers and the UITabBarControllers on iOS6.1 with StoryBoards.
When I load my app (1st ViewController) I need if (FB login = success) it jumps with segues to the 2nd ViewController automatically. Here I think I can't use a UINavigationController like root, apple's HIG don't like it.
I need a UITabBarController that connects to 3 UICollectionViewControllers (one tab for each one). I have to put the UITabBarController like root? If yes, how can I handle the others Viewontrollers between them? Like this:
I need a custom BarButtonItem (like the "Delete All" that you can see on the image 2) on each CollectionViewController, I need to use a UINavigationController for each one?
Let's assume you are happy to use unwind segues (if not there are many ways to do without).
1 When I load my app (1st ViewController) I need if (FB login = success) it jumps with segues to the 2nd ViewController automatically. Here I think I can't use a UINavigationController like root, apple's HIG don't like it.
You 1st VC (lets call it the loginVC)..
- should NOT be contained in the Navigation Controller.
- should be set as the application's initialViewController
Your 2nd VC (lets call it your startVC)
- SHOULD be contained in the Navigation Controller
- in that Navigation Controller's Identity Inspector, assign a storyboardID: #"InitialNavController"
In your App Delegate, let's have a loggedIn BOOL property:
#property (nonatomic, assign) BOOL loggedIn;
Now, in your LogInViewController...
In viewDidAppear check to see if we are already logged in, if so, navigate immediately to your startVC:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if ([(AppDelegate*)[[UIApplication sharedApplication] delegate] loggedIn]) {
UINavigationController* navController =
[[self storyboard] instantiateViewControllerWithIdentifier:#"InitialNavController"];
[self presentViewController:navController
animated:NO
completion:nil];
}
}
It's important that this is placed in viewDidAppear, not (for example) in viewDidLoad - the segue won't work unless the initial view is properly initialised and onscreen.
Make an unwind Segue (and declare it in loginVC's #interface) … loginVC will be the destination VC if users log out.
- (IBAction)unwindOnLogout:(UIStoryboardSegue *)segue
{
[(AppDelegate*)[[UIApplication sharedApplication] delegate] setLoggedIn:NO];
}
(corrected - removed this line:
[[self presentedViewController] dismissViewControllerAnimated:YES
completion:nil];
we don't need to dismiss as the segue has already done that behind the scenes. This is redundant and logs an error message)
In other viewControllers, whereever appropriate you can make a 'logout' button. CTRL-drag from that button to the 'exit' symbol at the bottom of the ViewController in the storyboard, and you will be able to select this segue as the unwind segue.
2 I need a UITabBarController that connects to 3 UICollectionViewControllers (one tab for each one). I have to put the UITabBarController like root? If yes, how can I handle the others Viewontrollers between them? Like this:
I think you are trying to work out how the tabBarController relates to the NavigationController in the previous viewController (startVC). The answer is, it shouldn't - you really don't want to embed the Tab Bar VC in the previous Nav Controller as it will create weird situations for the Tab Bar's child viewControllers.
The navigation from startVC to the tabBarVC should be via a modal segue, NOT a push segue.
You can make another unwind Segue in startVC to facilitate return from your tabBarController's viewControllers:
- (IBAction)unwindToInitialNavFromModal:(UIStoryboardSegue *)segue {
}
(corrected - removed this line:  
[[self presentedViewController] dismissViewControllerAnimated:YES completion:nil];  
this method doesn't need any content to perform the dismissing)
3 I need a custom BarButtonItem (like the "Delete All" that you can see on the image 2) on each CollectionViewController, I need to use a UINavigationController for each one?
You won't get a Navigation bar in your tabBarVC by default.
You can provide one in two ways
- embed each child viewController in it's own Navigation Controller;
- manually drag a navigation bar to EACH child viewController's scene.
Either is fine, it really just depends whether you will want navigation to other ViewControllers.
You can then add a barButtonItem on the left or right to connect up to the initialVC's unwind segue (CTRL-drag to the 'exit' symbol).

Popover not dismissing after didSelectRow segues to another view

I am in Xcode 4.5, with an iOS6 target.
Preamble: I have a libraryView (presenting view controller), with a popover containing a search. After the presentation of the search results, tapping a row dismisses the library and segues to entryView. That all works just fine.
My issue: upon closing entryView and returning to the libraryView, the search popover is still visible.
I have tried a number of different methods to remedy this:
I have added a Notification observer in the segue to the search popover, posted a Notification from the search controller, posted from the entryView to the following method located in the libraryView. And, yes, libraryView does have addObserver for this method:
- (void)searchComplete:(NSNotification *)notification
{
NSLog(#"SearchPopover dismiss notification?");
[_searchPopover dismissPopoverAnimated:YES];
}
I have added in testing...
if (_searchPopover.popoverVisible)
{
[_searchPopover dismissPopoverAnimated:YES];
}
To viewDidLoad, viewWillAppear, viewWillDisappear, awakeFromNib... all in the library. I have the searchPopover as a property and have tried it as an ivar.
Nothing I've tried dismisses the popover before the segue or after the return.
Anyone have any ideas? Help would be much appreciated!!!
I have found a solution to this issue... found it at this answer: iOS Dismissing a popover that is in a UINavigationController
But, there was a small additional step to make... correct a typo in the answer and change the "dismissPopover" method into an NSNotification Method. I added a segue for the popover, which normally isn't necessary. The key though, is the setting of the parent's definition of the popover to the UIStoryboardPopoverSegue.
Then, use notification to let the parent know it should dismiss.
From the parent view:
- (void)viewDidLoad
{
... other loading code...
[NSNotificationCenter.defaultCenter addObserver:self selector:#selector(dismissSearch:) name:#"dismissSearch" object:nil];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"SearchSegue"])
{
[segue.destinationViewController setDelegate:self];
_searchPopover = [(UIStoryboardPopoverSegue *)segue popoverController];
}
}
- (void)dismissSearch:(NSNotification *)notification
{
NSLog(#"SearchPopover dismiss notification?");
[_searchPopover dismissPopoverAnimated:YES];
}
In my child view (SearchView). Ideally, it would be in the didSelectRowAtIndexPath. I found it also worked in the segue to the view where it will display the searched item, which is where I would normally place an addObserver. In this case, it is a postNotification...
[NSNotificationCenter.defaultCenter postNotificationName:#"dismissSearch" object:nil];
One last note: I was using an IBAction that checked for popover visibility when tapping the button... display or dismiss. Found that having this as well as the other method was causing the popover to dismiss immediately upon tapping the button! Commenting out the if/else check for visibility solved that!
Thanks for rdelmar for leading me on this path!

Nested modal view controller gives strange message under iOS6?

I am presenting a modal view controller from another modal view controller, and this worked fine under all iOS versions prior to iOS6. But under iOS6 I am getting the following warning message in the emulator:
Warning: Attempt to present <UINavigationController: 0x14e93680> on <UINavigationController: 0x9fc6b70> while a presentation is in progress!
The modal view controller is not shown if this warning appears. Basically I am using code like this to show the modal view controller:
WebAuthViewController *authController = [[WebAuthViewController alloc] initWithNibName:nil bundle:nil];
authController.challenge = challenge;
authController.delegate = self;
UINavigationController *aNavController = [[UINavigationController alloc] initWithRootViewController:authController];
[self presentModalViewController:aNavController animated:YES];
[aNavController release];
[authController release];
The view that is already shown is a UIWebView also shown in a modal view, like this:
WebViewController *addController = [[WebViewController alloc] initWithNibName:nil bundle:nil];
addController.urlToLoad = [NSURL URLWithString:urlString];
addController.delegate = self;
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:addController];
navigationController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:navigationController animated:YES];
[navigationController release];
[addController release];
The apple docs still suggest that one is supposed to be able to stack navigation controllers like this, so I am at a loss to explain why this happens. Any hints?
A view controller can only present a single view controller. This might have been allowed before, but is probably enforced in iOS6 due to an internal reorganization (presentModalViewController:animated: is deprecated in iOS6). It's time to change your organization of view controllers. Perhaps it is possible to introduce a navigation controller which is to be presented. If there is already a presented view controller, push your next one to the navigation stack.
OK, that was really dumb - I forgot the quotes around the URL for curl and the shell did execute the command in the background without arguments.

Resources