I've set up a small sample project based on the SceneKit template, with controls similar as what you described. It's in Objective C but the relevant parts are pretty much the same:
- (void) handlePan:(UIPanGestureRecognizer*)gestureRecognize {
CGPoint delta = [gestureRecognize translationInView:(SCNView *)self.view];
if (gestureRecognize.state == UIGestureRecognizerStateChanged) {
panHorizontal = NO;
if (fabs(delta.x) > fabs(delta.y)) {
panHorizontal = YES;
}
} else if (gestureRecognize.state == UIGestureRecognizerStateEnded) {
SCNMatrix4 rotMat;
int direction = 0;
if (panHorizontal) {
if (delta.x <0) {
direction = -1;
} else if (delta.x >1) {
direction = 1;
}
rotMat= SCNMatrix4Rotate(SCNMatrix4Identity, M_PI_2, 0, direction, 0);
} else {
if (delta.y <0) {
direction = -1;
} else if (delta.y >1) {
direction = 1;
}
rotMat= SCNMatrix4Rotate(SCNMatrix4Identity, M_PI_2, direction, 0, 0);
}
if (selectedNode == mainPlanet) {
selectedNode.transform = SCNMatrix4Mult(selectedNode.transform, rotMat);
} else {
SCNMatrix4 transMat = SCNMatrix4MakeTranslation(selectedNode.position.x, selectedNode.position.y, selectedNode.position.z);
selectedNode.transform = SCNMatrix4Mult(selectedNode.transform, SCNMatrix4Invert(transMat));
selectedNode.transform = SCNMatrix4Mult( selectedNode.transform, mainPlanet.transform);
selectedNode.transform = SCNMatrix4Mult(selectedNode.transform, rotMat);
selectedNode.transform = SCNMatrix4Mult(selectedNode.transform,SCNMatrix4Invert(mainPlanet.transform));
selectedNode.transform = SCNMatrix4Mult(selectedNode.transform,transMat);
}
}
}
In handleTap:
selectedNode = result.node
In viewDidLoad:
mainPlanet = [scene.rootNode childNodeWithName:@"MainPlanet" recursively:YES];
orangeMoon = [scene.rootNode childNodeWithName:@"orangeMoon" recursively:YES];
yellowMoon = [scene.rootNode childNodeWithName:@"yellowMoon" recursively:YES];
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
[gestureRecognizers addObject:panGesture];
And local vars:
SCNNode *mainPlanet;
SCNNode *orangeMoon;
SCNNode *yellowMoon;
SCNNode *selectedNode;
BOOL panHorizontal;

MainPlanet would be your mainContainer and doesn't have to be visible (it does in my example because it has to be tapped to know what to rotate...). The two moons are your node A and B, child nodes of the main node. No wrappers necessary. The key part is obviously the commented portion.
Normally to rotate a child node in local space (IF the parent node is at 0,0,0)
- First move it back to the node by multiplying its transform with the inverse of its translation only.
- Apply the rotation matrix.
- Apply the original translation we removed in step 1.
As you noticed that will rotate the child node on its local pivot point and over its local axis. This works fine until you rotate the parent node. The solution is to apply that same rotation to the child node before rotating it based on the pan gesture (step 2), and then after that remove it again.
So to get the results you desire:
- First move it back to the node by multiplying its transform with the inverse of its translation only.
- Apply the rotation of the parent node (since it's at 0,0,0 and I assume not scaled, we can use the transform).
- Apply the rotation matrix based on the pan gesture.
- Remove the rotation of the parent node
- Apply the original translation we removed in step 1.
I’m sure there are other possible routes and perhaps instead of step 2 and 4 the rotation matrix could be converted to the main node using convert to/from but this way you can clearly tell what’s going on.