xcode 4.5, iPhone 5 breaks my UIScrollView - objective-c

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.

Related

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

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).

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.

uiscrollview and uibutton behaves transparent for touch events

I have a custom subclass of UIView and a full screen UIButton with a background image on it as you can see in the image,
on the right side, there is a UIView with a UIImageView on it and some other not important labels and buttons. The important part is, there are two scrollviews on that UIView and at the run time, i add UIButtons ( not small ones, 200 x 100 ) on those scrollviews with vertical scrolling enabled.
The situation is really very complex so i will write as items what happens in different conditions. In my application, there are 10 of these complete screens, on a custom subclass of UIView and i use UIPanGestureRecognizer to scroll from one to other.I have a good reason not to use a scrollview for this. All the windows ( the below image, not UIWindow ) populates their content in a loop, so the running code is exact same for all of them and all of them are being created from the same .xib file. And the last information, the bigger UIButton that covers all the window has an action for touchupinside and the smaller buttons in scrollviews have their own touchupinside selectors.
Some of my windows works perfectly, if i touch anywhere on the screen, the bigger buttons action is called, if i touch and drag, the scroll works and i navigate to the next / previous window, if i touch on smaller buttons in scrollviews, their actions fired and lastly i can perfectly scroll within those buttons.
Some of my windows, when i try to scroll the scrollviews, the bigger windows pangesturerecognizer catchs this event not the small scrollviews, and if i touch the small button in that scroll view, the bigger buttons event is fired ( it pass all through uibutton > uiscrollview > uiimageview > uiview to the uibutton at the button like they dont exist )
if i replace the right container view some other position on the big uibutton, it randomly works well or not, sometimes one of the scrollviews works well and the other not.
it has a consistent behaviour, if on a one position, it works, it allways works, and if it not, it never does.
Again, all the views and subclasses have their userinteraction enabled yes, the opposite is already imposible because the behaviour changes only acording to position of container view on the big uibutton and also changes acording to big uibuttons background image.
I've placed a touchesBegan method for just test purposes, when the touches does not work as expected, the event fired with touch.view is the container of the big button, even if i touch on a small button within the scrollviews.
I've spend two days already for that and no result.What can cause this behaviour ?
EDIT : After krumelur's comment I've changed my focus from configuration of those views to animation i give to the container views, and i've noticed that, the problem is about the animation, i'm adding the the code part that animates all ten windows and behaves like a custom scrollview. All windows have their custom layer class and when i catch pangesture recognizer i move windows on the screen with following code. items in that code is an array which holds layers of all the windows. The animation causes that strange situation but i couldnt figure out yet..
- (void)layoutSublayers {
[super layoutSublayers];
[CATransaction begin];
[CATransaction setDisableActions:YES];
float angleDelta = 2 * M_PI / [items count];
float a = angle;
for (UIBaseLayer *l in items) {
l.position = self.position;
CATransform3D translation = CATransform3DMakeTranslation(cosf(a)*(radius.x), 1.0, (sinf(a)*radius.y) - radius.y*1.0);
float dailyAngle = (M_PI_2 - a);
CATransform3D rotation = CATransform3DMakeRotation(dailyAngle, 0, 1.0, 0);
CATransform3D t = CATransform3DConcat(rotation, translation);
l.transform = t;
a += angleDelta;
}
[CATransaction commit];}
http://img836.imageshack.us/img836/296/screenshot20111215at013.png
bringSubviewToFront did not work reliably on 5.0 (on 5.1 the hack was not needed)
I had to add a bigger transparrent UIButton around the button that behaved as if it was disabled to fix this on iOS prior to 5.1.
At last I've figured out that, even I send back views with CATransform3DMakeTranslation and layer.zPosition settings, touch events goes to views according to their frame position. The views that aren't currently even seen (overlayed by others) still catch touches. Writing just one line of code ([self.superview bringSubviewToFront:self]) for the view that is currently on screen solved the problem.
For testing this and understanding what I'm talking about, I've made simple test application that has only two buttons on it.
The blue and yellow buttons.The blue button overlays the yellow button so you can only see the blue one at the beginning.
On the touch up inside code for the blue button, I just send it back with buttons.layer.zPosition = -1. After this, there is no visible part of the blue button remains on screen, instead I see just the yellow button now, but when I touch the yellow button (or I think I am touching it) still blue buttons touch up inside code runs.
I'm not sure if this is the desired logic but it does not seem reasonable to me.
As I mentioned above, writing [blueButton.superview sendSubviewToBack:blueButton] while setting its layers zPosition to -1 works as expected.

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];

How to get an iphone application to resize correctly on orientation changes?

I have an iphone app, with a view (well multiple but let's say one at a time). The view is very simple just some text-fields and images on there. I have set the auto-resizing attributes for those controls so that they know that they need to be centered no matter what the orientation is. There is no code that moves them or anything, everything is in IB.
The views only re-orient correctly if i change to landscape on the main view and then transition to the other views. If I go to any view on portrait and then try to go to landscape, the iphone performs the animation for rotation, but my controls don't seem to move to the right places. When i test the orientation in IB, it seems to re-orient correctly.
Why does that happen? How can I fix it?
I find I have to set reset the frame of my main view for changing orientation:
CGRect mainViewFrame = CGRectMake(0.0, 0.0, 320, 480);
[self.view setFrame:mainViewFrame];