So I have a custom PresentationController which means I've set modalTransitioningStyle = .custom. This means it's not set to overCurrentContext or overFullscreen. It also means that definesPresentationContext is not is not applicable, since it's only used in those cases.
With this I present a custom popover-like view controller that takes like third of the screen.
Now the tricky part:
On this custom presented VC, I want to present another view controller with the same custom presentation that is larger (let's say half of the screen) than the presenting one.
Problem:
The frame of the second presented view controller is constrained to the size and location of the parent, so you will not see it entirely.
Even if I would manage to present it on a fullscreen view controller behind the first, the content would also appear behind, not in front of it.
So the question is: How do I present a view controller that has larger bounds than the presenting one using my own Presentation Controller?
I don't think any code would help here, but let me know if you think otherwise.
Related
Here is my storyboard showing a segue to a UINavigationController:
I fade out my game scene and call this segue from my SKScene like so:
mainVC?.performSegue(withIdentifier: "tySeg", sender: nil)
The nav controller moves a UIViewController on top of the game scene, as expected. But during the transition, I can see two new instances of the SKScene popping up in the background before the transition finishes and they are covered up by the new view controller. A print statement in my scene's didMove(to view:) method confirms that an SKScene object is appearing twice.
I can't figure out why my SKScene subclass is automatically being called twice after it triggers a segue. I also tried presenting the view controller like so but had the same issue:
mainVC?.present(navVC, animated: false, completion: nil)
I have a cludgy workaround in mind but I'd rather understand what is causing this to happen so I can prevent it. Any ideas?
UPDATE:
I suspect the reason has something to do with a passage from this Apple doc:
When presenting a view controller using the
UIModalPresentationFullScreen style, UIKit normally removes the views
of the underlying view controller after the transition animations
finish. You can prevent the removal of those views by specifying the
UIModalPresentationOverFullScreen style instead. You might use that
style when the presented view controller has transparent areas that
let underlying content show through.
When using one of the full-screen presentation styles, the view
controller that initiates the presentation must itself cover the
entire screen. If the presenting view controller does not cover the
screen, UIKit walks up the view controller hierarchy until it finds
one that does. If it can’t find an intermediate view controller that
fills the screen, UIKit uses the root view controller of the window.
You can see in my screenshot that GameViewController is my initial view controller. This is where I am calling the segue from. Like the doc says, maybe UIKit is removing the underlying content (the SKView that is presenting my game scene) when the segue is called. But I am using a full-screen presentation style, and UIKit requires that the scene's view controller must cover the screen. Since it was removed by UIKit, then UIKit goes up the view hierarchy and finds GameViewController which it calls to display.
I'm making a lot of assumptions, but seems like that might explain why my game scene is being recreated twice (or once... I had different results in my testing) while it calls a segue and waits for the transition to finish.
Also, I noticed that if I change the segue I'm using from Show to Present Modally, Over Full Screen, then the issue does not occur. That seems to support my guess.
Well, after our conversation it turns out that it uses the same instance of GameViewController.
So in your case you just workaround it. It's happening because your second view controller somehow not covering the entire screen (or it has opacity), and the system layout again the GameViewController. As you quoted:
When using one of the full-screen presentation styles, the view
controller that initiates the presentation must itself cover the
entire screen. If the presenting view controller does not cover the
screen, UIKit walks up the view controller hierarchy until it finds
one that does. If it can’t find an intermediate view controller that
fills the screen, UIKit uses the root view controller of the window.
For others i'll keep some of the old answer:
When you segue to a view controller you always creates a new instance. (So if you want to reuse the instance, don't use segues!)
When you present, you always shows it modally.
When you show you move to another view controller.
New to Swift. I've seen many tutorials on using PageViewController, but they always have the Page View taking up the whole screen.
I'm trying to implement a Page View functionality in only PART of my app, not the entire screen, so that other "static" elements (e.g. a ToolBar) can remain. Something that would look kinda like this...
https://imgur.com/9wM1vll --- (Need more rep. before embedding images)
...where swiping will cause different images to appear as seen in various PageViewController tutorials (e.g. http://www.appcoda.com/uipageviewcontroller-storyboard-tutorial/).
When I start with a Single View Application, I go to Storyboard and try to drag a "Page View Controller" from the Object Library into the ViewController frame, it just "bounces back", i.e. it won't let me add the Page View Controller.
Now, if I add the Page View Controller to the white space around the other View Controller, then this gets back to the tutorials where the PageViewController takes up the entire screen and I don't want that.
How does one achieve this?
Thanks.
Sorry if this is a dupe but I have tried & failed to find anything that answers my question directly. The closest are Implement PageViewController in TableViewController details or Adding a UIPageViewController to a subview, subview only shows edge of pageContentview, but these are not similar enough for me to comprehend, plus they're in Objective C which I've not yet learned.
When I start with a Single View Application, I go to Storyboard and try to drag a "Page View Controller" from the Object Library into the ViewController frame, it just "bounces back", i.e. it won't let me add the Page View Controller.
A page view controller is a view controller. Thus, to make its view occupy only part of the screen, you must obey the rules for view controllers in general: you need a custom parent view controller, and the page view controller must be its child view controller — and you must do the elaborate dance that this entails when you create the child and put its view into the interface.
To get the storyboard to do the dance for you, use a Container View and hook it by its embed segue to the page view controller:
(Still, in my opinion it is always better to learn to do the dance manually, in code, yourself.)
This might be a stupid question, but I'll shoot.
I made a little test project to test out a concept I had for a sliding view controller type of thing. I naively assumed I could create a UIView (let's call it peekView) with an outlet in a controller, and call something like [slidingControllerSlideFrom:self.view] from any visible view controller, the implementation of such being:
- (void)slidingControllerSlideFrom(UIView*)controllersMainView
{
// push side controller to top of navigation stack
self.peekView = controllersMainView;
// sliding animation
}
But there is no effect. No crash, no warning, no change of view in the pushed controller.
Of course, the pushed controller crashes when trying to add self's view as a subview, but assigning it to a predefined UIView just results in nothing.
So, why? And if a mere 'why' is not enough of a question- what happens when I try to assign one controller's view another controller's subview, and what was the reason for designing UIKit where you cannot set views from self.view?
To do that you have two options:
1 - If the controller in the peekView is always the same one in a given scene, use a "Container View". Those are explained here. Basically, they allow you to add a view in your scene that is managed by another controller.
2 - If the controller in the peekView depends on different conditions, you will have to create something similar to a custom tabbarcontroller. That means that you instantiate the controller that you need, add it's view as a subview of peekView (not assign the controller's view to the peekView itself) and then use didmovetoparentviewcontroller to notify the child controller. This question might help.
UPDATE:
Just saw your comment, so let me answer what you actually asked: The peekview property is actually just a reference to the real UIView you placed in the screen. When you do this:
self.peekView = controllersMainView;
You are changing the reference, but no the view object itself. That's why you are not seeing any changes. There are ways of adding a new view to the controller from code, but it is much simpler to simply use addSubview to add your controllers view to a UIView that is already in the controller.
Check out the discussion here: subView Slide up from bottom of the screen
and here: SubView slide in animation, iphone
Hopefully that gives you a bit of framework on how to approach this task!
I’d like to create a small overlay view that would float on top of all the others. I need the view to accept touches and I need it to stay in place even during transitions between the underlying views. What I have tried so far:
Standalone view inserted into the key UIWindow. The obvious downside is that the view is not attached to any view controller, which means handling (at least?) rotation events by hand.
UIViewController containment, have a “root” view controller that would contain the floating view and all navigation would be done underneath it. This is very much a “first-class-citizen” solution, but presenting a modal view controller overlaps the floating view.
Second UIWindow with a root view controller containing the floating view. This goes against Apple recommendations and I had some trouble filtering which events should go to which window.
Does anyone have a working solution that doesn’t require too much brittle hacking?
In the end I have followed Jonathan’s suggestion and implemented the overlay as a separate view inserted into the key window. The view has an associated controller object (a subclass of NSObject, not UIViewController), and this controller takes care of the rotation logic. That way the view implementing the actual overlay object doesn’t need to care about rotation events at all.
When using custom container view controller, I don't quite understand why the presenting view controller needs to specify the from, because being the container class, it should already know what's in the view hierarchy, no?
transitionFromViewController:toViewController:duration:options:animations:completion:
Container view controllers sometimes need to put the views of the contained controllers into specific subviews of their own view. (For example, a SplitViewController reimplementation might have left and right positioning views holding the master and detail controller views respectively.) Providing the fromViewController tells UIViewController where in the view hierarchy the new controller's view should be inserted, and also which specific view should be removed after the animation.
(contrary to another answer, the frames of the views aren't set for you at all. You do that, before the call, and in the animation block. The "Creating Custom Container View Controllers" system guide in the docs has a pretty good example.)
As it happens, actually using transitionFromViewController:... appears to be optional. You can manage your view hierarchy manually, with or without animations, and it works fine. I'm still forming my opinions, but I think I prefer to do it manually, to more easily handle cases where one of the VCs is nil.
This is done this way to allow you to have a view controller that has views with viewControllers in it. The from defines the originating view controller and gives the system the ability to position the animations appropriately.
Imaging you had a view with 4 views in it like tiles. the main view controller can consecutively call this on its "child" view controllers and with the from -> to specification, it won't make the assumption that the caller is the from viewController.