iPad Landscape messing up touches began - ipad

My app is only allowable in the landscape orientations, and it launches in landscape. Thus, the top left corner when the iPad is landscape is (0,0), so everything works fine.
However, when I pick up "touchesBegan"...it isn't working properly. Only when I tap on like the right two-thirds of the iPad does it pick up the touches. I know it has something to do with the orientation, because the app is literally just blank screen with a single UIView and nothing else. It is quite simple. There are no other possible problems that would cause this.
To be sepecific, if I print out the x location in the touchesBegan function, and if the iPad is held with the home button on the left, the width is 1024. And 1024-768 is 256. This is exactly the x position where it begins to sense my touches. Anything to the left of x=256 does not sense the touches.
How do I fix this?

Check Struts and Springs and make sure that whatever should pick up the touches is covering the whole area and locked to the 4 sides.

To do it programmatically,
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
CGRect appFrame = [[UIScreen mainScreen] applicationFrame];
[self.view setFrame:CGRectMake(0.0f, 0.0f, appFrame.size.width, appFrame.size.height)];
return YES;
// if you want to support only LANDSCAPE mode, use the line below
/*
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft | UIInterfaceOrientationLandscapeRight);
*/
}
This sets the view to occupy the full screen.

The answer is that, when defining the UIWindow, it needs to be defined as
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
and not strict coordinates.

Related

Get screen dimension (per scanline) in pixels for iPhone X (10) ios 11

Considering that iPhone X Notch has smooth curves (no rectangle around notch), How to find if a pixel (x,y) on captured screenshot is part of draw buffer covered by the notch (vs actual display buffer) on iPhone X?
I am trying to log start X and end Y (pixel) position for actual pixels drawn for each of the horizontal scan lines when iPhone X is in landscape mode.
Is there a way to use inbuilt api to get this information? [[UIScreen mainScreen] nativeBounds] won't suffice in this case.
Hardcoding values or proving an image mask to detect it is possible. I am wondering if there is a more robust way to find width in pixel for scan line A/B/C in attached pic.
// For a given scan-line for a given Y = any-value between 0 & window.screen.height (Landscape orientation)
+ (CGPoint)printScanlineForY:(NSUInteger )y {
// what goes here?
...
// Case 1: expected for non-notch scan-line, return CGPointMake(0, screen-width)
// Case 2: expected for notch affected scan-line, return (non-0, screen-width)
// non-0 as determined by notch draw curve.
}
// Returning nativeBounds CGRect would return larger bounding box for entire screen.
// Output on iPhone X simulator: Native Screen Bounds: {{0, 0}, {1125, 2436}}
+ (CGRect)printScreenBounds {
CGRect bounds = [[UIScreen mainScreen] nativeBounds];
NSLog(#"Native Screen Bounds: %#", NSStringFromCGRect(bounds));
return bounds;
}
Related question with helpful details: Detect if the device is iPhone X
You can reference the documentation here which has some nice images
https://developer.apple.com/ios/human-interface-guidelines/overview/iphone-x/
If you align your views to layout margins the system should take care of putting your views in visible areas.
If you want to figure out exactly where the notch is you can empirically calculate it by creating a view taking the full screen bounds, and doing a hit hit test for each coordinate , checking if it is "touchable". Collect the coordinates to some sort of array and then use them for future reference.
For instance something like:
#implementation FindNotch
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; // recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system
{
UIView *hitView = [super hitTest...];
if (view == null) { ... } // the notch isn't "touchable"
}
#end
FindNotch *notch = [FindNotch alloc] initWithFrame:[UIScreen mainScreen].bound];
static UIWindow *window; window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds;]
window.windowLevel = UIWindowLevelStatusBar + 1; // make sure the view is above anything else in the notch area. Might require some tweaking
Notice that it isn't tested and you may need to raise the window level above the status bar or else it may be "underneath" and not respond to touches

ios7 and ios8 landscape modes - part of the screen doesn't response

I'm developing an application in iOS8 in landscape mode and everything works fine. I'm using UIKit and don't support portrait mode at all.
Im trying to customise it to iOS7 but the frame that I'm getting is always like the iPhone is in a portrait mode.
I know that the in iOS8 there was a major change with the width and height and it's different in iOS7.
I have the following method which I used to set the window n the appDelegate and when I need the frame at the ViewControllers.
I'm doing the following:
- (CGRect) screenRect {
CGRect screenRect = [UIScreen mainScreen].bounds;
if ((NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
return CGRectMake(0,0,screenRect.size.height, screenRect.size.width);
}
return screenRect;
}
The views look fine but the left/right side of the screen (depends on the device orientation) behaves like it's outside the frame. i.e doesn't response to gestures!
When I'm setting the fame to the new "screenRect" it keeps turning back to the device bounds.
Thank you very much.

Custom UIWindows do not rotate correctly in iOS 8

Get your application into landscape mode and execute the following code:
UIWindow *toastWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
toastWindow.hidden = NO;
toastWindow.backgroundColor = [[UIColor cyanColor] colorWithAlphaComponent:0.5f];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[toastWindow removeFromSuperview];
});
In iOS 7 you will get a transparent blue overlay on top of the entire screen that disappears after 5 seconds. In iOS 8 you will get a transparent blue overlay that covers a little over half the screen
This obviously has something to do with the change Apple did in iOS 8 where the screen coordinates are now interface oriented instead of device oriented but in true Apple fashion they seem to have left a myriad of bugs in landscape mode and rotation.
I can "fix" this by checking if the device orientation is landscape and flip the width and height of the main screen bounds but that seems like a horrible hack that is going to break when Apple changes everything again in iOS 9.
CGRect frame = [[UIScreen mainScreen] bounds];
if (UIInterfaceOrientationIsLandscape([UIDevice currentDevice].orientation))
{
frame.size.width = frame.size.height;
frame.size.height = [[UIScreen mainScreen] bounds].size.width;
}
UIWindow *toastWindow = [[UIWindow alloc] initWithFrame:frame];
toastWindow.hidden = NO;
toastWindow.backgroundColor = [[UIColor cyanColor] colorWithAlphaComponent:0.5f];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[toastWindow removeFromSuperview];
});
Has anyone been experiencing this problem and come across a better, less brittle solution?
EDIT: I know I could just use a UIView and add it to the key window but I would like to put something on top of the status bar.
Your 'fix' isn't great for another reason, in that it doesn't actually rotate the window so that text and other subviews appear with the appropriate orientation. In other words, if you ever wanted to enhance the window with other subviews, they would be oriented incorrectly.
...
In iOS8, you need to set the rootViewController of your window, and that rootViewController needs to return the appropriate values from 'shouldAutoRotate' and 'supportedInterfaceOrientations'. There is some more about this at: https://devforums.apple.com/message/1050398#1050398
If you don't have a rootViewController for your window, you are effectively telling the framework that the window should never autoRotate. In iOS7 this didn't make a difference, since the framework wasn't doing that work for you anyway. In iOS8, the framework is handling the rotations, and it thinks it is doing what you requested (by having a nil rootViewController) when it restricts the bounds of your window.
Try this:
#interface MyRootViewController : UIViewController
#end
#implementation MyRootViewController
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAll;
}
- (BOOL)shouldAutorotate
{
return YES;
}
#end
Now, add that rootViewController to your window after it is instantiated:
UIWindow *toastWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
toastWindow.rootViewController = [[MyRootViewController alloc]init];
I believe you have to convert the UICoordinateSpace.
On iPhone 6 Plus apps can now launch already in landscape orientation, which messed up an application I am working on since it only supports portrait orientation throughout most of the app, except one screen (meaning it needed to support landscape orientations in the plist).
The code that fixed this was as follows:
self.window = [[UIWindow alloc] initWithFrame:[self screenBounds]];
and calculating the bounds with the following code:
- (CGRect)screenBounds
{
CGRect bounds = [UIScreen mainScreen].bounds;
if ([[UIScreen mainScreen] respondsToSelector:#selector(fixedCoordinateSpace)]) {
id<UICoordinateSpace> currentCoordSpace = [[UIScreen mainScreen] coordinateSpace];
id<UICoordinateSpace> portraitCoordSpace = [[UIScreen mainScreen] fixedCoordinateSpace];
bounds = [portraitCoordSpace convertRect:[[UIScreen mainScreen] bounds] fromCoordinateSpace:currentCoordSpace];
}
return bounds;
}
Hopefully this will lead you in the right direction.
In iOS 7 and earlier, UIWindow's coordinate system did not rotate. In iOS 8 it does. I am guessing the frame supplied from [[UIScreen mainScreen] bounds] does not account for rotation, which could cause issues like what you're seeing.
Instead of getting the frame from UIScreen, you could grab the frame from the appDelegate's current key window.
However, it does not appear you really need functionality supplied by UIWindow. I'd like to echo others' recommendation that you use a UIView for this purpose. UIView is more generic than UIWindow, and should be preferred.
The best thing to do is use views instead of windows:
Most iOS applications create and use only one window during their lifetime. This window spans the entire main screen [...]. However, if an application supports the use of an external display for video out, it can create an additional window to display content on that external display. All other windows are typically created by the system
But if you think you've got a valid reason to create more than one window, I suggest you create a subclass of NSWindow that handles sizes automatically.
Also note that windows use a lot of RAM, especially on 3x retina screens. Having more than one of them is going to reduce the amount of memory the rest of your application can use before receiving low memory warnings and eventually being killed.

unexpected nil window in _UIApplicationHandleEventFromQueueEvent, _windowServerHitTestWindow

I am trying to set up an edge swipe gesture in iOS 8 on iPad but getting and error that seems like a bug.
I have the following code:
UIScreenEdgePanGestureRecognizer *edgeRecognizer = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:#selector(handleRightEdgeSwipe:)];
edgeRecognizer.edges = UIRectEdgeRight;
[self.view addGestureRecognizer:edgeRecognizer];
and then I handle the gesture:
-(void)handleRightEdgeSwipe:(UIGestureRecognizer*)sender
{
//slide in view code here
}
The problem is that it doesn't detect the right edge swipe every time. And sometimes it detects it multiple times.
Whether it detects or not it always shows the following information in the console when swiping the right edge on iPad:
2014-10-07 00:04:40.386 Office Log[1531:500896] unexpected nil window in _UIApplicationHandleEventFromQueueEvent, _windowServerHitTestWindow: ; layer = >
What does this message mean and how can I fix it so that the right edge swipe is detected consistently?
I think it's a bug in iOS, which I can confirm on an iPad mini 2 and an iPad Air on iOS 7 or higher, even on the Home Screen.
In "Landscape Left" (Home button on the left) a "Right Edge Gesture" from outside the Screen is not working for me. Test it by your self on the Home Screen.
I reported a bug to Apple 9 Month ago, but noting further happened.
Update:
I played a bit with the UIWindow init and when it is a bit bigger than it really is, the Gesture works. Of course this is a horrible fix.
self.window = [UIWindow new];
self.window.rootViewController = [[UIViewController alloc] init];
// Real Size
CGRect frame = [UIScreen mainScreen].bounds;
// Real Size + 0.000001
self.window.frame = CGRectMake(0, 0, frame.size.width+0.000001, frame.size.height+0.000001);
[self.window makeKeyAndVisible];
I got the same issue.
My solution works fine: just set in your xib your Windows to hidden.
I don't really understand why it works, but it works.
EDIT 1:
I found another solution, better I think, and more understandable:
Put this code on your willFinishLaunchingWithOptions, in your appDelegate:
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
CGRect bounds = [[UIScreen mainScreen] bounds];
[self.window setFrame:bounds];
[self.window setBounds:bounds];
return YES;
}
Then, on your didFinishLaunchingWithOptions:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Your codes...
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
return YES;
}
Your can then set your window object hidden to NO, and it should works.
I got issue when testing iPhone app on iPad. No problems on simulator and no problem if I compile app as universal and run on iPad.
unexpected nil window in _UIApplicationHandleEventFromQueueEvent, _windowServerHitTestWindow: <UIClassicWindow: 0x1276065a0; frame = (0 0; 768 1024); userInteractionEnabled = NO; gestureRecognizers = <NSArray: 0x1740557e0>; layer = <UIWindowLayer: 0x17403fd80>>
Perhaps frame is reported wrong ? ( frame = (0 0; 768 1024) )
iOS 8 has a bug where any touch that begins exactly on an iPad right edge when in Portrait Upside-Down (home button on top) or Landscape Left (home button on left) mode fails to be hit tested to the correct view.
The fix is to subclass UIWindow to hit test the right side properly.
#implementation FixedWindow
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
UIView* hit = [super hitTest:point withEvent:event];
if (!hit && point.x >= CGRectGetMaxX(self.bounds))
hit = [super hitTest:CGPointMake(point.x - 0.5, point.y) withEvent:event];
return hit;
}
#end
Attach the window to your application delegate via the window property.
#implementation AppDelegate
- (UIWindow*)window
{
if (!_window)
_window = [[IVWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
return _window;
}
#end
In Portrait and Landscape Right modes, I've confirmed that right edge touches are always at least 0.5px from the edge instead of exactly on the edge, so this fix should work in analogy to that.
Expanding the window frame
Note that firebug's fix will also work i.e. slightly expanding the window frame to include the right side. However:
If you do this at application:willFinishLaunchingWithOptions: or application:didFinishLaunchingWithOptions:, your view hierarchy doesn't get resized to the new frame and right edge touches won't make it through the hierarchy.
If you rotate the device, the window may not be centered correctly. This leads to either smearing or bumping interface elements slightly.
iOS 7:
iOS 7 has a similar bug in that the hit test also fails but with a non-nil result and unrotated geometry. This fix should work with both iOS 7 and 8:
#implementation FixedWindow
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
UIView* hit = [super hitTest:point withEvent:event];
if (!hit || hit == self)
{
CGRect bounds = self.bounds;
hit = [super hitTest:CGPointMake(MIN(MAX(point.x, CGRectGetMinX(bounds) + 0.5), CGRectGetMaxX(bounds) - 0.5),
MIN(MAX(point.y, CGRectGetMinY(bounds) + 0.5), CGRectGetMaxY(bounds) - 0.5))
withEvent:event];
}
return hit;
}
#end
One possible fix is to remove or comment out code for hiding status bar if you have that.
I was pulling my hair to solve it, and I could only reproduce it on my root view. It appears that if you hide the status bar you cannot drag down today widgets/notification center (you can with some effort).
/* <- add this
- (BOOL)prefersStatusBarHidden
{
return YES;
}
add this -> */
Set your deployment to 8.x or above, set launch screen as your main xib.
Done!
It may be to late but some people may still need it,
normally the cause is that you haven't supplied correctly sized launch images or a launch screen and/or Main Interface is not set to your own storyboard at General> Deployment Info

Changing the size of UIScreen on rotation

I am making an iPad app that starts out in the portrait orientation but can rotate over to the landscape orientation. In -(void) loadView, I call the function drawView. In drawView, I have this line of code:
CGRect r = [[UIScreen mainScreen] bounds];
The only problem is that it doesn't update itself when I rotate to landscape mode, so it still thinks that the screen is in the vertical orientation and if I want a text view to extend all the across the entire screen, it cuts it off at the 768th pixel, instead of the 1024th pixel. In -(BOOL)shouldAutorotate... I have case UIInterfaceOrientationLandscapeRight: and case UIInterfaceOrientationLandscapeLeft:, and I would have to ideally place CGRect r = [[UIScreen mainScreen] bounds]; under each case, but I don't think that will work. Any suggestions? Thanks for your help!
Edit: I've even tried calling a function with
CGRect r = [[UIScreen mainScreen] bounds];
in it, but it still won't work. I place an NSLog after it and I received a response, so the app is definitely working properly and not crashing, but I am still unable to figure this out. Any ideas? Thanks for your help!
I think your problem could be solved by using the autoresizingMask of UIView;
Does your app happen to rely on plists for configuration? I've seen some apps where you can get landscape but only after you set an orientation key to false.

Resources