Top level view in a UIView hierarchy - ios

I have a singleton instance of a custom UIView with a method -(void)display whose job is to animate the custom view in and out on whatever screen is currently being displayed. (kind of like the -show method in UIAlertView).
My question is how do I determine which view is at the top of the UIView hierarchy and currently displayed?

The "top" of the view hierarchy is a UIWindow. And there can be (and usually is) more than one UIWindow in the application. If you want to have something shown over everything else on the screen, I suggest you implement a custom UIWindow. E.g. UIAlertView is also implemented this way.

Related

IOS difference between subview and container view

what is the difference between a subview and a container view. I have a piece of code that is successfully working by adding subview programmatically. But I want to be able to lay out the subview in the editor not in code. The only thing I could find was containerview. What is the difference and can they be used interchangeably.
Thanks.
You use UIView when you already have a view and you do not need to have a dedicated view controller to build and handle interactions within it.
From the UIView help page:
UIView object claims a rectangular region of its enclosing superview (its parent in the view hierarchy) and is responsible for all drawing in that region ...
Simplified structure: YourViewController ---(has)---> UIView
You use UIContainerView when you need to embed another view controller in the one that you already have. The embedded view controller is in charge of returning a view for the region that the UIViewContainer occupies. Therefore, your UIContainerView knows which view controller to use to render UIView inside the region it occupies.
From the UIContainerView help page:
Container View defines a region within a view controller's view subgraph that can include a child view controller.
Simplified structure: YourViewController ---(has)---> SubViewContoller
---(has)---> UIView
That SubViewController returns a view and handles its events.
Last if you want to learn how to layout subviews, i cannot explain that over here, so you might need to go through one of the tutorials.
https://www.raywenderlich.com/113388/storyboards-tutorial-in-ios-9-part-1

Make UIView subview as default UIView for UIViewController with Storyboard

I have a multi-viewcontroller app.
The main view of the main VC is subclassed so I can use Core Graphics animations. It's called "animations" class.
On the storyboard, I add a UIView called "MainArea" as a subview of the "animations". I have a VC called "MainAreaVC". As you know it has a property of "view". How can I make the "MainArea" subview the default UIView of my "MainAreaVC"?
The other option is to just make the "MainArea" view a property of my "MainAreaVC" and use this. I just don't want the "MainAreaVC"'s default "view" property just hanging out doing nothing.
The view in a UIViewController is the bottom view: it is always the one at the root of the tree hierarchy, and appears behind everything else, possibly clipping subviews.
You can change the view Class, but no matter what you do, by design it must be at the root. If, somehow, you managed to point to one of your subviews, that subview would in essence become the root.
Comment:
You do not have to add a view of type animations containing another view of type mainArea. You can have 'MainAreainherit fromanimations, and drag suchMainArea` in the storyboard.

MVC with view built in code

I've been trying to interpret the lessons from CS193P, and have a few questions.
I'm building views in code, the way I do it is I have a UIView subclass where I put all the views in place in the init method. This class is initialized by the ViewController.
The question is then, what is the right approach from here - say i want to animate a button I placed at 0,0 to 100,100. I'd like to animate it from the ViewController, but i don't like the fact that i set the 0,0 position in the UIView class (in the initializer) and now i am setting a new position in the ViewController. I'd prefer there would be just one place knowing about the actual (x,y) positions of my views.
How am i supposed to go about this?
Move the positions in the initializer to the ViewController
Put a method in my UIView "-(void)AnimateToSecondPosition" where the actual "second position" is then up to the view?
Just let it go. It seems like this would be the right approach if i had placed the button in interface builder - i consider interface builder to be the view then...
Or maybe even a fourth option?
Please help me understand it better and not just give me the right answer ;)
I'd like to be able to compare my approach in some way to how you would do it using interface builder, so each of my views are public and accessable from the controller - this way i believe i could easily start using interface builder instead if i wanted, without changing the controller code, just hooking up the outlets.
I'm guessing the case would be the same for disabling, hiding and doing other things with the views.
Thanks in advance.
If you want to create a new View programmatically you should generally instantiate it in your View Controller using its designated initialiser:
UIView *testView = [[UIView alloc]initWithFrame:myFrame];
If you create a custom view it's totally fine and correct to put some configuration code in the init method, but it's your ViewController that should be in charge of deciding what to do with this view - it is his job! Using the MVC the View and the Model should never communicate directly (as you definitely learned in the first lesson of CS193P).
Therefore the same apply to the animations. You should animate the Views within your ViewController and not implement the animation in the View itself.
Therefore in my opinion you "second position" should be setup by the VC - if this has to be done when something happens to the view (e.g. someone pressing a UIButton) you should set a target/action to your VC and handle this within your VC.
ADDED:
Regarding building UIViews in the Interface Builder I don't know what you mean by "and let them go". Interface builder will create the views and add them to the specific superviews at runtime - as you can see in the example below you control the view hierarchy graphically on the left. For instance in this case there is a UIView (which I coloured green for clarity) and two buttons. One is a subview of the main view while the other is a subview of the green UIView.
Once your ViewController is loaded the view hierarchy is automatically loaded to self.view - in fact if you run the following code in your VC when it is loaded you will see the list of self.views subviews in your console.
for (UIView *view in self.view.subviews){
NSLog("%#", [view description]);
}
If you know already that you need to change some attributes of a specific UIView you setup via Interface Builder (e.g. we know we want to change programmatically the color of the green UIView in the example above) you should create an outlet which allows you to have a reference to that view in your code. You do it by crtl-drag from the storyboard to your ViewController code - see the example below.
When you have done that you can refer in code to this as any other property, with the difference that it has been created by Interface Builder.
Hope this helps.
You can add an -setButtonFrameToSecondPosition to the view subclass, which simply updates the frame of the button, and then call that from the view controller via one of the +[UIView animate:...] methods.

Why can't I assign a UIView to self.view?

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!

Is there any way to place a UIView above a UIPopoverController?

I have code in my app that adds a UIView to the root view controller's view. This view is semi-transparent, and functions as a modal overlay that covers the entire application (the view contains a button that when clicked removes the view from its superview, which is how the user gets back to the main application).
This has worked fine up to now, but now we're using UIPopoverControllers and attempting to use this same modal overlay to block the main application. The problem is that this UIView is shown behind the popover, instead of on top of it.
Is there any way to add a subview to a root view controller's view in such a way that it appears on top of any visible UIPopoverControllers, but without dismissing them?
The reference on UIPopoverController states:
"The popover content is layered on top of your existing content in a special type of window."
NSLogging of the subviews-array shows, that the view is not added to the view hierarchy of the viewController that displays it. From my point of view what you are trying to do isn't possible.
Here is an idea though:
You could add your blocking UIView to the contentViewController of the UIPopOverController and use it to set a property on the contentViewController.
Then you set your main viewController, which actually displays the UIPopoverController, as delegate of the popover and do something like this:
- (void) popoverControllerShouldDismissPopover:(UIPopoverController*)popoverController
{
if(popoverController.contentViewController.yourProperty)
{
return YES;
}
return NO;
}
I don't know how much this helps in your current situation, but maybe it gets you started.

Resources