How to get *actual* frame size for UIScrollView in UINavigationController in iOS 7? - objective-c

So it appears that the view frame in navigation controllers iOS 7 is defined rather differently (in my opinion, strangely) as compared to iOS 7...
EXAMPLE: Imagine a UIViewController that contains a UIScrollView (self.scrollView) that fills the entirety of self.view and with struts and springs so that it always fills self.view. Now embed that UIViewController in a UINavigationController with the navigation bar kept visible.
In iOS 6, I observe the following:
self.view.frame.origin = (0.0, X) where X = height of status bar (that contains time, signal bars, etc. = 20.0)
self.view.frame.size.height = self.view.bounds.size.height = overall height minus self.navigationController.navigationBar.frame.size.height
self.scrollView.frame.origin = self.scrollView.bounds.origin = (0.0, 0.0)
In iOS 7, I observe the following:
self.view.frame.origin = (0.0, 0.0)
self.view.frame.size.height = self.view.bounds.size.height = overall height (i.e., no reduction in height when embedded in navigation controller)
self.scrollView.bounds.origin = (0.0, -1 * self.navigationController.navigationBar.frame.size.height), but self.scrollView.frame.origin = (0.0, 0.0)
My guess is that this is done so that content can continue underneath the navigation bar so that the navigation bar can display the slightest amount of blur + transparency a la iOS 7.
QUESTION: How can I get the actual size (width x height) of self.scrollView in iOS 7 when it's embedded in a navigation controller, given the observations above, since self.scrollView.bounds.origin == self.scrollView.contentOffset? Do I simply have to write different code for iOS 6 v. iOS 7 and detect which version is running?
I don't really want to assume that the screen is any certain size and hard-code it in, since that seems like bad form...
UPDATE 1: I've uploaded a sketch of what I'm trying to ask. Note that the correct answer in this case is 416.0, which is easy to obtain in iOS 6, but I'm not sure how to obtain it in iOS 7 without resorting to assumptions.

What is happening is the your UITableView is automatically being adjusted so that the content will be visible behind the UINavigationBar on iOS 7. This is done by changing the contentInset property found in UIScrollView. So to get the frame excluding the part behind the nav/status bar, you just need to account for the contentInset.
CGRect frame = self.tableView.frame;
frame.origin.y += self.tableView.contentInset.top
frame.size.height -= self.tableView.contentInset.top;
This code will also work on iOS 6 because self.tableView.contentInset.top will default to 0.

On iOS7, you need to set UIScrollView.contentInset.bottom/top according to UIViewController.bottomLayoutGuide/topLayoutGuide.length.

Related

iOS9 covering status bar with custom uiwindow - wrong position

I am trying to cover status bar with my own view and to do that I calculcate frame for my view by doing something like that (also after rotation):
UIScreen *screen = [UIScreen mainScreen];
CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
For iOS8+ (because since ios8 UIScreen is orientantion-dependant):
CGRect frame = [screen.coordinateSpace convertRect:statusBarFrame toCoordinateSpace:screen.fixedCoordinateSpace];
[self setFrame:frame];
For iOS7:
[self setFrame:statusBarFrame];
It works just fine for iOS8 and below but when building my app with Xcode 7 beta 4 and iOS 9 SDK something is wrong when starting app in landscape or upsidedown (it works fine if app starts in portrait) ...
ie. when I start the app while Upsidedown the custom uiwindow which should cover status bar will always end up at the bottom side of screen, any ideas what might be wrong?
Without additional information, the best solution would be to simply hide the status bar. If you still want the "status bar" for animation purposes or some other reason, the typical solution in this scenario is to simulate it by calling
- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates on UIScreen
and use the returned view as the background for whatever view/window you're presenting. As of iOS 9, there's no public method that'll allow you to get the specific behavior you specified.

iOS6 contentInset in UIWebview behaves differently from iOS7

I attempt to add spaces in Webview so the content fits the webview nicely like implementing UIViewContentModeScaleAspectFit. The webview's superview is fixed with 320 * 200, and the webview's content size varies but are small enough to fit in the size. Everything works fine except when using iOS 6.
float verticalInset = (self.frame.size.height - fitSize.height) / 2;
float horizontalInset = (self.frame.size.width - fitSize.width) / 2;
wv.scrollView.contentInset = UIEdgeInsetsMake(verticalInset, horizontalInset, verticalInset, horizontalInset);
wv.center = self.center;
fitSize is correctly calculated, because if i just set webview's frame to the size, the content fits perfectly. However, that won't fill up its superview when zoomed in.
It seems like when I change the contentInset in iOS 6 it doesn't resize its content to fit the result frame (frame - inset); but iOS 7 does resize it. Is there any solution to fix this difference?

Vertical UISlider in iOS with autolayout

As per my iPad app requirement, i've to show the UISlider vertically.
I'm using iOS7 compiler and deployment target is iOS6.
In the story board I added horizontal UISlider of width 600 pixels. I created IBOutlet in my view controller. I didn't set any auto layout constraints. I'm using the below code to rotate and make it vertical.
self.slider.transform = CGAffineTransformMakeRotation(M_PI_2);
After and before rotation I'm printing the frame size of the slider which is correct. But the slider is not looking proper. Its just showing only knob in the center. How can I rotate the UISlider?
I got a vertical slider working with iOS 8 and Xcode 6 with only 3 constraints in the storyboard and one line of code. Here's a cropped screencap of the interface:
There are 3 constraints between the vertical slider and the UIImageView next to it:
vSlider.Center Y = Image View.Center Y
vSlider.Width = Image View.Height
vSlider.Center X = Image View.Trailing + 16
And of course the one line of code is:
self.vSlider.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi / 2))
It's easy to set up these constraints in the storyboard in Xcode 6, but I think it should be simple to write these constraints in code to support iOS 7 or 6.
I got it to work this way:
In viewDidLoad: I added
[self.slider removeConstraints:self.slider.constraints];
[self.slider setTranslatesAutoresizingMaskIntoConstraints:YES];
so that it's called before rotating the slider with
self.slider.transform=CGAffineTransformRotate(self.slider.transform,270.0/180*M_PI);
and there is no need to remove and re-add it to superview.
This is an old topic, but here is a Swift solution with autolayout constraints in storyboard and nothing else.
1/ You need to add rotation to the IBOutlet:
#IBOutlet weak var mySlider: UISlider! {
didSet {
mySlider.transform = CGAffineTransform(rotationAngle: -CGFloat.pi/2)
} // didSet
} // IBOutlet
2/ Define in storyboard the constraints, keeping in mind that the Slider will be rotated around its center.
For instance if you want to locate mySlider on the left side of myView, you need three constraints.
myView.Leading = mySlider.CenterX - 20
mySlider.width = myView.Height (with a multiplier of 0.8 for instance)
mySlider.CenterY = myView.CenterY
mySlider will of course appear horizontal in storyboard, but will have the correct sizing, and the center will be correctly positioned.
Uncheck Auto-Layout on your ViewController, there is no other option under the SDK 7.0 to make it work vertically :(
There are so many possible solutions around about putting UISlider vertical. Here is my summary for iOS7 in XCode5 with autoLayout enabled(defaultly in storyboard):
in viewDidLoad add method
self.slider.transform = CGAffineTransformMakeRotation(M_PI_2);
define your autoLayout constraints about slider explicitly in storyboard as whatever you like
In your viewDidLoad, try:
UIView *superView = self.sizeSlider.superview;
[self.sizeSlider removeFromSuperview];
[self.sizeSlider removeConstraints:self.view.constraints];
self.sizeSlider.translatesAutoresizingMaskIntoConstraints = YES;
self.sizeSlider.transform = CGAffineTransformMakeRotation(M_PI_2);
[superView addSubview:self.sizeSlider];
It does not work with constraints, so the trick is to remove the constraints for your uislider.
You might have to resize it manually by setting its frame property.
You can't use storyboard to build up a UISlider.
Build up UISlider by coding.
slider = [[UISlider alloc] initWithFrame:CGRectMake(640, 150, 600, 400)];
[slider.layer setAnchorPoint:CGPointMake(0.0f, 0.0f)];
slider.transform = CGAffineTransformMakeRotation(M_PI/2);
[self.view addSubview:slider];
Try this :-
self.slider.transform=CGAffineTransformRotate(slideToUnlock.transform,-90.0/180*M_PI);
Try below code to Rotate the UISlider in Vertical Position..
//To rotate the slider in Vertical Position
CGAffineTransform sliderRotation = CGAffineTransformIdentity;
sliderRotation = CGAffineTransformRotate(sliderRotation, -(M_PI / 2));
sliderBrightness.transform=sliderRotation;
For me a two-step process worked best (incorporating some of the previous solutions)
Autolayout step)
I added a vertical view in IB and used autolayout to link it to neighboring views. Then I added a slider in the view and simply hooked it up to the center of the view. Then hooked up the width of the slider to the height of the view. Finally control-dragged the slider outlet to my ViewController code (as slider)
Code step)
Then simply added the to my viewWillAppear (swift-code):
let trans = CGAffineTransformMakeRotation(CGFloat(M_PI_2));
slider.transform = trans;

Change size of viewing area after changing size of UINavigationBar and UITabBar

I'm currently working on my very first app and I've changed the size of the UINavigationBar and UITabBar and now I have extra space black space in the general viewing area (etc. ViewController, DetailViewController). How can I change the viewing area to accommodate for this new size?
I've pasted how I'm currently setting the new size for both UINavigationBar and the UITabBar.
/* Get the screenarea of the device */
CGRect screenArea = [[UIScreen mainScreen] applicationFrame];
/* Define the size of the navigation bar */
CGRect viewHeaderbBarFrame = navigationBar.frame;
viewHeaderbBarFrame.origin.y = screenArea.origin.y;
viewHeaderbBarFrame.origin.x = screenArea.origin.x;
viewHeaderbBarFrame.size.height = 44;
viewHeaderbBarFrame.size.width = screenArea.size.width;
navigationBar.frame = viewHeaderbBarFrame;
/* Define the size of the footer bar */
CGRect viewTabBarFrame = tabBar.frame;
viewTabBarFrame.origin.y = screenArea.size.height - 26;
viewTabBarFrame.origin.x = screenArea.origin.x;
viewTabBarFrame.size.height = 46;
viewTabBarFrame.size.width = screenArea.size.width;
tabBar.frame = viewTabBarFrame;
Thanks.
basically by shrinking your tabbar and navbar, you need your view to expand. I'm assuming that you are using a tabbar for primary navigation.
Figure out the proper size and adjust your view like this:
[[self.tabBarController.view.subviews objectAtIndex:0] setFrame:CGRectMake(newX,newY,newWidth,newHeight)];
in the buyer beware category, if your goal is the app store, you would be wise to review the guidelines on modifying those elements. I'm not sure of all the specific points, but I think apple frowns making changes like that to plainly displayed core navigation elements.
Also, this approach works well for the current screen dimensions, but may not work if the rumors are true and the next phone has a bigger screen.
be well.
You should never have to mess with the view height in this way. Views are pushed onto the nav bar. Their size should be set to fit a 320 x 480 screen minus tab, nav, and title bars. And you should set the AutoResize width/height as flexible and the left/rigth/top/bottom to not-flexible (just leave them out).
view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
iOS likes everything to to be spring loaded and pinned to one another. Then when you rotate things, it all sticks and moves properly. They do all the work for you. So, it's best to embrace their funky way of handing things. The interface builder gui can help. It even lets you test out rotation if you want. And you can set the option of tab/nav/title bars too. Or if you understand it, then you can also do this manually.

Get center screen position of view in landscape orientation

At the moment I have an UIImageView inside a window. When the scene loads I save its position with:
imageView.center
The user can drag it around. On some occasions the item gets animated back to its original position which has been saved. This all works fine, when I hold the IPad vertically.
When I hold it horizontally though I cant move it back to that position, because in landscape mode I cant use the position which was saved in vertical orientation mode. this is because in Interface Builder I set the item to get auto sized relative to the borders (meaning if the item was in the center, then rotating the IPad it still stays in center)
So my question is: How can I get the correct original position the item would have in landscape orientation?
I tried to calculate the position by hand like this:
- (CGPoint)getHorizontalCoordinatesForPoint:(CGPoint)point
{
CGSize size = [[UIScreen mainScreen]applicationFrame].size;
CGFloat relativeX = (point.x / (size.width));
CGFloat relativeY = point.y / (size.height);
CGFloat horizontalScreenX = relativeX * (size.height);
CGFloat horizontalScreenY = relativeY * (size.width);
return CGPointMake(horizontalScreenX, horizontalScreenY);
}
but this position is a bit off. I think it is because I dont take into account the size of the navigation bar. Is there some converting function in ios which already does what I want?
Don't.
Rather, instead of saving [imageView center] when the scene loads, save it at key points:
-touchesBegan:withEvent: (or, if you are using gesture recognizers, in the gesture recognizer callback).
-didRotateFromInterfaceOrientation:
The minor complication is what happens if a user, say, is working in landscape, quits the app, rotates the portrait, and relaunches the app. In this case I would start the app in landscape and let the autorotation to portrait kick in and correct your center point.