Detect when an added subview's frame's origin is behind the status bar - objective-c

I am adding subviews to the top of my view controllers, setting the added frame's origin's y to 0
aView.frame.origin.y = 0.0;
On one instance, the view appeared behind the status bar. This can be corrected easily by setting origin.y = 20.0;
Regardless of what the cause may be, how can I detect if I need to set it to 20.0 or not? In other words, how can I find out if the top of the page is behind the status bar or not?

You can peek into window.screen.applicationFrame to see coordinates, and at UIApplication's statusBarOrientation and statusBarHidden properties. But conditionally setting your frame top to 0 or 20 isn't really the solution - you want your frames to be set up so that it does the right thing without you having to change it. 0,0 should always be the top-left visible corner of your view.
If you used the view-based application template when you created your project, nearly everything should be set up correctly - just make sure that you specify a Gray or Black status bar in your XIB.
For programmatically created view controllers that you add directly to the Window, you might need:
myViewController.frame = window.screen.applicationFrame;

Related

How to calculate the correct location of a vertical NSRulerMarker?

What is the correct formula for the location of an NSRulerMarker on a vertical NSRulerView, if the client view is flipped?
The situation: I have a view (let's call it the "main view") that is embedded in an NSScrollView with rulers, and it has subviews. The user can drag these subviews around, and while dragging, I want to indicate the current position on the rulers.
The main view is flipped: The zero point is top-left.
The horizontal position is pretty simple:
NSPoint scroll = myScrollView.documentVisibleRect.origin;
NSRect rect = mySubView.frame;
rulerMarkerDragLeft.markerLocation = rect.origin.x - scroll.x;
However the same method for the vertical position...
rulerMarkerDragTop.markerLocation = rect.origin.y - scroll.y;
does not work. The marker is only in the correct position when the scrollview has been scrolled down to the extreme bottom. For every n points scrolled back up, the marker location is n points too high. This is independent of the main view's size or the size of the visible area.
I can't seem to wrap my head around this problem; I guess there is a value I need to subtract from my result that expresses how far up the scrollview has been scrolled (or rather, how much further it can be scrolled down), but I don't think I can derive that value from myScrollView.documentVisibleRect...?
I may have overlooked something simple, but I can't find the solution.
Edit 2022-11-02 17:17 CET: Found the problem. I had set the NSRulerViews clientView to the contentView of the window. I am now setting it to the "main view" (ie. the view inside the scroll view), and now it works "automagically": I just set the marker locations to the subviews frame, no correction for scroll position or anything else needed.
The solution was simple: the ruler views' clientView needs to be set to the view that is inside the scroll view, not the main content view of the window.
The positioning of the ruler markers is now very straightforward: you just use the local coordinates inside the view, ie. the subviews' frame values.
No correction for scroll position, view height or such necessary.
My mistake was assigning the window's main content view as the rulers' clientView.

Status Bar turning completely black when using clipsToBounds = YES in iOS8. How do I change this?

I'm setting my NavigationBar's translucent property to NO via the UINavigationBar appearance proxy in my AppDelegate.
Then, to get rid of the 1 pixel-height separator between UINavigationBar and my view's below it, in my ViewController, I'm setting self.navigationcontroller.navigationbar.clipsToBounds = YES;.
This is to remove the separator and achieve an effect similar to this...
There's an additional view with Dates underneath the UINavigationBar.
However, when I begin to use clipsToBounds = YES, my status bar goes completely black. I don't want this. I want it to be 'blue', just like it is in the photo for this CBS Sports app.
How can I change the color of the status bar, or why is my color from the navigation bar no longer extending up to the status bar? [Also, I'm using an embedded NavigationController, not one that I dragged out onto the view]
Do not set the clipsToBounds property to YES. The way extended bars work is the layer is drawn beyond its bounds until under the status bar.
You have two options here. One is to remove the separator using runtime trickery, which I have explained how to do in this answer.
Since your case does not involve translucent navigation bar, it will be much simpler. What you want to set is shadowImage to be empty like so:
self.navigationController.navigationBar.shadowImage = [UIImage new];
But you'll notice this does not work right away. Documentation mentions that this will not work until a background image is not set using one of the setBackgroundImage: methods. Since your bar is not translucent and has a single color, you can create a 1x1px wide image with the color, and set it as the background image.
A problem here is that when you set clipsToBounds to true, you lose the top of the status bar. What I do, therefore, is just the opposite of what you're doing - I make the navigation bar absolutely transparent:
self.navbar.backgroundColor = UIColor.clearColor()
self.navbar.translucent = true
self.navbar.setBackgroundImage(UIImage(), forBarMetrics:.Default)
self.navbar.clipsToBounds = true
Now we are just seeing right through the navigation bar to whatever is behind it. So you can have whole top area of your view controller's view be blue, and you just see it.

iOS 7 Auto Layout - Ignore In-Call Status Bar

Using Auto Layout constraints, I am trying to set up a subview with a vertical position that never changes, even when the in-call status bar (or some other non-standard status bar) is showing. I don't want to position the view relative to the Bottom Layout Guide because I want its y-coordinate to be the same for 3.5" and 4" devices.
Here's an illustration of what I want to have happen:
Initially, I set up the subview with a "Top Space to: Top Layout Guide" constraint for its y-position. With this configuration, the In-Call Status Bar pushed the subview 20px downward:
Next, I tried replacing that constraint with "Top Space to: Superview," where Superview was the root view of the controller. No change. Is there some other way to maintain a constant y-position for a view with Auto Layout constraints? I know I could resort to handling this manually by subscribing to UIApplicationDidChangeStatusBarFrameNotification, but I'd like to keep things simple if I can.
Your label is positioned to have a margin on top, between the start of the view and the start of the label. When the status bar grows, your view is shrunk. If you wish the label to remain in the same place, you can listen to UIApplicationWillChangeStatusBarFrameNotification notifications and adjust your view's size and origin to offset the additional space of the status bar. If you only want your label to shift up, you can also listen to the notification above, and adjust the top space constraint accordingly.
No you cannot.
The frame looses 20 px height in Portrait :( and the layoutguides don't care about it.
So either you do what Leo Natan said and use the notifications UIApplicationWillChangeStatusBarFrameNotification
Or either you will have to use the bottomConstraint as you dont want to do.

Mechanisms to move a view partly offscreen

I have a problem that nobody has yet offered any help with so let me ask a technical question to help me diagnose it on my own.
What mechanisms does the system have for moving a view so it appears to be partly off screen besides changing the view's frame (i.e. bounds/center) or transform?
This is an app which needs to run on iOS 5/6 still so I'm not using auto layout.
I have a fullscreen view reached via a push segue.
After a pause I hide the (translucent) status and navigation bars.
When the bars hide with animation they slide up off the screen.
In iOS 5/6 this just exposes the top of the view (which is an image). In iOS 7 it moves the entire view up "offscreen" a corresponding amount (i.e. 64 points if I hide both bars) showing a bar of content "below" the view at the bottom of the screen.
When I un-hide the bars (via a tap) the bars appear and the view moves back down to occupy the whole screen.
Whether the VC is set to extend the view under bars or not doesn't affect the behavior.
I have not been able to reproduce this behavior in a simplified app.
In either state, i.e. partly offscreen or fully onscreen, the top-level view frame remains {0, 0} 320 x 480 and the transform matrix remains {1, 0, 0, 1, 0, 0}. I have no idea what property of what object the system is changing to visually move the view. Perhaps if I knew what object/properties to examine I could deduce something about what is going on. Can anybody tell me how the system might be doing this?
The answer is that the "view" that was being moved was being presented by a UIPageViewController and I wasn't thinking about that. The UIPageViewController is presenting the child views inside a UIScrollView (subclass, I think) and so the scroll insets for the containing UIScrollView were being changed by iOS 7. Since the whole "presenting as a child" thing actually works -- of course I couldn't see the problem in the child view.
The "fix" was to un-check the View Controller setting for Adjust Scroll View Insets for the UIPageViewController view.

I can't understand UIScrollView behavior

I've read a lot of documentation but still don't really understand how that UIScrollView works.
I have an example where I use an UINavigationViewController with the status bar (little top bar with wifi, battery, etc., icons) and a navigation bar (with the "back" button).
As the first subview of the UINavigatioViewController's main view I have a UIScrollView. Inside it I have created several subviews that make it's contents size to be 500 points.
In the "viewDidLoad" method I set the scroll view's "contentSize" equals to 500. But it doens't completely scroll down to show the last subview.
I read that I should add some points to the "contentInset" because of the "bars". I don't really know why. Isn't the scroll view inside the main view that is correctly framed? Why does it need to take the "bars" into account?
Anyway, I read that it should be like 64 points (44 navigation bar + 20 status bar). But it doesn't work.
The "magic" number (at least for me) is 84 points. I must add that quantity to the content size (584) or use it as:
self.scrollView.contentInset=UIEdgeInsetsMake(0.0,0.0,84.0,0.0); // AT THE BOTTOM!!
I'm really confused about this. Could anyone give a hand on understanding it?
Thanks.
self.scrollView.contentInset=UIEdgeInsetsMake(0.0,0.0,84.0,0.0);
UPDATE
I have seen that because I made my ScrollView size bigger in Interface Builder in order to add new elements beyond the normal small visible size, it had a "frame" size near to 500 points.
I set it frame size to 416 (just to fit and cover all the visible space) and now with a content size of 500 points (without any additional inset values) it scrolls perfectly.
But I still don't understand the previous behavior...