I am making a little game with few mini games. On the 1.VC I have UIButtons inside a scrollView to choose the mini game. When the miniGame is loaded (a UIVC) it loads a SKView with the chosen SKScene game.
This is my App view Setup:
1.
UIViewController -> 1.1. ScrollView -> 1.1.1. UIButton -> 1.1.1.1. UIViewController -> 1.1.1.1.1. SKView -> 1.1.1.1.1.1. SKScene (the 1. mini game).
In the 1.1.1.1. VC I have a backButton (UIButton) which returns me back to the 1.VC.
Since I added the backButton in 1.1.1.1.VC I can always return at any moment in the first mini game to 1.VC.
PROBLEM:
The only way to go back from 1.1.1.1. (or any deeper) to 1. is by clicking the UIButton. I want to go back programmatically. When the endGameVC (a SKScene) is presented I want it to "seque" or "present" back to the 1.VC automatically (using a NSTimer).
So far I have tried:
1.
[self.view removeFromSuperview];
Result:
Black Screen - cant do anything from here
2.
[self.view presentScene:nil];
Result:Gray screen - I removed the skview and could go back with the button click
3.
[self.view removeFromSuperview];
[self.view addSubview:[[WBMGamesDataManager sharedInstance] tempy]];
Result:
-[WBMMainVC superview]: unrecognized selector sent to instance 0x9ac4c30
2014-04-04 11:54:06.317 KinderApp[1940:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[WBMMainVC superview]: unrecognized selector sent to instance 0x9ac4c30'
... other stuff that I tried didn't get me anywhere.
Any idea or solution is welcome. If you know what I am doing wrong, or if the whole approach is fishy I am open for comments/critics. Thank you :).
I am assuming you are using segues on way of doing this is by popping the current view controller
[self.navigationController popViewControllerAnimated:YES];
but for this to work you need to call it from the UIViewController that hosts the SKView so depending on where you need to go back programatically, pass a reference of the UIViewController to that desired view, and create a method that will simply invoke the above line
/////in UIViewController
-(void)popIt{
[self.navigationController popViewControllerAnimated:YES];
}
////in the view where you want to simulate the back method
[referenceToVC popIt];
hope this helps, feel free to ask if you anything is unclear.
Found the solution:
In my:
#implementation MiniGameConcept
which is a SKScene I have put the following line of code at the place where I used my timer/counter where I needed the game to end:
[self.view.window.rootViewController dismissViewControllerAnimated:YES completion:nil];
This leads me to the 1. VC which is my main screen where the player can choose the mini game set.
It worked for me. I also found a way to go one step back, but this basically gets me to the rootVC. Console output with NSLog shows the VC name.
Related
I'm new to programming on xCode. I've made a login screen in a snapchat format. Currently, I started with two view controllers one for login and then signup but I changed it so I start with an intial view controller that has two custom buttons I made. View the picture. I want it to be that so when you clean the login button it takes you to the login screen and when you click the sign up button it takes you to the sign up screen.
I'm not sure what I'm doing wrong. I want![enter image description here][1]
My app delegate has the following:
http://i.stack.imgur.com/8fWT4.png
http://i.stack.imgur.com/b9mfN.png
I just don't understand as to why I keep getting the following error: Scene is unreachable due to lack of entry points and does not have an identifier for runtime access via -instantiateViewControllerWithIdentifier:.
Unless your segueing to the view in question, you would need to give it a storyboard identifier to connect the nib with the related class. Go into the storyboard and ensure you've set the class and storyboard id. If you're segueing to the class, ensure you give your segue an identifier. But if not, you can then do something like:
YourViewController *vc = (YourViewController*)[self.storyboard instantiateViewControllerWithIndentifier:#"YourStoryboardIdentifier"];
[self presentViewController:vc animated:YES completion:^{}];// Or however you want to present it. ie pushing onto the navigation stack
You can use this code:
WaitingRoomVC *ivc=[self.storyboard instantiateViewControllerWithIdentifier:#"waiting"];
[self.navigationController pushViewController:ivc animated:YES];
I have an app based on a UISplitViewController that shows an ActionSheet in the MasterViewController of the Split. Before iOS 5.1, I had no problems presenting the action sheet in the popover presented by the split, but now, apparently there is something wrong with the new "slide-in" way to show the MasterController.
The thing is that when I'm trying to present the ActionSheet, using any [actionSheet show..] method, the app crashes with the following error (The exact Assertion is the following).
*** Assertion failure in -[UIActionSheet presentSheetInPopoverView:], /SourceCache/UIKit_Sim/UIKit-1914.84/UIActionSheet.m:1816
sharedlibrary apply-load-rules all
Error in re-setting breakpoint 1:
Catchpoint 2 (throw)Error in re-setting breakpoint 1:
Error in re-setting breakpoint 1:
Current language: auto; currently objective-c
I google this for a while, but no substantial answers.. some people say it can be a bug in the
new SplitViewController...
Ideas?
Thank you in advance!
UPDATE: I posted a possible generic workaround, check it out. If it works for you, leave a comment.... If its ok, I will mark it as correct in a couple of days
Based on the above, and with massive respect to the Apple engineer who helped me at WWDC, here is the solution which not only works around this bug, but also points the popover at the right button.
if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]))
{
[actionSheet showFromBarButtonItem:self.actionSheetBarButtonItem animated:YES];
}
else
{
CGRect windowsRect = [self.navigationController.toolbar convertRect:self.actionSheetBarButtonItem.customView.frame toView:self.view.window];
[actionSheet showFromRect:windowsRect inView:self.view.window animated:YES];
}
I have this same problem too.
One workaround which prevents the crash at least is to show your UIActionSheet like this:
if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
[self.actionSheet showFromBarButtonItem:sender animated:YES];
} else {
[self.actionSheet showInView:self.view.window];
}
So in portrait mode, the action sheet is displayed in the center of the window. Not ideal, but at least it doesn't crash. And when in landscape mode, it behaves as normal.
Just as omz commented, it seems this issue has been solved in iOS 5.1.1 by apple.
So I decide to just add it to known issue section of change log for my app, and the workaround is to suggest the users to upgrade to iOS 5.1.1.
Another option to keep the popover affect of pointing at a particular option, you can actually do the following:
1. Create your own UIPopover
2. Create your own UIViewController inside of the UIPopover.
3. Display the UIActionSheet inside the newly created UIViewController.
4. SetPopoverContentSize from the UIActionSheet's Size.
5. Lastly, Wire your UIActionsheet's Clicked method to dismiss the popover.
Takes a little more code, but gives you the same functionality you had before for the most part, and has a cool little slide-in effect for the UIActionsheet.
I think the following is a generic solution based on Tap Form's answer:
CGRect windowsRect = [actionSheetContainerView convertRect:viewToPresentActionSheet.frame toView:actionSheetContainer.window];
[actionSheet showFromRect:windowsRect inView:actionSheetContainer.window animated:YES];
This will resent the actionSheet in the window, but pointing the right direction
my app has tabBarController with 3 views and in one of them I want to popup a web browser with the ability to return back to the application. To do that I am using UINavigationController.
In myAppDelegate.h I have defined the property UINavigationController *nav and in myAppDelegate.m I have #synthesize nav.
In the class where the webPopup function resides upon pressing the button my code comes to this function.
- (IBAction)showWeb:(id)sender {
myAppDelegate *app=[[UINavigationController alloc] initWIthRootViewController:self];
// because I want to return back to the same view
webController *web = [[webController alloc] initWithStyle:UITableViewStypeGrouped];
[app.nav pushViewController:web animated:YES];
app.nav.view.frame = CGRect(,0,320,430);
[self.view.window addSUbview:app.nav.view];
}
The web popup occurs but it is moved vertically, when I press "back button" my former view appears as well and it is also shifted vertically from what it was before.
After going back and forth few times the thing hangs.
Questions:
1. what can cause the shift?
2. how to avoid when I go "back" to see the title(test from the "back"button, I think this might cause a shift when I go back.
3. how to find out why it hangs after few attempt?
Thanks.
Victor
The line:
myAppDelegate *app=[[UINavigationController alloc] initWIthRootViewController:self];
makes no sense to me. Surely your compiler is warning you about that? What is "myAppDelegate" defined as? Classes should have a capital letter at the front, by the way.
Also, the line
[self.view.window addSUbview:app.nav.view];
is highly suspect, because the UIWindow for your application should have only one child UIView. You shouldn't just add more views willy nilly and expect things to work. It is possible to change the child UIView by removing the old one and adding a new one, but you don't seem to be doing that. Having more than one child UIView of UIWindow gets you into trouble very quickly -- for example, device orientation changing can break.
I'm not exactly clear as to why the app delegate (or the window for that matter) needs to be messed with at all to do what you are trying to do. Sounds like you should just be using standard Nav View Controllers and Web Views.
Also, you are alloc init'ing w/o any memory management.
How can i push a TabBarController to a View?
The App is View-Based. In the App I have a XIB with a TabBarController and I want to push the TabBar to the display. I have done everything, and now I don't know what I'm doing wrong.
Here is the code that should push the TabBarController:
- (void)showTabBar {
TabBar *tbc = [[TabBar alloc] initWithNibName:#"TabBar" bundle:nil];
[self presentModalViewController:tbc animated:YES];
[tbc release];
}
At this moment there is an empty view in the XIB. So the display is white when I run this code. There is also the TabBarController, which I want to see on the display.
Thanks for your help.
UPDATE:
I should say that these are two different classes. The class where the function "showTabBar" is based should push the TabBarController. The second class is TabBar. TabBar is the delegate of the TabBarController.
So I have modified the answer like this, but I have tried both:
[self presentModalViewController:tbc.tabBarController.view animated:YES];
The error now looks like this:
Incompatible Objective-C types 'struct UIView *', expected 'struct UIViewController *' when passing argument 1 of 'presentModalViewController:animated:' from distinct Objective-C type
UPDATE:
Hey the error has gone away. Cool. ;)
But the app is terminating. I've seen this behavior before, but I didn't know what to do.
The debugger console says this:
2011-01-27 15:49:32.629 Touch[3657:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Application tried to present a nil modal view controller on target <StartUp: 0x9300f20>.'
Because the UITabBarController class inherits from the UIViewController class, tab bar controllers have their own view that is accessible through the view property. When deploying a tab bar interface, you must install this view as the root of your window. Unlike other view controllers, a tab bar interface should never be installed as a child of another view controller.
http://developer.apple.com/library/ios/#documentation/uikit/reference/UITabBarController_Class/Reference/Reference.html
What this mean is, this is not "allowed" and Apple can or may in the future reject your application. UITabBarController is intended to be added to the root view(window) in your application delegate. If you want a tabbar to be pushed on later you need to use the UIToolBar with e.g. uisegmentedcontrol or the toolbar buttons.
Even though some apps does this(e.g Wordpress app) you will later find out that you will run onto some issues in the console when presenting viewcontrollers modally.(A warning will be given).
UPDATE: See my answer to this question first. This appears to be a bug. A minimal test case has been created and a report has been filed with Apple. (Fixed as of iPhone OS 3.1.)
Here's a puzzler from the "I'm so close!" department.
I have a Tab Bar-based iPhone app. Each tab features a UINavigationController with the usual suspects (nav bar, table view ... which in turn can lead to another VC, etc.).
Now, one of those lower-level VCs is to be used in portait and landscape modes. But there's a problem. Our landscape-friendly VC's shouldAutorotateToInterfaceOrientation: won't get called out-of-the-box! What to do?
Here's what we do. In my Tab Bar Controller, which I have implemented in its own file, I have this:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return [self.selectedViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
This ends up propagating the request to my landscape-friendly VC, which also responds to this message. All my other VCs don't implement this method, so they simply go with the default portrait orientation.
Problem solved!!! Yay!
Well, not quite. :(
Seems like things don't go so well when my landscape-friendly VC is invoked from within the depths of the tab bar controller's MoreNavigationController.
I decided to compare/contrast between a VC called from within one of the first four tab bar UINavigationControllers ... and that same VC called from within the MoreNavigationController. This is going to be a bit ultra-detailed, so bear with me. Hopefully the play by play proves useful for sleuthing things out.
When the app loads, there are several initial calls to the tab bar controller's shouldAutorotate... method. In these early cases, selectedViewController is nil. However, we eventually finish loading, the initial tab item is selected, and all is well.
Right. First, let's pick one of the first four tab bar items and drill down to our VC.
We'll pick third nav bar item, so that's the third nav controller. We drill down to our VC that supports rotation. A quick inspection confirms that the parent is indeed the third nav controller from our tab bar's view controller list. Good!
Let's rotate the device. The tab bar controller is asked to autorotate (see the above code). We observe that selectedViewController is also that third nav controller, plus the nav controller's top and visible view controllers are both set to our trusty VC that supports rotation.
Thus, the tab bar controller will forward the shouldAutorotate message over to the third nav controller ... but our rotation-friendly VC ultimately gets the message. (I'm not doing anything special here. Maybe the desired VC gets the message because it's the top and/or visible VC?) In any event, we rotate to landscape, things are resized, and all is well. "Great Success!"
Now let's hit that back button and pop the VC stack, leaving landscape mode in the process. The tab bar controller is queried again.
Time for a little aside here. The topViewController for our nav controller is still that rotation-friendly VC, but visibleViewController is now set to UISnapshotModalViewController! Heh. Never saw this one before ... but Erica Sadun has. Looks like it's for "disappearing view controllers" (which in this case is certainly true - it's disappearing alright).
As I keep stepping through, the visible VC stays as Snapshot, but the top VC eventually changes to the next VC on the stack, since my special VC is eventually gone. Fair enough.
So that's the scenario where everything works well.
Now let's try the same test, only this time we're going to go to the MoreNavigationController (the More tab bar item) and drill down to the same VC class as before. In my case it happens to be the 7th one in the tab bar controller's VC list.
We enter the rotation-aware VC and ... this time it gets asked to rotate directly! The Tab Bar Controller is not asked for permission to rotate at all. Hmm.
A quick check of the parent VC shows it is a MoreNavigationController. OK, that makes sense.
Now let's try to rotate the device. NOTHING GETS CALLED. None of our breakpoints get hit. Not in our VC. Not in our tab bar controller. (Huh?!?!)
O-kaaaay. Let's pop the stack, go back into the same VC and try to rotate again. Weird. NOW we get a call in the Tab Bar Controller asking for autorotation permission. Here, the selected Controller is our trusty Nav controller (#7), but this time its visibleViewController and topViewController are SET TO NIL!
Once we continue from here, a mysterious message appears in the debugger console:
Using two-stage rotation animation. To
use the smoother single-stage
animation, this application must
remove two-stage method
implementations.
Mysterious because I'm not using two-stage rotation animation! No SecondHalf method variants are in play anywhere in my source code.
Alas, my rotation-aware VC is never told that rotation is occurring (even though rotation does occur on-screen), so of course my view is all fouled up as a result. Mayhem and sadness ensue. :(
We won't even bother popping the stack at this point.
I think the View Controller doc hints at the possible problem:
If you want to perform custom
animations during an orientation
change, you can do so in one of two
ways. Orientation changes used to
occur in two steps, with notifications
occurring at the beginning, middle,
and end points of the rotation.
However, in iPhone OS 3.0, support was
added for performing orientation
changes in one step. Using a one-step
orientation change tends to be faster
than the older two-step process and is
generally recommended for any new
code.
I wonder if MoreNavigationController is still responding to the two-step process, and is thus tripping up any attempts to use the one-step process? Note that, if you respond to the two-step messages, the one-step variant will not work (again, per the docs). I'm not responding to them, but I have a sneaking suspicion something behind-the-scenes IS.
In fact, if I comment out the single-step method, and try to respond to willAnimateSecondHalfOfRotationFromInterfaceOrientation:duration:, I do get the memo! But it still doesn't pop off the stack very cleanly (in terms of visuals). Even stranger: willAnimateFirstHalfOfRotationFromInterfaceOrientation:duration: is NOT called, even when I tried to sneak a call to self (using the FirstHalf message) in shouldAutorotateToInterfaceOrientation:. It returns immediately during a trace, as if I never even defined it. Sigh.
So that's the play-by-play.
In summary, has anyone successfully handled one-step device rotation for a VC invoked from within a Tab Bar Controller's MoreNavigationController? Inquiring minds want to know!
Apple advises against subclassing UITabBarController, so I found an easy way to handle autorotation using categories instead. It doesn't fix your bug with the More... view controllers, but I think it's a more Apple-friendly way of getting the job done (and means less subclassing for you).
To make every tab in my application autorotate properly, I've defined -shouldAutorotateToInterfaceOrientation: in my custom view controllers, but they are all inside UINavigationControllers within a UITabBarController, so the message won't get sent down the chain to my VC until those two also respond. So I added the following lines to my app delegate files:
Added to the bottom of MyAppDelegate.h
#interface UITabBarController (MyApp)
#end
#interface UINavigationController (MyApp)
#end
Added to the bottom of MyAppDelegate.m
#implementation UITabBarController (MyApp)
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
return YES;
}
#end
#implementation UINavigationController (MyApp)
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
return YES;
}
#end
It appears we have a bug. I have created a reproducible, minimal test case, and reported it via Apple Bug Reporter (RADAR Problem 7139857).
Update: This has been fixed as of iPhone OS 3.1.
The essential problem is:
View controllers already on a
Navigation Controller stack do not
receive
willAnimateRotationToInterfaceOrientation:duration:
messages when a Tab Bar Controller's
'More Navigation Controller' is in
use.
This problem does not occur when the tab bar item view controllers are basic view controllers. Only when they are navigation controllers and only when the "More" navigation hierarchy is in use.
The console message (regarding two-stage rotation animation) suggests that something within the framework (the More Navigation Controller?) is still using a two-stage animation, even though single stage is now recommended as of iPhone OS 3.0.
That could explain why willAnimateRotationToInterfaceOrientation: is not called in that particular case. Per Apple's view controler documentation, this message will NOT be invoked when two-stage, first/second-half orientation messages are being responded to instead.
A slightly modified version of Victorb's answer which allows every single view controller to decide if it allows rotation.
Here as a gist for easier copying & forking
AppDelegate.h
#interface UITabBarController (MyApp)
#end
#interface UINavigationController (MyApp)
#end
AppDelegate.m
#implementation UITabBarController (MyApp)
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
UIViewController *selectedVC = [self selectedViewController];
if ([selectedVC respondsToSelector:#selector(shouldAutorotateToInterfaceOrientation:)]) {
return [selectedVC shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
}
//optimistic return - if you want no rotation, you have to specifically tell me!
return YES;
}
#end
#implementation UINavigationController (MyApp)
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
UIViewController *visibleVC = [self visibleViewController];
if ([visibleVC respondsToSelector:#selector(shouldAutorotateToInterfaceOrientation:)]) {
return [visibleVC shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
}
//optimistic return - if you want no rotation, you have to specifically tell me!
return YES;
}
#end