I am in need of some help please. I have been stuck on this issue all day and I cannot find a fix. I will try and be as clear as possible with my problem.
I have 2 ViewControllers. One is called ViewController and the other is called GameViewController. In the ViewController I have my home screen menu and on the GameViewController I have the Game screen AND the Game Over screen. When you press play from ViewController it takes you to the Game, and then when you die you stay in the same GameViewController but in a Game Over view. If you press restart in the Game Over screen a segue takes you back modally to the Game Screen.
So here is the problem I am having.
When you die for the first time and go to the Game Over screen, you can click on home and it will take you home, BUT if you press restart, then die again (that's twice ) and then try to press home from the Game Over Screen it will NOT run the code inside the home button. The button actually works, I have an NSLog in their and it shows in the debugger screen. Weird I know. Please see below code that will show everything I have done to try and make this work.
In GameViewController.h //Above all the #imports
#class GameViewController;
#protocol GameViewControllerDelegate <NSObject>
-(void) GameViewControllerDidCancel:(GameViewController *)controller;
- (IBAction)homeBtn:(id)sender;
#end
And this just after the the close } in #Interface
#property (nonatomic, weak) id <GameViewControllerDelegate> delegate;
In GameViewController.m after the #Implementation
#synthesize delegate = _delegate;
And this in my homeBtn
- (IBAction)homeBtn:(id)sender {
NSLog(#"home btn pressed");
[self.delegate GameViewControllerDidCancel:self];
}
Then in my ViewController.h
#import "GameViewController.h"
#interface ViewController : UIViewController <GameViewControllerDelegate>
ViewController.m
When I press play
- (IBAction)playButton:(id)sender {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
UIStoryboard* sb = [UIStoryboard storyboardWithName:#"Main_iPad"
bundle:nil];
GameViewController* controller= [sb instantiateViewControllerWithIdentifier:#"GML"];
controller.delegate = self;
[self presentViewController:controller animated:NO completion:nil];
[audioPlayer stop];
}else {
UIStoryboard* sb = [UIStoryboard storyboardWithName:#"Main_iPhone"
bundle:nil];
GameViewController* controller= [sb instantiateViewControllerWithIdentifier:#"GML"];
controller.delegate = self;
[self presentViewController:controller animated:NO completion:nil];
[audioPlayer stop];
}
}
And the method that I try to call from my homeBtn is also in here
#pragma mark - GameViewControllerDelegate
- (void) GameViewControllerDidCancel:(GameViewController *)controller {
[self dismissViewControllerAnimated:NO completion:nil];
}
I am pretty sure that is it. So like I said before, the restart button is just linked to a segue to return back to the Game screen.
Please guys, I am really desperate now, I am completely out of ideas.
Thank you
Quite a convoluted view hierarchy and hard to understand.
What exactly does the restartGameButton do. Is it an unwind segue?
When you press play from ViewController it takes you to the Game, and then when you die you stay in the same GameViewController but in a Game Over view. If you press restart in the Game Over screen a segue takes you back modally to the Game Screen.
What is the GameOver view thing? Once you say its a view in the GameViewController, but then again you say if you press restart a segue takes you back modally to the Game Screen (i.e. GameViewController) which implies the GameOver Screen is also a ViewController.
Why use modal presentation when it is a actually a navigation stack that you want?
Best solution in my opinion:
Make 3 ViewControllers, one for the Start, one for the Game, one for GameOver. Embed the ViewControllers in a navigation controller and push and pop with this one instead of presenting many modal views on top of one another.
Related
I have a main view controller that is able to process gestures in an iPad app.
I launch a second view controller via:
wVC = [self.storyboard instantiateViewControllerWithIdentifier:#"vc_webView"];
[self presentViewController:wVC animated:YES completion:nil];
If I now gesture while that VC is showing, the gestures are not processed. How can I "pass" the gestures to the first storyboard for subsequent processing so that I don't need to rewrite the whole gesture functionality in the new VC?
OK so the answer is to use delegates.
For completion's sake, here is the way to do it:
Set up the first VC to be a delegate of the second, then in the second VC call the delegate function.
In original controller right before presenting the wVC:
wVC.delegate = self;
In the wVC.h file:
#protocol senddataProtocol <NSObject>
-(void)ProcessPasswordGesture:(NSInteger)iGest;
#end
#property(nonatomic,assign)id delegate;
In the wVC.m file:
#synthesize delegate;
[delegate ProcessPasswordGesture:<data>];
Hope this helps someone else!!
I have trouble w/ hiding toolbar when leave screen by segue.
Details: App has a few dozen screens, all of them are belonged the same navigation controller. A few screens have toolbar. For these a few screens in -(void)viewDidLoad I use
[self.navigationController setToolbarHidden:NO animated:NO];
and in -(void)viewWillDisappear:(BOOL)animated:
[self.navigationController setToolbarHidden:YES animated:YES];
so toolbar is visible only on necessary screens and the screen which needs toolbar controls the visibility. All work well when I navigate by "back" button.
The trouble starting when I try to navigate by segue like this (goto home & goto another branch).
[owner.navigationController popToRootViewControllerAnimated:NO];
[self performSegueWithIdentifier:SEGUE_NAME sender:self];
toolbar is stay visible in spite of calling -(void)viewWillDisappear which should hide toolbar.
are there any ideas how to perform these "ToolBarHidden" by right way.
thanks
PS: of course I could hide toolbar on every screen, but I want to avoid this unnecessary operations and want to know how to do it right.
**STEP1:** in your controller.h
#interface ViewController : UIViewController {
UIToolbar *bar;
}
#property (nonatomic, strong) IBOutlet UIToolbar *bar;
#end
**STEP2:** in your controller.m
#synthesize bar;
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.bar.hidden = NO;
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
self.bar.hidden = YES;
}
**STEP3:** connect in Intrface
hope this help you!
I am presenting a modal view using a storyboard segue set as Form Sheet.
The problem is, when I rotate the iPad after this view is displayed, the view is removed from the view/dismissed.
I have no idea why. It only seems to occur when starting in Portrait then rotating to Landscape.
If I start in Landscape then show the view then rotate it stays on the screen fine.
Any ideas?
EDIT ----
It also seems that full screen modal views are also dismissed after rotation!
There's nothing special going on in the presentation code, this is a full screen modal:
EditViewController *editView = [self.navigationController.storyboard instantiateViewControllerWithIdentifier:#"editViewController"];
editView.delegate = self;
editView.image = image;
editView.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:editView animated:YES completion:nil];
This happens on both iOS 6 and iOS 7
EDIT 2 ----
Forgot to mention, i'm presenting the modal from the left/master view controller of a UISplitViewController
late, but what it worked for me was just before
[self presentViewController:aController animated:YES completion:nil];
dismiss the master controller, adding this lines
[self.splitViewController setPreferredDisplayMode:UISplitViewControllerDisplayModePrimaryHidden];
[self.splitViewController setPreferredDisplayMode:UISplitViewControllerDisplayModeAutomatic];
and then present your controller
Get rid of: editView.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
That will solve the issue you are experiencing. Modal view controllers presented on iPad as a Form Sheet do not rotate correctly using that transition style.
its really hard to get the cause how and why this is happening as i found that this also happen with UIPopover also as when you rotate it UIPopover hide because ???
So if you want to keep your view then just call again your controller after rotation will do fine user experience
This is not a bug its a limitation on UISplitViewController. The problem exists when the masterViewController (which is a UIPopoverController) is able to be dismissed. Heres how it works with the assumption that your app does allow the masterViewController to be dismissed in portrait and does not allow in landscape.
In portrait while the masterViewController is visible, if you were to present a modal from a viewController in the masterViewController and then rotate to landscape, the modal would disappear in iOS7 and the app would not rotate in iOS8. iOS8 introduces a condition to prevent the bad experience of iOS7. iOS7 losses the modal in the process of moving the masterViewController from the popoverController to a contained viewController in the splitViewController.
The modal needs to be presented from the splitViewController and not from the masterViewController. The only problem with this is the modal gets presented below the masterViewController in portrait. My solution is to dismiss the masterViewController and then present the modal.
There are several ways to achieve this result depending on how complex your code needs to be. Here's how I do this in my app.
I first subclass UISplitViewController in order to have a reference to the popoverController. I use delegate forwarding in order to access the delegate methods internally and externally. Heres the .h
// MainSplitViewController.h
#import <UIKit/UIKit.h>
#interface MainSplitViewController : UISplitViewController
#property (nonatomic, weak, readonly) UIPopoverController* primaryColumnController;
#end
And the .m
// MainSplitViewController.m
#import "MainSplitViewController.h"
#interface MainSplitViewController () <UISplitViewControllerDelegate>
#property (nonatomic, weak) id<UISplitViewControllerDelegate> externalDelegate;
#property (nonatomic, weak) UIPopoverController* primaryColumnController;
#end
#implementation MainSplitViewController
- (instancetype)init {
self = [super init];
if (self) {
self.delegate = self;
}
return self;
}
#pragma mark - Split View Controller Delegate
- (void)splitViewController:(UISplitViewController *)svc popoverController:(UIPopoverController *)pc willPresentViewController:(UIViewController *)aViewController {
self.primaryColumnController = pc;
if ([(id)self.externalDelegate respondsToSelector:_cmd]) {
[self.externalDelegate splitViewController:svc popoverController:pc willPresentViewController:aViewController];
}
}
#pragma mark - Delegate Forwarder
- (void)setDelegate:(id<UISplitViewControllerDelegate>)delegate {
[super setDelegate:nil];
self.externalDelegate = (delegate != self) ? delegate : nil;
[super setDelegate:delegate ? self : nil];
}
- (BOOL)respondsToSelector:(SEL)aSelector {
id delegate = self.externalDelegate;
return [super respondsToSelector:aSelector] || [delegate respondsToSelector:aSelector];
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
id delegate = self.externalDelegate;
return [delegate respondsToSelector:aSelector] ? delegate : [super forwardingTargetForSelector:aSelector];
}
#end
Next I create a class extension on UIViewController
// UIViewController+Popover.h
#import <UIKit/UIKit.h>
#interface UIViewController (Popover)
- (UIViewController *)popoverPresentingViewController;
#end
And the .m
// UIViewController+Popover.m
#import "UIViewController+Popover.h"
#import "MainSplitViewController.h"
#implementation UIViewController (Popover)
- (UIViewController *)popoverPresentingViewController {
UIViewController* viewController = self;
if ([self.splitViewController isKindOfClass:[MainSplitViewController class]]) {
viewController = self.splitViewController;
MainSplitViewController* mainSplitViewController = (MainSplitViewController *)self.splitViewController;
if (mainSplitViewController.primaryColumnController.popoverVisible) {
[mainSplitViewController.primaryColumnController dismissPopoverAnimated:YES];
}
}
return viewController;
}
#end
Now where ever you present the modal, instead of calling [self presentViewController: ... call [self.popoverPresentingViewController presentViewController: ...]. Remember to import UIViewController+Popover.h
your question came closest to my bug, On returning from modalView the parentView will switch to orientation in which the application was opened.
Visually it appears that the modal view is rotated and then returns.
I solved it by removing the modal view altogether, and using
[self.navigationController pushViewController: <the View(not modal now)>]
instead of using-
[self presentViewController:<Modal View>]
I think this is because the navigation controller doesn't own the Modal View, hence it reloads - when returning from the modal view - to incorrect orientation
Problem:
When presenting a view controller modally, it gets dismissed on rotation.
Approach:
Set the UISplitViewControllerDelegate
Use the UISplitViewControllerDelegate methods
Hold a reference to your modal view controller in an instance variable
Check if your modal view controller's presenting view controller exists.
If it exists, nothing needs to be done, else just present without any animation.
UISplitViewControllerDelegate methods:
func primaryViewController(forCollapsing splitViewController: UISplitViewController) -> UIViewController? {
if let someModalViewController = someModalViewController,
someModalViewController.presentingViewController == nil {
let masterViewController = viewControllers.first
masterViewController?.present(someModalViewController,
animated: false) {
}
}
return nil
}
func primaryViewController(forExpanding splitViewController: UISplitViewController) -> UIViewController? {
if let someModalViewController = someModalViewController,
someModalViewController.presentingViewController == nil {
let masterViewController = viewControllers.first
masterViewController?.present(someModalViewController,
animated: false) {
}
}
return nil
}
Note:
UISplitViewControllerDelegate has quite a methods, it can be daunting initially, if you spend some time experimenting, you can achieve what you want.
It has fine grained access.
I'm very late but try this. It works for me.
[self.splitViewController presentViewController:editView animated:YES completion:nil];
First, have a look at this screenshot of my storyboard:
It is an application for a sound map. The user can either record a new field recording or chose an existing one from the library and upload them. The ViewController where the user has to add a title/description etc. (or modifies them when coming from the library) is presented modally (on the top right).
If the user choses to cancel this and to delete the recording, he shall return to the recording screen, if he comes from there, otherwiese to the library. If he choses to save/upload the recording, he shall return to the library, where the upload progress will be displayed.
How can I come back to the desired ViewController independently of the ParentViewController that I come from?
I thought about unwindSegue, but that doesn't work for my layout. Then I figured out a dirty workaround where I changed the selected tab of the TabBarController. But then I also want to set up the delegate correctly to pass some data.
Any thoughts are welcome!
I don't think changing the selectedIndex of the tab bar controller is a "dirty workaround" -- that's the way to do it, given your set up. It appears that you would want the EntryViewController (Library) to be the delegate of the EntryDetailViewController. If that's so, you could set up the delegate in the viewDidLoad method of the EntryDetailViewcontroller like this:
#import "DetailViewController.h"
#import "TableController.h"
#interface DetailViewController ()
#property (strong,nonatomic) UITabBarController *tbc;
#end
#implementation DetailViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.tbc = (UITabBarController *)self.presentingViewController;
self.delegate = (TableController *)[(UINavigationController *)self.tbc.viewControllers[1] topViewController];
}
- (IBAction)saveAndUpload:(UIButton *)sender {
[self.tbc setSelectedIndex:1];
[self.delegate saveRecording:#"test recording"];
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)delete:(UIButton *)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
In my example, I just have two buttons to either save or cancel, and my TableController would be the same as your Library controller.
I currently have a button within an app that I am working on, and when I press the button it seems to not call the method when it is pressed. I thought added all the proper code, and wired everything up correctly, but that doesn't seem to be the case.
ViewControllerRootHome.h
#property (weak, nonatomic) IBOutlet UIButton *btndev;
- (IBAction)showDev:(id)sender;
ViewControllerRootHome.m
#synthesize btndev = _btndev;
- (IBAction)showDev:(id)sender {
NSLog(#"dev button pressed");
//dev button pressed
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewControllerDev *dev = (ViewControllerDev *) [storyboard instantiateViewControllerWithIdentifier:#"dev"];
[self presentModalViewController:dev animated:YES];
NSLog(#"dev button press End");
}
The execution is not even reaching the firs log statement in the method.
Things are wired up as such,
On a side note the button that reads "Logs" seems to be loading the scene fine, but for whatever reason "dev" button does not want to call the method / load the scene.
Ohh the source code for everything can be found here, https://github.com/ipatch/KegCop
That button works fine, so if you're having problem, I'd refer you to AliSoftware's comment about making sure to clean your build. But if you look to the left of the method name, showDev, you'll see a solid bullet when the IBOutlet is hooked up correctly:
If you compare that to another method, this one, saveMasterEmail is not currently hooked up correctly, evidenced by the empty circle to the left of the method:
Unrelated, your code:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewControllerDev *dev = (ViewControllerDev *) [storyboard instantiateViewControllerWithIdentifier:#"dev"];
[self presentModalViewController:dev animated:YES];
could be simplified to:
UIViewController *dev = [self.storyboard instantiateViewControllerWithIdentifier:#"dev"];
[self presentModalViewController:dev animated:YES];
Or, even easier, define a segue for that button and you don't need to do anything with code.
Finally, I'd make sure you avoid circular references between your scenes. You shouldn't be doing a modal segue (or presentViewController or presentModalViewController) from A to B and then again from B to A. If you do a modal segue from A to B, you should call dismissViewControllerAnimated to return back to A. Otherwise, you'll have two instances of A floating around.