Наконец-то я приступил к реализации прокрутки CCLayer с помощью производного класса UIScrollView, следуя инструкциям, упомянутым в этом вопросе:
Оба они отлично читаются, и настоятельно рекомендуется получить более глубокое понимание того, как UIScrollViews (и UIViews в целом) могут быть созданы для обработки касаний в сотрудничестве с Cocos2D.
Однако, после реализации этих решений, я также столкнулся с ошибкой, описанной в вопросе: если вы не прокручиваете, касания распространяются от UIScrollView к CCLayer правильно. Если вы прокручиваете, слой прекрасно прокручивается, но не прокручиваемые касания UIScrollView распространяются на CCLayer со смещением, которое увеличивается с увеличением прокрутки, что делает CCLayer (и / или сопровождающее CCMenus) непригодным для использования.
Я обнаружил, что причиной этой проблемы является ошибка в том, как Cocos2D транслирует касания, отправленные в OpenGLView, в локальные системы координат CCNode или CCMenu. Эта ошибка присутствует в 1.0 rc и затрагивает только те касания, которые генерируются в подпредставлении OpenGLView (например, в нашем UIScrollView) и распространяются в основную область касания Cocos2D (а именно в OpenGLView), вызывая следующую строку внутри -touchesBegan метода UIScrollView:
[[[CCDirector sharedDirector] openGLView] touchesBegan:touches withEvent:event];
(Обратите внимание, что вызова этой предыдущей строки достаточно для распространения касаний без прокрутки и без масштабирования из UIScrollView в Cocos2D, вам не нужно вызывать nextResponder: как это делают вышеупомянутые сообщения в блоге.)
Решение поставляется с небольшой модификацией двух источников Cocos2D:
CCNode.m
- (CGPoint)convertTouchToNodeSpace:(UITouch *)touch {
// cocos2d 1.0 rc bug when using with additional overlayed views (such as UIScrollView).
// CGPoint point = [touch locationInView: [touch view]];
CGPoint point = [touch locationInView: [[CCDirector sharedDirector] openGLView]];
point = [[CCDirector sharedDirector] convertToGL: point];
return [self convertToNodeSpace:point];
}
- (CGPoint)convertTouchToNodeSpaceAR:(UITouch *)touch {
// cocos2d 1.0 rc bug when using with additional overlayed views (such as UIScrollView).
// CGPoint point = [touch locationInView: [touch view]];
CGPoint point = [touch locationInView: [[CCDirector sharedDirector] openGLView]];
point = [[CCDirector sharedDirector] convertToGL: point];
return [self convertToNodeSpaceAR:point];
}
CCMenu.m
-(CCMenuItem *) itemForTouch: (UITouch *) touch {
// cocos2d 1.0 rc bug when using with additional overlayed views (such as UIScrollView).
// CGPoint touchLocation = [touch locationInView: [touch view]];
CGPoint touchLocation = [touch locationInView: [[CCDirector sharedDirector] openGLView]];
touchLocation = [[CCDirector sharedDirector] convertToGL: touchLocation];
...
Ключом здесь является UITouch locationInView: метод. Аргументом для этого метода должен быть UIView, в который вы хотите перевести координаты касаний. Большинство проектов Cocos2D имеют только один UIView: OpenGLView, поэтому касания генерируются в OpenGLView (= сенсорное представление) и переводятся в одно и то же представление. Однако, если вы добавляете накладывающиеся подпредставления для получения касаний, такие как UIScrollView, «представление касанием» будет иметь это значение, которое больше не соответствует требуемому OpenGLView.