Well, that was just a hunch, but I think it turned out to be right, so let it become formal!
It seems that your problem is that there are two 60 Hz timers / loops in the main thread - the CoreMotion update queue and the SceneKit rendering cycle. You effectively exchange with each other that you set the SceneKit state in the CoreMotion update handler and hope that the SceneKit rendering cycle picks it up in the same frame.
(I suspect the hardware difference is that the iPhone 6 has sufficient performance overhead, that the time is working fine, and outdated equipment is not, so SceneKit picks up model positions that are a frame or two behind your motion data.)
Instead, do everything in the same 60 Hz cycle. (Actually, this is a good general tip, whether your other 60Hz event source is CoreMotion or something else - in a game engine or similar real-time renderer, you should let the target frame rate for rendering render everything else, what are you doing.)
This means that you should be a survey for motion data, and not for it to be pressed on you - you only care about the state of motion with the time stamp of the frame that you are performing, and not with a countdown from 1/60 second to or last few samples if you drop the frame. Connect to the SceneKit rendering loop with a scene rendering delegate , read CoreMotion deviceMotion in your renderer:updateAtTime: method and set the model rotation there. Note that you still need to call startDeviceMotionUpdatesUsingReferenceFrame: before you can start polling motion data.
In addition, SceneKit usually starts the rendering cycle only when it knows that the content of the scene should be animated. if you attached the animation to something in the scene or set an animation property in a transaction or used actions or physics. (Thus, if the next render of the frame is the same as the last one, it will not work on your deviceβs battery, repeating the same thing over and over.) But when you expect to make changes during the animation cycle, you need to make sure that it works all the time.
Setting the playing property in your view to true is actually the right way to do this - it tells SceneKit that you want to keep the rendering, and not stop when the animation stops. (Yes, the documentation would probably be clearer on this ... if you let Apple know about this , they will probably fix it.)
Finally, Swift's hint: you can directly assign elements in properties using struct types without having to create a whole new value, so instead:
cameraRollNode.eulerAngles = SCNVector3Make(0.0, 0.0, -roll) cameraPitchNode.eulerAngles = SCNVector3Make(pitch, 0.0, 0.0) cameraYawNode.eulerAngles = SCNVector3Make(0.0, yaw, 0.0)
You can do it:
cameraRollNode.eulerAngles.z = -roll cameraPitchNode.eulerAngles.x = pitch cameraYawNode.eulerAngles.y = yaw