UIKeyboardWillChangeFrame and iPhone X - uikeyboard

Trying with simulator here, and it seems that the frame returned when listening for changes with UIKeyboardWillChangeFrame is incorrect for iPhone X. Specifically the frame seems to be a bit taller than it actually is (visibly) on screen. I'd say the returned frame is about 20 points taller.
Did I forget something obvious?
Some data...
The iPhone X and iPhone 8 have the same width in portrait, which makes it the same height in landscape; 375 points. ( https://www.paintcodeapp.com/news/ultimate-guide-to-iphone-resolutions )
// iPhone X values:
// keyboardChange, frame: (0.0, 375.0, 812.0, 171.0)
// keyboardChange, frame: (0.0, 204.0, 812.0, 171.0)
// iPhone 8 values:
// keyboardChange, frame: (0.0, 375.0, 667.0, 162.0) (keyboard closed)
// keyboardChange, frame: (0.0, 213.0, 667.0, 162.0) (keyboard opened)
We can see that the Y value when keyboard is opened on the iPhone X is smaller, which leads us to align any of our own UI elements higher on screen. Ie they will not align perfectly on top of the keyboard. Which is my current problem, and I assume all other apps doing that will also suffer from this problem.

My question did not contain enough info to be able to directly attack the problem, I see now. As I wrote in my comment above, the issue was not a problem with iPhone X per se but if you want to put a UI element on top of the keyboard using the frame supplied with the UIKeyboardWillChangeFrame notification, you must make sure to constrain to the extreme bottom of the superview, and not to the safe area bottom, which had happened in my storyboard as I added safe area support.

Related

finding proportion/ratio for clamping a font size given a min/ideal/max font size and min/current/max screen width

I am trying to create a clamping formula (same logic from CSS) to make the typography more responsive in PowerApps. I have a minimum, maximum and an ideal size that is a dynamic calculation. Which gives us:
Max(min_,(Min(ideal_),max_)))
Now I am struggling to find that ratio. In my case the screen width will never go lower than 360px and the min font size for this example is 16px, the max 40px, when the screen is large/extra large (meaning anything above 900px in our case).
Now how can I represent a formula that calculates a value in between these two that considers the current width of the screen? This has very little to do with PowerApps is more of a math question and general responsive design, I just don't know how to do it :D
I could guess compound proportion as in:
16 px f -> 360px w
x px f -> current px w
40 px f -> > 900 px w
Is this logic right? What do I do now? This might look obvious to you so please try to guide through or give some video/link/article.
Thank you all.
For whoever wondering about this. I think I found the answer.
Max(minsize_,(Min((minsize_+(maxsize_-minsize_)*((App.Width - App.MinScreenWidth) / (maxscreenwidth_ - App.MinScreenWidth))),maxsize_)))
Taken and adapted from https://css-tricks.com/snippets/css/fluid-typography/

Predict Where UI Will Land Based on Velocity (scrollrect unity)

I know this may not be a normal question but I think you can help me figure it out.
Background: I want to create a scrollrect (a scrollable UI element) that snaps onto the elements it's scrolling. So that it always comes to a stop with an element in the center. The scrolling of these scroll rects is based on the velocity your finger was swiping at when it left the screen, and if you input a certain velocity it always moves the same amount (within 1 pixel).
So I figured the smoothest way to create this snapping scroll rect would be to predict where the scroll rect would land & then adjust the deceleration rate so that it landed on the nearest element instead.
So basically I would like to:
Turn this loop into a math function where I can input velocity & get out the movement delta.
Be able to figure out what the deceleration rate should be based on the end movement delta & velocity.
Here's the code that the scroll rect uses for its movement:
protected virtual void LateUpdate()
{
//It's probably easiest if you imagine positionX always starting at 0
//but I'm no expert
m_VelocityX *= Mathf.Pow(m_DecelerationRate, Time.unscaledDeltaTime);
if (Mathf.Abs(m_VelocityX) < 1)
{
m_VelocityX = 0;
}
positionX += m_VelocityX * Time.unscaledDeltaTime;
}
Where LateUpdate is called every frame, and positionX is the x position of the UI I am moving. (it holds the UI elements I want to snap too)
ScrollRect Code
LateUpdate Info
Mathf Info
Time.unscaledDeltaTime Info
And here are some velocities and movement deltas (how far it traveled), where the deceleration rate is 0.135 if that's helpful:
Velocity 500 -> 490
Velocity 350 -> 343
Velocity 200 -> 195
Velocity -200 -> -195
Velocity -400 -> -391
Ty all so much for the help! This math is way to hard for me to wrap my head around but I think it will end up being cool!

iOS 11 SceneKit hitTest:options: fails

I'm facing a difficult situation using hitTest:options: in SceneKit on iOS 11.
In a maping application I have a terrain node. Using hitTest:options: I was able for long to spot a point on the terrain from a touch on the screen. It still work as expected with released binary on iOS 11, and also on Xcode 9 compiled binary for iOS 10 simulator.
But iOS 11 binary on iOS 11 SDK gives totaly eratic results. Return array from hitTest:options: may contain no result or too many. Moreover, most of the time none of the results is valid. Here below are images to illustrate the point. All image are from a scene with no hidden node.
Edit: I made a test today using hitTestWithSegmentFromPoint:toPoint:options: and got false results also.
First with working simulator.
It shows a normal hit on the terrain. The hit point is illustrated with a red ball. It is half inset in the terrain as its center is right on the terrain.
These two images show a case where the "ray" cross the terrain 3 times. We got 3 hits all placed correctly on the terrain.The second image change the angle of view to show the 3 points.
Now the failing iOS 11 situation:
On this picture we got one hit but it is "nowhere" between the two mountains, not on the terrain.
The last two pictures show other attempts with 4 and 16 hits, all "in the blue" with no connection to the terrain.
Sometimes the hit are "away" past the terrain, sometimes they are between the camera and the terrain.
I was facing the same problem on iOS 11. My solution:
var hitTestOptions = [SCNHitTestOption.sortResults : NSNumber(value: true),
SCNHitTestOption.boundingBoxOnly : NSNumber(value: true)]
if #available(iOS 11.0, *) {
hitTestOptions[SCNHitTestOption.searchMode] = SCNHitTestSearchMode.all.rawValue as NSNumber
}
Four years latter I went back to this problem and found a solution to my original problem.
After Apple released iOS 11.2, multiples hits were solved but we got a "no hits" conundrum.
The problem lies in a specific situation that was not fully explained in the original question. After a terrain is originally computed and displayed we always get a first hit. Then we pan the terrain to center the hit point and rebuild a new terrain sector. In the process, we save computing by reusing severals geometry elements, only changing the z coordinates of the terrain vertexes. The problem lies in reusing the triangle strip SCNGeometryElement. From now on, any terrain built by reusing this object is fine looking but fails the hitTest method.
It turns out that the SCNGeometryElement can't be reused and should be rebuilt.
The originally working code was :
t_strip = [geom_cour geometryElementAtIndex:0];
To workaround the HitTest: failure we have to do :
//get current triangle strip
SCNGeometryElement *t_strip_g = [geom_cour geometryElementAtIndex:0];
//create a new one using the current as a template
t_strip = [SCNGeometryElement geometryElementWithData:t_strip_g.data
primitiveType:t_strip_g.primitiveType
primitiveCount:t_strip_g.primitiveCount
bytesPerIndex:t_strip_g.bytesPerIndex];
The current SCNGeometryElement is used as a template to recreate a new one with exactly the same values.

Unity3D: TPS shooting without mouse aiming

I'm currently developing some TPS game. I have my player model and camera snapped to its shoulder, and some Empty game object in front of player at some distance for calculating vector for bullets (Yellow diamond at screenshot).
I'm developing for mobile platforms, so there is no mouse; just that Empty game object that points direction of the gun.
So when a fire event occurs I want to apply force to bullet and it will fly in right direction. Here is my code
b.transform.position = transform.position;
b.transform.position += transform.forward;
b.SetActive(true);
var rb = b.GetComponent<Rigidbody>();
print((Aim.position - transform.position).normalized);
rb.AddForce((Aim.position - transform.position).normalized * Thrust);
Aim is my EmptyGameObject that points direction, transform is GunEnd gameobject, and b is my bullet instance. So if I try shoot from default player position bulet flies correct from GunEnd to Aim object.
But if I rotate character for example more that 90 degree left, bullets start to fly in some weird trajectory
So, can anybody help me how to correct send bullets?
When you move it´s position with b.transform.position += transform.forward; you might be setting it in an odd place if the transform does not rotate when you aim (and by what I can see in the screenshot, it is not rotating as its components in the transform.rotate remain the same in y). Try moving it with the vector you find with the Aim, like this:
b.transform.position += (Aim.position - transform.position).normalized;

Upgrading old app to iOS 6 - open GL issues on iPhone 5

I'm finally upgrading a very old universal game app I have to play nice with newer OSs and device sizes - I've got everything updated and targeting iOS 6.1 right now but when I run it on the iPhone 5, my actual in game view, which is rendered using open GL into an EAGLView, is positioned very strangely and shows a lot of clipping (see screenshot).
On the "normal" devices that were around when we first created this, everything appears as expected.
In my view controller, I basically load a nib with the right size set for the different devices - iPad and non 4" devices get a 1024x768 view and the 4" device gets a new 1136x640 view.
Then, in my viewDidLoad, I set up my view's self.view.contentScaleFactor to [UIScreen mainsScreen] scale], I then do some view sizing like so (roughly):
if(iPad){
[self.view setFrame:CGRectMake(0, 0,1024,768)];
[self.view setCenter:CGPointMake(384,512)];
DefaultViewScale=1.2;
}else if(WideScreen){
[self.view setFrame:CGRectMake(0, 0, 568, 320)];
[self.view setCenter:CGPointMake(160, 293)];
DefaultViewScale = 1.0f;
}else{
[self.view setFrame:CGRectMake(0, 0,480,320)];
[self.view setCenter:CGPointMake(160,240)];
DefaultViewScale=1.0f;;
}
Lastly, I apply a transform to scale the view by a factor defined above which I've just hand tweaked and then rotated it since the app is Landscape-Left only.
[self.view
setTransform:
CGAffineTransformConcat(CGAffineTransformMakeScale(DefaultViewScale,DefaultViewScale),
CGAffineTransformMakeRotation(-M_PI_2))];
I then initialize a new EAGLContext (openGL ES 1),
[(EAGLView *)self.view setContext:context];
[(EAGLView *)self.view setFramebuffer];
setFramebuffer is mostly:
[EAGLContext setCurrentContext:context];
// Create default framebuffer object.
glGenFramebuffers(1, &defaultFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
glGenRenderbuffers(1, &colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &framebufferWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &framebufferHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);
glViewport(0, 0, framebufferWidth, framebufferHeight);
There's some more boilerplate EAGLView code but note that I'm setting the glViewport to whatever gl tells me it's width and height is which is grabbed from the UIView's layer size.
And finally it sets up the projection matrix:
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrthof(0, self.view.frame.size.width , 0, self.view.frame.size.height, -1, 1);
glMatrixMode(GL_MODELVIEW);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND_SRC);
glEnableClientState(GL_VERTEX_ARRAY);
glDisable(GL_DEPTH_TEST);
// Set the colour to use when clearing the screen with glClear
glClearColor(51.0/255.0,135.0/255.0,21.0/255.0, 1.0f);
glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA);
This is not my strongest area of knowledge, so let me know if I've missed something and I can get you more info if needed. If anyone has an "a ha" or a similar experience, I'd appreciate some tips in the right direction.
Thanks
Short answer, start using GLKView instead of EAGLView.
First, a good way of getting to know the best practice for setting up e.g. an OpenGL context in the most recent version of iOS is to create a new project using the "OpenGL Game" template and look at for reference.
The most significant difference is the GLKView (introduced in iOS 5.0), which greatly simplifies things for you. It will take care of most of the things you now do manually, including setting up the viewport.
Start by replacing use of the EAGLView with the GLKView (make sure to reference GLKit.framework in your project).
Remove the call to the setFramebuffer method. GLKit takes care of this for you.
Remove the [self.view setTransform:] call. If your app is full-screen OpenGL, there is no need to use view transforms. (And it not, it is likely that it is still not needed).
Set the frame of the view to the bounds of the screen (e.g. by letting it autoresize). You can probably do this in the XIB.
Make sure to call [context setCurrentContext] in your viewDidLoad somewhere.
That should more or less be it. Also make sure to set the context property of the GLKView to the OpenGL context, just as for the EAGLView.
I suggest ensuring that your launch images are up-to-date.
OpenGL is definitely not my area of expertise, but I had a similar issue when upgrading a flash game to iOS 6. I did not supply appropriate launch images for the retina displays of the new iPhone etc, and my app was run in 'compatibility mode' with margins at the top and bottom of the screen. Admittedly, you don't quite have these margins, but it's possible that it's messing with how big your app thinks its screen is. Might be worth checking out.
Why is your "DefaultViewScale=1.2" on an iPad? If the app is fullscreen, it shouldn't be scaled anymore since it's 1024x768. Are you rescaling something there?
In my OpenGL Apps I just have a EAGLView that is always fullscreen and then read the sizes from the framebufferWidth/height.
If you have a UIViewController, with the HUD-Elements correctly set up, you wouldn't need to do any [self.view setTransform..]. I have the feeling you're making life more complicated for yourself, then it should be!
Just add the EAGLView with "UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth" as the lowest subview of your ViewControllers main view. And set up the rotation code correctly (keep in mind the iOS5 calls so shouldAutoRotateToInterfaceOrientation:.. are deprecated).
It looks like the transformation you're doing is after setting the frame of the view, and may therefor change the bounds. I would suggest breaking in your draw method and checking the bounds of both the view and its layer.
Remember that the frame is set from the perspective of the parent, while the bounds is in local coordinates. From UIView.h:
do not use frame if view is transformed since it will not correctly
reflect the actual location of the view. use bounds + center instead.

Resources