I a have a following line of code invoked after a touch gesture has completed:
CGRect parentBounds = self.view.bounds;
CGRect parentFrame = self.view.frame;
when iPad is placed in a vertical way both parentFrame and parentBounds have similar dimensions of w:768 h:1004 (or something close to that), but when I rotate parentBounds is 1024x748 while parentFrame is 768x1024.
Is this behavior normal? I thought I understood the concepts beetwen frames and bounds (and how they relate to each other)... but now I am really confused.
Could anyone explain what is happening with frame and bounds of a window (superview) when rotation occurs?
The window does not change orientation; the root view does. It does this by applying a view transform (self.view.transform). You're not supposed to call frame if transform is not CGAffineTransformIdentity.
This is not a complete answer, but might help if you don't get something better: When the device is rotated, the top-level window's frame does not change. Instead, a transform gets applied that rotates everything 90 degrees (or 180 degrees), and then the subviews will get resized to fit in the new coordinate system.
From Apple's PhotoScroller sample code:
We have to use our paging scroll view's bounds, not frame, to calculate the page placement. When the device is in landscape orientation, the frame will still be in portrait because the pagingScrollView is the root view controller's view, so its frame is in window coordinate space, which is never rotated. Its bounds, however, will be in landscape because it has a rotation transform applied.
In short, the view's frame is not affected by device rotations, but its bounds is.
Related
I have a zoomable UIScrollView which contains a custom drawn view (two circles). When the view is zoomed, I want to know where the centres of the circles lie, wrt the App's window. Consider these steps:
The view at 1X
The view zoomed at an arbitrary level, with the pinch focused on the first circle
The view zoomed at an arbitrary level, with the pinch focused on the top right corner
What I am meaning to ask is, if I were to lay another view containing a circle on top of the scroll view such that the circle in this view is concentric (need not be of the same size) with the smaller circle in the scroll view's view, where should the centre of the new circle be located?
When using UIView's coordinate conversion methods it is not relevant that you custom view is embedded in a scroll view:
MyCustomCircleView *circleView;
CGPoint centerInLocalCoordinates = circleView.centerOfACircle;
CGPoint centerInWindowCoordinates = [circleView convertPoint:centerInLocalCoordinates
toView:nil];
Maybe window coordinates are not what you are looking for. Remember that the window's coordinate space is always in portrait orientation and with the home button at the bottom. When holding the device in landscape orientation, your coordinates are (of course) rotated.
It might be easier to convert to a superview's coordinates, for example your view controller's view's coordinate space.
Did you draw the circles? Some combination of scrollView.contentOffset, scrollView.contentSize, and scrollView.zoomScale is what you want. You can walk back the translation and scaling using those values.
So i have a View and a scroll view inside that view. I call a
[View setFrame:CGRectMake(55,70,260,420)];
i put a
NSLog(#"%f %f",scrollview.frame.origin.x, scrollview.frame.origin.y);
before and after the setFrame and it reads 6,112 and then 6,172. 112 is correct, i have no idea where the 60 comes from. i searched 60 in my implementation file, and there is nothing that could affect the y at all. is there some rule with doing a setframe when there is a view inside that view?
Thanks
If you take a look at the UIView reference, the frame property states
frame
The frame rectangle, which describes the view’s location and size in
its superview’s coordinate system.
#property(nonatomic) CGRect frame
Discussion
This rectangle defines the size and position of the view in its
superview’s coordinate system. You use this rectangle during layout
operations to size and position the view. Setting this property
changes the point specified by the center property and the size in the
bounds rectangle accordingly. The coordinates of the frame rectangle
are always specified in points.
So i guess when you NSLog the frame for scrollview it is displayed based on the coordinates of the superview. The second NSLog shows different because the superview's coordinate system has changed.
I fixed this by making the scrollview in my code instead of doing it through the XIB. the scrollview is created after the setFrame and addSubView of the parent View
I'm having a nightmare with the rotation on iPad. I've searched all over the place for some tutorials, but nothing seems to really be for what I want. (Possibly not searching for the right thing?!)
I have a portrait view by default which is an image and a button inside the view. When I rotate, I detect this can work out if it's landscape. I then try to set the frame size of the uiview to fit nicely on the screen.
If I let it autoresize, it simply stretches and fills the screen. This I don't want.
but the trouble is, when I resize, the button gets resized too, but not in the same ratio as the image.
My question is: What's the best way to resize the view. I wanted to simply reduce the uiview by say 60% and it resizes EVERYTHING in that view with the same 60%. The only way I see this is working at the moment is to create two views... but that's twice the work and maintenance!
I've tried messing with the autosizing arrows in Interface builder, but that again seems to screw things up more!
I'm completely lost here!! Thanks for any info
The problem you have there is that the view is automatically resized to the screen ratio. On an iPad in Portrait Orientation the screen size is 1024x768. After the rotation to Landscape the origin rotates too and your screen content is skewed or stretched to 768x1024.
What you need to do is to override the
-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
message of the UIViewController of the view which rotates. This message is called within the animation block of the rotation. You just set the framesize of your subviews (the button) to whatever is best for you. Once i had a problem with rotating an OpenGL view. The content of the view was stretched when rotating to landscape. Since it is not possible to alter any OpenGL matrices within the animation block the only solution i found was to make the view quadratic and to set the origin behind the bounds of the screen (in -x direction). You have to override the message also to reset the origin above the screen (in -y direction) bounds in landscape mode, to keep the viewport in the middle of the screen. That way the view kept its ratio. Whatever solution is best for you, overriding this message should work out.
Have you tried disabling the autoresizesSubviews property on your UIView? It should prevent any size changes on the subviews when you resize your view.
In my iPhone OS application I want (need) to watch for changes in the device orientation in order to rearrange certain portions of the screen. The approach I used was to use CGRect frame = [UIScreen mainScreen].applicationFrame to get the screen size, and from there calculate the size and / or positioning of other controls (I also tried self.view.frame).
All testing was done so far in Portrait mode, so I could focus on programming the main features and later on just do some adjustments for Landscape. And here enters the problem: In -(void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation I added some logging to check the sizes before proceeding, but apparently the values for Width and Height are "wrong" (I say "wrong" because at a first glance the values does not make sense to me).
Here's the output of some logging:
Rotation: Landscape [w=300.000000, h=480.000000]
Rotation: Portrait [w=320.000000, h=460.000000]
The values for "w" and "h" in Landscape seem inverted to me - I was expecting that w=480 and h=300.
What am I doing wrong? The code I used to debug is below.
-(void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
CGRect frame = [UIScreen mainScreen].applicationFrame;
CGSize size = frame.size;
NSLog(#"%#", [NSString stringWithFormat:#"Rotation: %s [w=%f, h=%f]",
UIInterfaceOrientationIsPortrait(self.interfaceOrientation) ? "Portrait" : "Landscape",
size.width, size.height]);
}
The orientation of your device changed, not the physical characteristics of the screen. You basically tipped it on its side, but in reality it is 320 pixels wide (20 of which are not available to you at the moment since the status bar is showing) and 480 pixels tall. If your view is auto-rotating, then the width/height have been translated for you, but when you ask for the actual dimensions of the screen, you get back the actual dimensions of the screen.
This is also why, when working with translated views, it is important to do calculations based on the view's center and the view's bounds and never on the view's frame.
Use self.view.bounds instead.
I've been stumbling over the same problem and with some diagnostic work I discovered that the view's bounds and frame do not accurately reflect the landscape orientation in viewDidLoad (when the device is held in landscape when the view controller is pushed onto the stack), but do so correctly in viewWillAppear: and viewDidAppear:. I just moved my code that needed the dimensions of the frame/bounds from viewDidLoad to viewWillAppear:, and it worked properly.
Sorry for long winded post.
I am trying to understand UIScrollView and running into very simple problem.
I am creating a scroll view
I am making this view 1.5 size larger then normal size
Using UIScrollView I expect to see some edge elements of view out of bounds, but should be able to pan the view therefore bringing missing elements back to the visible area.
However I am seeing that I can't just pan/scroll view anyway I want, instead view always wants to scroll up, as soon as move away my finger from the screen (touch end event).
I am not handling any touches, etc - I just want to understand why does not scaled view stay put where I scroll it?
CGRect viewFrame = self.view.frame ;
viewFrame.size.width *= 1.5;
viewFrame.size.height *= 1.5;
CGSize mySize = viewFrame.size;
[ ((UIScrollView *) self.view) setContentSize: mySize];
self.view.transform = CGAffineTransformMakeScale(1.5, 1.5);
What I really trying to accomplish is something similar to Number on iPad (the same code will work on iPhone):
There is a view with lots of controls
on it (order entry form)
User can zoom into the entire form so all elements look bigger
user can pan the form therefore bringing various elements into the visible area of the screen.
It seems that UIScrollView can should be able to handle zoom and pan actions (for now I am using Affine Transform to zoom in to the order entry form and iPad)
Thanks
When you transform a view, you transform its internal coordinate system as well. This means that if you scale a view, the view still "thinks" it is the same size it was before the scale because its coordinate units scaled as well.
For example, if you have an image view that has a size of (50,50) and you transform it so that it covers (200,200) on the screen, when you ask the image view its size it will report that its size is still (50,50).
Scrollviews are unusual types of views because they have understand their absolute size relative to physical device screen in order to work properly. When you transform their coordinate system, they lose that connection to the physical device screen and can no longer function properly. This is what you are seeing.
I haven't done this but I'm pretty sure to create the illusion of a zoom in a scrollview, you increase the frame of the scrollview and then transform its subviews (or transform the subviews and then increase the frame of the scrollview to contain the new subview size.) That is the only way to keep the scrollview in sync with the physical device screen.