iOS7 - Get UIView's useful height (excluding statusBar, navigationBar and tabBar) - objective-c

Introduction
This is a rather common question, however I have certain extra points to observe. One of them is that I don't want to make my bars opaque. I like the iOS7 translucent bars.
I have an UIImageView inside a UIScrollView, and I'm trying to set the initial scrollView.zoomScale to fit the image in the screen just like the stock Photos app does. That means: the image should not be cropped and should fill the screen as much as possible.
However for my app this is a bit more complicated as I have visible statusBar, navigationBar and tabBar. I will adopt the solution to hide them until the user touches the screen, but I'm still curious for a solution when hiding is not desirable.
Done so far
I'm currently calculating the zoom based on a relation between the image's height and the view's height:
double heightRelation = self.image.size.height / self.view.frame.size.height;
After applying 1/heightRelation to the scrollView.zoomScale, the image is still bigger than the useful space. Then I found the iOS7 default heights for statusBar (20pt), tabBar (49pt) and navigationBar (44pt) from the documentation and also from:
NSLog(#"status : %f", CGRectGetHeight([[UIApplication sharedApplication] statusBarFrame]));
NSLog(#"tab : %f", self.tabBarController.tabBar.frame.size.height);
NSLog(#"nav : %f", self.navigationController.navigationBar.frame.size.height);
... and after playing a little, I found that the exact combination would be to subtract statusBar and navBar heights from the view's height:
double heightRelation = self.image.size.height / (self.view.frame.size.height - CGRectGetHeight([[UIApplication sharedApplication] statusBarFrame]) - self.navigationController.navigationBar.frame.size.height);
This works great for the Portrait mode, but doesn't for Landscape. I know I could also make a condition to set the zoom according to the orientation but... The code is probably getting unnecessarily ugly.
So I ask you: what's the best method to get the useful height?
P.S.: Interface Builder's option "Adjust Scroll View Insets" has no effect.

Don't use the bars directly. Use the length properties of bottomLayoutGuide and topLayoutGuide of the view controller to determine how much of the view is "wasted" under the bars. These take into account navigation bars, toolbars, status bar, tab bars, etc., and are maintained by the system when the bars are resized (for example, after rotation to landscape on phone/pod idioms).

Related

xcode 4.5, iPhone 5 breaks my UIScrollView

So I've got a pretty complex project. I'm using both interface builder and xcode directly to build objects. Right now I have UIScrollViews being built in IB, where they need to be, and UIButtons built on top of those scrollviews. There are several scrollviews in the same spot, but that really shouldn't make much of a difference.
Anyway, the issue is that it works perfectly on the iPhone 4. But when building on the iPhone 5, it moves the Scrollviews to the bottom of the screen, where before it was x=0, y=361. All my other objects are being placed correctly with some empty space underneath them. I know how to check for iPhone 5:
I don't know how to post code on here with colors and whatnot, they make it super complicated so here is how I'll do the if/then:
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone){
CGSize result = [[UIScreen mainScreen] bounds].size;
if(result.height == 480){
// iPhone Classic
}
if(result.height == 568){
// iPhone 5
}
}
I don't know of a way to do if/then in IB. I tried just manually changing the location this way:
[peopleScrollView scrollRectToVisible:CGRectMake(0, 449, 320, 58) animated:NO];
That did not work. So, what I'm asking is there a way to change the location of a UIScrollView in the code itself? If there is not, then I think I will have to build all 5 UIScrollViews manually in code, which I definitely do not want to do.
If you select your Scroll View object, then click the Size Inspector module, you will notice the default Autosize Mask is set to: Left, Top.
Depending on your view "Mode" option, and your view's "Resize Subviews Automatically" option, this view and subviews will be shifted down on the 4" screen compared to the 3.5" screen.
Depending on what your particular view should look on each screen is up to you. On my project, I adjust the autosize mask to Left, Top, and Bottom,
as I want my UIScrollView and subviews to remain at the top of the screen (as drawn in IB) on the 3.5" and 4".
You can also set vertical and / or horizontal sizing arrows inside the box in the autosize graphic
. This will attempt to scale object as accordingly for dynamically sized screens.
The Autosize Mask should be your new best friend with iPhone5.
See Xcode Interface Builder. How Do These Autosizing Mask Settings Differ? for more info.

Storyboard editor layout confusion

I am having layout problems with the storyboard editor with a fairly simple screen. I have a UIViewController to which I have added a 320x440 UIScrollView at 0,0 followed by a 320x20 UIProgressBar at 0,440. It looks fine in Storyboard editor. I'm not entirely sure how the 20 pixel status bar at the top of the screen is accommodated given the CGRect frame coordinates that Storyboard calculates.
On loading ( in -(void)viewDidLoad ), the UIScrollView frame seems to be set to 320x460 pixels at 0,0 but the UIProgressBar is still 320x20 at 0,440.
When I add subviews to the UIScrollView, (UIImageViews in particular), they get stretched and get clipped on the screen because although the UIScrollView thinks it is 460 pixels high, it only has 440 pixels of screen to display in.
Can anyone point me to a solution?
Thanks
OK - I have identified what was going on - there were a number of issues, nearly all to do with mutually incompatible settings in storyboard attributes on various view controllers.
In summary, the main view controller containing the UIScrollView had the 'wants full screen' checkbox ticked - goodness knows how, but it appears that I had then gone through other views trying to compensate for that initial error by clipping, resizing, setting layout constraints etc. which resulted in rather confusing outcomes.
My advice would be to not touch ANYTHING in storyboard editor unless you know what it's effect will be - it is a dangerous place. I found the issue by going back to basics and creating a trivially simple replication of my app and then observing the differences between that app and my own. Sorry if I wasted anybody's time researching an answer.
Thanks

Disabling scrolling when image is zoomed

I have an image inside a UIScrollView. What I want to happen is if I zoomed in to a particular position, I want to disable the scrolling (both vertical and horizontal) so that it will remain on the zoomed area. Can you give me any ideas on how to do this?
Two things to keep in mind:
Make sure you are exactly where you want when you need to disable the scroll. (you can use some methods from the UIScrollViewDelegate to accomplish that).
Make the contentSize of your UIScrollView the same size of your frame. This way both the horizontal and the vertical scroll will be disable.
CGRect myScrollViewRect = myScrollView.frame;
CGSize myScrollViewFrameSize = CGSizeMake(myScrollViewRect.frame.size.width, myScrollViewRect.frame.size.height);
myScrollView.contentSize = myScrollViewFrameSize;
For clarity I putted more code than you would normally need to.

What is the best way to create a composite scrollable view on iOS

I need to create a scrollable composite view on iOS. That is to say, the view will contain at least one image, possibly a button, and some text (that we may wish to format with bold fonts, etc). The amount of data, and particularly the amount of text, is variable, from maybe 4 lines to maybe 100. The data is "variable" to a degree, and in particular the image and text do not come joined at the hip.
This all needs to fit in a "pane" of about 280h x 115w pixels in a portrait-only layout.
A single UITextView doesn't provide the facilities to display an image or format the text.
A UIWebView provides the ability to display the image and formatted text, but the button is a problem (not even sure if it's doable).
A UIScrollView would easily allow the image and button, and then a UIWebView could be embedded in the scroll view for the text, but then scrolling becomes a problem -- I'd like the entire view to scroll as one, without having to resize the web view to contain it's content, and without the confusion of a scrollable within a scrollable (the doc warns of "unexpected behavior").
(I'm guessing your thoughts at this point are that I want too much.)
So, any suggestions? What's the best way to get close to what I need here?
In iOS5 the UIWebView has a scrollView property, which is a normal UIScrollView. You should be able to add a UIButton as a subview of the scrollView to achieve what you want, although positioning it correctly may be a challenge. Prior to iOS5 you could cycle through the subviews of the UIWebView to find the UIScrollView with isKindOfClass...
I suggest testing with a UIWebView inside your UIScrollView. I don't see any interference in the iOS 5.0 simulator. I don't know if there are problems in iOS 4.0.
If you find that there is interference, you can prevent it by setting the web view's userInteractionEnabled property to NO, either in the nib or in code. This will prevent the web view from receiving any touches, so the user also won't be able to pinch-zoom it, follow links in it, or copy text from it.
In the web view's delegate, implement webViewDidFinishLoad: to set the web view's size and the scroll view's contentSize. For example:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
CGRect frame = self.webView.frame;
frame.size = [self.webView sizeThatFits:CGSizeMake(frame.size.width, HUGE_VALF)];
self.webView.frame = frame;
self.scrollView.contentSize = CGSizeMake(CGRectGetMaxX(frame), CGRectGetMaxY(frame));
}
When I did a similar thing, I had a dozen of views which I added to the UIScrollView and then calculated the frames of all the views. Granted, it was an extremely tedious work, given that some views could get hidden under various conditions. The layout code was actually pretty simple, laying out views from top to bottom, but ugly. The upshot is that it works like a charm, fast and reliably. You can even trivially wrap it in an animation block.

How do I resize a UIScrollView so that it's not 100% of the parent UIView?

I've built an app using the UITabBar template. I have a few tabbar items, one item displays a view. That view has a UIScrollView element that has paging enabled to mimic the behaviour of the iPhone springboard i.e. pages that can be scrolled left to right.
I'm trying to drop in a UIPageControl, so I've resize the UIScrollView so that it's slightly shorter than the parent UIView height and have placed a UIPageControl below it.
When I run the app the UIScrollView is always 100% of the height of the parent UIView and I can't see the UIPageControl.
I've got the following code in my viewDidLoad method of the view controller for the tab:
UIScrollView *tempScrollView=(UIScrollView *)self.view;
tempScrollView.contentSize=CGSizeMake(640,377);
This sets the content size ok and I can scroll left to right. I've tried adding:
tempScrollView.frame=CGRectMake(0, 0, 640, 377);
To to resize the scroll view but it still shows 100%. See diagram below showing the issue:
I think you shouldn't resize the frame to 640, 377 because it would make the paging stop working, once the contentSize would be the same as the frame size.
One solution would be to set the desired frame size in the interface builder (like the left most figure) and set proper autosizing masks. I gues what you are looking for is the configuration below
To check if the changes are working, I would use a uiscrollview of height visibly smaller, just to make sure the behaviour is the desired.
If you want your view to scroll you need to change the tempScrollView.contentSize to be bigger than tempScrollView.frame
If you do this:
CGFloat contentWidth = tempScrollView.frame.size.width*2;
tempScrollView.contentSize=CGSizeMake(contentWidth,377);
You will have 2 pages.
You need to activate paging too with:
[tempScrollView setPagingEnabled:TRUE];