The following is a snippet of the code being used to set up the subview layout on device rotate (I am using the same code for initial setup of the views):
(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
CGRect screenBounds = [[UIScreen mainScreen] bounds];
if(screenBounds.size.height >= 1024) {
[myButton setFrame:CGRectMake((screenBounds.size.width/3), (screenBounds.size.height/100)*81, (screenBounds.size.width/3), (screenBounds.size.height/100)*16)];
}
else {
[myButton setFrame:CGRectMake((screenBounds.size.width/3), (screenBounds.size.height/100)*81, (screenBounds.size.width/3), (screenBounds.size.height/100)*16)];
}
}
What is happening is myButton is being redrawn properly, but trying to interact with the button does nothing. If I go back to portrait, the button is able to be pressed after redraw. If I start in landscape, the button works; but if I later rotate away and then back to landscape, button no longer works. This has me stumped.
So, it's very difficult to tell what's going on in your view without being able to see your storyboard, view hierarchy, etc. However, Xcode has made troubleshooting these types of issues much easier with Live View Debugging.
The first thing you can try is turning on view frames. This will allow you to see the frames and bounds for each control. Most likely what you will see is that your button's frame is in one place, but the bounds is someplace else. You can turn on view frames via Debug -> View Debugging -> Show View Frames.
If you need a more comprehensive overview of your hierarchy -- for example, maybe another view is covering the button -- Xcode now has a really cool feature to capture the view hierarchy. You can manipulate the hierarchy in 3D, filter for certain controls and more. This will allow you to visually inspect the hierarchy at run-time and see what might be going wrong with your button. Debug -> View Debugging -> Capture View Hierarchy.
Related
i am having some very strange issue. Basically i am implementing a drag and drop view with a snap to the horizontal 1D grid. Well, so when i drag a view and its center coordinate X is bigger or smaller then a different view, the non-dragged view should be animated to the left or right of its original position.
This works fine most of the time. But in some special cases its not working. In some situations specific situations the view does not receive any gesture callbacks anymore. I can reproduce this issue and have found out that when i remove the code that applies the animation, everything its working fine.
Basically this is the code that is called when the dragged view is at a position where the view below should be moved to the left or right
/**
* Animate element to its saved position
*/
- (void)switchElement:(unsigned int)draggedIndex with:(unsigned int)otherIndex
{
// first animate
UIView *view = views[draggedIndex];
UIView *otherView = views[otherIndex];
// IF I COMMENT THIS OUT, EVERYTHING WORKS FINE
otherView.frame = [self getImageRectForIndex:draggedIndex];
// now switch internally
if(draggedIndex != otherIndex)
{
// switch views
views[draggedIndex] = otherView;
views[otherIndex] = view;
}
}
Any idea if there is something to have in mind if i animate UIViews and have gesture recognizers attached to them?
If somebody is willing, i can paste the whole class here to test it.
SOLUTION
I am having some "highlight" views in my design. And i have moved the relevant views behind those transparent background views by accident. So now i am not using addSubview: but insertSubview:atIndex: instead.
But marking #Anthonin C. answers as the right one. Because it pointed me in the correct direction (I found it out by overriding the hitTest: method)
Would you please track the otherView.bounds property to verify that your touch isn't out of bounds. I faced this issue recently and Apple documentation provide solution here : Delivering touch events to a view outside the bounds of its parent view
Sorry, I don't have enough reputation to comment your answer.
I'm having an issue with my app's layout which is a bit tricky to explain. When the app first starts, this is what I'm showing:
After the user taps "Create Profile", I animate those buttons and show a registration form instead:
Needless to say, the buttons are now not in their "natural" position. Note, however, that the text fields are - that's where I have placed them in the storyboard, but when the view first loads I hide them. The animations are working great, but then I needed to scroll my view up when the user gives focus to a text field and the keyboard hides the field. The details of how to trigger the bug are a bit hard to explain, so I managed to boil it down to what seems to be a redraw event, except that it isn't... Let me try and explain that.
First of all, here's what happens when the keyboard is about to show:
- (void)keyboardWillShow:(NSNotification*)notification
{
CGRect frame = self.view.frame;
frame.size.height -= 1;
self.view.frame = frame;
}
Notice that this is a test only, probably the minimal I found that would still trigger the bug. All it does is resize the view. I would expect the view to be exactly as it was, with one less pixel, right? Wrong. Here's what I get:
That is, all elements returned to their "natural" positions, completely ignoring their previous positions. My first guess was that it would seem that the window is redrawing, so I tried this:
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
NSLog(#"View was drawn");
}
But this only triggers when the window is first drawn, not when this strange behaviour happens. To understand what I mean by "natural position", here's what I have in storyboard:
You can also see that I'm not using constraints and the underlying structure of my view:
The full code for the entire setup is quite extensive, so pretty much not practical at all to show. However, how I animate the subviews resumes to changing their frame as I did in keyboardWillShow, and setting their positions to whatever I need.
Any ideas?
So you're using storyboards and you have "Use AutoLayout" set to false for your entire storyboard?
In that case your app is using "struts and springs" style placement rules. You're going to have to debug those.
It's a bit hard to describe everything in a view controller in a post. It's easier to go over it in IB. Perhaps you can write a utility function that logs all the autoresizingMask values for the views in your view controller, and go over those, and perhaps post them here describing the autoresizingMask values for each view in your original post.
I'm having a bizarre problem here with iOS8. I've been googling and bashing my head against a wall for a couple of days with no luck. I'm not exactly sure how to even explain it, but here goes...
Apps have been running fine under iOS7, but now compiling using xCode 6/iOS8 I'm having a few strange orientation problems. I'm not using a xib file, but instead creating a window and view programatically. I'm running OpenGL in the view, and then handling everything else inside OpenGL. So, I'm collecting touches and passing them to my GL routines. All has been fine forever, til now.
Now with iOS8 it seems as if the "touch window" is rotating itself so that touches in Landscape are limited to one side of the screen, as if the display window is Landscape, but the touches are all within a Portrait window that is set to on one side of the screen.
What seems bizarre is that touches come through to the view all over the screen as normal, but the Y value stops at 320 and goes no further. The X acts normally. If I turn the device to Portrait (the view controller does not auto rotate), it all works, but in Landscape it switches to this strange behaviour.
I've tried all I can think of without any changes, and am not sure where to begin to work out why this could be happening. Any advice would be greatly appreciated.
On iOS8, UIScreen is now interface oriented. In some (or should I say all of) libraries before iOS8, some checks were done to adjust that lack and swap width and height when the application was in landscape.
You should look at some code that makes some rotation and remove it.
-[UIScreen bounds],
-[UIScreen applicationFrame],
Status bar frame and
Keyboard frame are now interface-oriented.
(cf. WWDC2014 video - view controller advancements in iOS8 # 50:41)
In our case.
Force to rotate with view's transform caused that problem.
We removed that code, rotate with
+ (void)attemptRotationToDeviceOrientation
and problem solved.
We called that method at our forceRotate method.
-(void)forceRotateLandscape {
...
UIViewController *vc = [UIViewController new];
[topVc presentViewController:vc animated:NO completion:nil];
[topVc dismissViewControllerAnimated:NO completion:nil];
supportedOrientations = UIInterfaceOrientationMaskLandscapeRight;
[UIViewController attemptRotationToDeviceOrientation];
...
}
My guess is that whatever view owns the gesture recognizer isn't getting resized during the rotation for one (or more) of the following reasons:
It uses auto layout and isn't pinned to its superview
It uses springs and struts and doesn't have the correct UIViewAutoresizing mask
It lives in a subview that isn't getting resized
If you're using auto layout, try this in your view controller:
- (void)viewDidLayoutSubviews
{
// For each subview with its own auto layout:
[self.mySubview layoutSubviews];
// ...
[super viewDidLayoutSubviews];
}
Ok, I've found the issue on my side (I'm the bounty giver).
We were probably using some kind of hack in our App to manually handle the rotation of our views, because we were returning 0 to the - (NSUInteger)supportedInterfaceOrientations in our rootViewController.
- (NSUInteger)supportedInterfaceOrientations {
return 0; // BOOOOOOO, even forbidden by documentation
}
Then we manually managed our internal views by listening to the UIDeviceRotationChanged notification, rotating them views, and asking the statusBar to rotate as well.
Returning something not 0 to this supportedInterfaceOrientations made the "touch" layer rotate along with the rest.
But now we have to make the code evolve to use iOS rotation instead of our hacky layer.
Also, after investigating more, especially on iPad, you have to use a Storyboard-based UIWindow.
Using a XIB-based window will result in its dimensions being wrong when the app is launched while in landscape. This leads to a mountain of buggy frames and messes up badly with our layout.
I had the same problem. In iOS7 all worked fine, in iOS8 it was like the "touch-view" was still in portrait.
I just replaced the main xib with a storyboard, no other change (i've tried a lot playing with screen bound and frame without any results).
Just try it out, let me know if it work also for you. Good luck.
I have the same issue with second window in landscape. It refuses to display itself on the full screen and also to receives touch events on the cut part of the screen. The only solution that I found so far is to enable support of the iPhone6/6+ native resolutions by adding required launch screens. If you do not support these resolutions in your app that might be the same problem.
I'm going through someone's code trying to determine why their storyboard goes out of control on orientation flips..
Modal segues are going from View Controller to another View Controller. The segue identifier is being defined and called correctly:
self performSegueWithIdentifier:#"identifiername" sender:self];
if the orientation is standard.. self.view.frame.origin.x and y are 0,0. If you flip it to landscape.. you get -20, 1280? Nothing is defined programmaticly using CGRects and all standard Orientations return true with no changes.
Does anyone have any ideas whatsoever???
I would look through the project for any odd calls to setFrame: obviously. But also, for self.view.transform =, applied transforms (like zooming with a transform) affect frame. You can also check your bounds, I suspect the bounds of your view are correct, but checking never hurts.
Also you can easily log a CGRect like this:
NSLog(#"%#",NSStringFromCGRect(self.view.bounds));
Good hunting.
I’m trying to set up an iPad test application, window-based, where I have a single view controller and a single view. When I rotate the iPad, I want the orientation of the toolbar to change, but not that of the view itself. For example, a sort of background view that you work in is fixed to the device, but the status bar and toolbars rotate around it. This would enable the user to work the view from all angles, but always with a correctly-oriented toolset.
A beautiful implementation of what I want can be found in the Brushes for iPad app, where the painting’s orientation is locked to the device, and the toolbars rotate around it. I think other painting apps do the same thing.
I’ve been trying to figure out how to do this, but after exhausting many many other questions here concerning orientation, I’m still at a loss.
Could anyone point me in the right direction towards a neat solution? A particular combination of autoresizes for the autoresizeMask? Countering the rotation animation with another one in the opposite direction? Using multiple concurrent view controllers, one for the rotating views and one for the non-rotating ones?
I’d very much appreciate it,
(Edit: Attempted to clarify the question, after Olie’s comment.)
To prevent rotation, you'd put this in your view controller's .m:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
But you say you still want the view frame to resize in response to the rotation. I haven't had a need to do this myself, so I'm not sure if it's sufficient to just set the autoresizingMask to have flexible width and height; you may also have to implement didRotateFromInterfaceOrientation: and use setNeedsLayout and/or resize the view manually.
I had a bug that did this a while back -- I'm pretty sure that what you're asking will get you a HIG-violation rejection from Apple. However, I'll give a shot at remembering what the problem was. I'm pretty sure it was something like this:
I had a tabbarViewController that said "I orient to any orientation."
One of the tabs was a regular-old UIViewController that said "I only do LandscapeLeft & L-Right"
When you rotated, the inside (UIVC) stayed put, but the outside (TabVC) rotated around things.
I might have some of the details backwards or otherwise convoluted, but the general ideas is: stacked VCs, not all one VC.
Good luck!
To the extent I have worked with I cannot see any simple answer to your question. What about rotating everything (tabbar, nav and status bar, your view controller) and then redrawing the content of your view controller in "old coordinates" so for the user it will look like it's not rotated?