SpriteKit and Scrollable List - ios

I've been trying to figure out an "easy" way to have a scrollable vertical list for my SpriteKit game.
I'm going to use it to implement the shop / upgrades part of my game so I would like the user to be able to scroll down the list to see the different upgrades that are possible.
I've seen quite a few posts on integrating a UIScrollView into SpriteKit but most of them seem to scroll the whole screen.
What I have so far is:
-(void) createSceneContents
{
[super createSceneContents];
_scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(10,100, 250, 200)];
_scrollView.contentSize = CGSizeMake(2000, 120);
_scrollView.scrollEnabled = YES;
_scrollView.showsHorizontalScrollIndicator = YES;
_scrollView.backgroundColor = [UIColor brownColor];
[self.view addSubview:_scrollView];
//Test sprites for scrolling and zooming
SKSpriteNode *greenTestSprite = [SKSpriteNode spriteNodeWithColor:[SKColor greenColor]
size:CGSizeMake(25, 25)];
[greenTestSprite setName:#"greenTestSprite"];
greenTestSprite.position = CGPointMake(25, 125);
[self addChild:greenTestSprite];
//[self addChild: [self newMenuNode]];
}
I'm trying to think what the easiest way is for me to be able to add the green test sprite into that UIScrollView. can't get my head round the way that other people have done this as they seem to be connecting the scrolling of the UIScrollView with the SKView to make the screen seem like it's scrolling.
I'm sure there is a much easier way of doing this but been searching for a while now.
Any help would be appreciated.
Thanks allot in advance.

I would avoid using UIKit components if possible. When you have a UIView that is parented to UIKit components, it makes it difficult to interact with your SpriteKit components that live in the SKView. It's pretty easy to subclass SKNode and make a "ScrollingNode" that uses gesture recognizers on the SKView to do the scrolling. Here's an example:
https://github.com/JenDobson/SpriteKitScrollingNode
Another great tutorial to take a look at is http://www.raywenderlich.com/44270/sprite-kit-tutorial-how-to-drag-and-drop-sprites. Go to the part at the end on gesture recognizers.

You can use UIKit in a SpriteKit application.
If you use a UIScrollView you are going to end up having to get your hands dirty regarding re-use of cells, layout, user-interaction etc. You can actually make a cell look pretty good (and a cell is a UIView so you can sack a bunch of subviews in there) if you just use (and customise) a UITableView
Your hierarchy would be:
Scene:
TableView

Related

How can I use CCClippingNode to cut the top and bottom of a CCTableView

I'm fairly new to Cocos2d and I'm "really" struggling with sizing a CCTableView. What I'm referring to is how can its height be controlled so that I have space at the top and the bottom.
I have my table view functioning and I'm trying to visually wrap the top and bottom with a sprite to provide the look and feel of a frame. But when I touch the table view to scroll
It up or down, it occupies most of the screen vertically. I started messing with the contentSize, thinking this would alter where each cell appears and disappears from the top and bottom. But this had no effect.
Having had s Google about, I'm reading that I have to use a Clipping Node with a Stencil? Coming from UIkit, this seems like a bit of a faff. Would anyone like to hazard at a tutorial, bit of code with a simple CCTableView representation, sat in the middle of the scene where each cell appears and dissapears under a content frame?
You should take a look at CCCropNode in the cocos2d extensions package:
https://github.com/cocos2d/cocos2d-swift-ext/tree/master/Extensions/CCCropNode
the CCCropNode uses a glScissor to clip the drawing to a specified area: Much simpler than the CCClippingNode (which uses the stencil buffer).
Also this forum post might help:
http://forum.cocos2d-swift.org/t/ccclippingnode-woes/14752
You'll still have issues with touches outside of the target region hitting into your scroll / table area (and I've found that the CCCropNode touch clipping doesn't work - need to investigate that as it is causing me problems).
I faced the same issue and I managed to solve the touches outside the table region by adding code to CCScrollView.m as following:-
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(CCTouch *)touch
{
// Added by Season
//*************************************************************
CGPoint lcgpTouch = [touch locationInWorld];
if (!(lcgpTouch.x >= [self position].x && lcgpTouch.x <= [self position].x + [self contentSize].width &&
lcgpTouch.y >= [self position].y && lcgpTouch.y <= [self position].y + [self contentSize].height)) {
return NO;
}
//*************************************************************
if (!_contentNode) return NO;
if (!self.visible) return NO;
if (!self.userInteractionEnabled) return NO;
And you are required to setContentSize to CCTableView in order to have this piece of code get the size correctly.
Additional info:
I have some CCSprite on top of the outside target region that suppose to response to touchBegan failed after the above coding, then I changed all CCSprite to CCButton and it works like a charm. You do not need to do what I did here, you can modify coding to allow CCSprite to response to touchBegan easily.
Hope this coding above help you all.

Adding UITextView to a scene in SpriteKit

I trying to create a game, and following Apple's advice I am using multiple scenes.
You could consider the game to be a text heavy point and click adventure.
Therein lies the problem. Lots of text, having done a bit of a search, it seems the recommend way, or rather the current only way to do this is with a UITextView thus:
[self.view addSubview: textView];
This is all well and good if you only have one SKScene, as that is the scene being displayed by the current view/SKView.
My problem is, that when I add the text to my scene's *view, which isn't the first scene the app loaded (its the third, or higher), the app jumps back to the first scene it loaded (which is my menu scene) and happily displays the required text.
Anybody got any idea why? I have menu scene transitioning to scene one, to scene two (in which I wish to display the test).
Please don't say I need a view per scene if I want to display more than a handful of words per scene, that just doesn't really make sense, but perhaps neither does my usage of SpriteKit.
I am still some what stunned there is no SK equivalent of the UITextView.
Anyway, any help, pointers would be great, thank you.
Ok, here are the relevant parts of the code.... I think.
Controller:
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
// Configure the view.
SKView *skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
skView.showsDrawCount = YES;
// Create and configure the scene.
SKScene *scene = [[GTMainMenu alloc] initWithSize:skView.bounds.size];
// Present the scene.
[skView presentScene:scene];
}
where GTMainMenu is a subclass of SKScene, and has a button (orange box) to an "act" (A subclass of GTScene, itself a subclass of SKScene), this will cause a transition to the act, which has another button to the first scene.
Only you never make it to the scene, as the viewDidLoad returns you to the main menu.
This is the viewDidLoad of the scene, which will cause it to "jump" back to the main menu:
- (void)didMoveToView:(SKView *)view
{
[super didMoveToView:view];
if (!self.contentCreated) {
[self createSceneContents];
self.contentCreated = YES;
}
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(self.size.width/2, self.size.height/2+20, 200, 40)];
textView.center = self.view.center;
textView.textColor = [UIColor blackColor];
textView.font = [UIFont systemFontOfSize:17.0];
textView.backgroundColor = [UIColor whiteColor];
textView.text = #"Where am I?";
[self.view addSubview:textView];
}
There is a git repo available here.
This is a striped down version of my project, removing everything that is unrelated to the issue at hand.
If you will excuse the code, my day job is Java, and I am struggling with certain concepts in Objective C at the moment.
Oh and sorry I managed to include the usersettings :S
Your view controller's viewWillLayoutSubviews method is not safeguarded against repeated execution. This method will not just run at app launch but every time the view rotates and resizes.
Add a check before creating/presenting a scene in that method:
if(self.view.scene == nil) { /* present scene */ }
Have you looked into SKLabelNode? I used it extensively in my SpriteKit game. If you need your SKLabelNode to do anything fancy (physics, etc.), you can just add it to a parent SKSpriteNode.

CATransformLayer touch events handling

I have searched over the internet and Stackoverflow but i couldn't find any answer to this specific question, So here's my question,
I'm trying to create a double sided UIView which responds to touch events both sides(Such as UIButton, UISwitch, etc...). I have achieved that by adding two distinct CALayers to CATransformLayer. All works good but touches doesn't responds to the ui elements added from interface builder. To replicate the problem, i have created an UIView in the nib(frontView) added all ui elements from interface builder and add that UIView reference to CATransformLayer and all works good except it doesn't respond to touch events.
I understand that CALayer or CATransformLayer doesn't inherits from UIResponder class like UIView. And i believe there is a way to bring this layer to responder chain but i'm wondering how?. Below is my simplified problem code,
- (void)viewDidLoad
{
[super viewDidLoad];
CATransformLayer *myLayer = [[CATransformLayer alloc] initWithLayer:self.view.layer];
myLayer.frame = CGRectMake(0, 0, 300, 300);
[myLayer addSublayer:frontView.layer];//Here 'frontView' is a reference to UIView from nib file
[self.view.layer addSublayer:myLayer];
}
Any support will greatly appreciate.
Thanks in advance.

Overlaying a UIView onto a Cocos layer?

I'm new to iOS and Cocos development.
I currently have a basic app going on in my HelloWorldLayer class. It contains my sprites and touch interaction methods and all is well.
I'm trying to add another "panel" (UIView?) over top of what is currently seen. Eventually this panel will have buttons or other things that will interact with the main canvas.
How can I include another UIView onto the canvas screen? Through my appDelegate, or my HelloWorldLayer?
Thanks
Here is one way to do it. I've used UITextView here but you could use the approach for any descendant of UIView. Bear in mind that UIKit's y coordinate is zero at the top-left of the screen, whereas Cocos2D's is zero at the bottom left.
// Make your subview
UITextView* t = [[UITextView alloc] initWithFrame: CGRectMake(10, 10, 200, 200)];
t.backgroundColor = [UIColor blackColor];
t.textColor = [UIColor whiteColor];
t.text = #"Hello UIKit!";
t.editable = NO;
// Add it as a subview of the Cocos2D view
UIView* cocosView = [[CCDirector sharedDirector] openGLView];
[cocosView addSubview:t];
Alternatively you could try Blue Ether's CCUIViewWrapper, repository here.

Nested UIScrollView won't bounce and does not detect slow swipe gestures when bounce is set to NO

Sorry for the long and self explanatory title, but UIScrollView has raised so many questions that I find it difficult to reach the ones that might help in different situations.
I have nested scrollviews in my iPad app. So far so good, everything has its ups and downs but its quite slick and responsive. My outter scroll view is a paged one, that contains fullscreen or bigger content scrollviews in it. Outter scrollview is horizontal and inner vertical. Like the photo gallery one. I found that when I'm zooming and scrolling the inner scrollview, there's a noticeable delay in detecting slow and long swipe gestures ONLY when the scrollview has been scrolled down to the bottom of the content and bouncing is OFF.
the other thing is that the inner scrollview bouncing property goes YES/NO pseudo-randomly. So, this is the code in the constructor that set the inner scrollviews that are acting up:
if (UIInterfaceOrientationIsPortrait(forOrientation)) {
self.minimumZoomScale = 1.0;
self.maximumZoomScale = 1.0;
self.bounces = NO;
self.alwaysBounceVertical = NO;
self.scrollEnabled = NO;
}else if (UIInterfaceOrientationIsLandscape(forOrientation)){
self.minimumZoomScale = 1.333333f;
self.maximumZoomScale = 1.333333f;
self.bounces = YES;
self.alwaysBounceVertical = YES;
self.scrollEnabled = YES;
}
self.scrollsToTop = NO;
self.showsVerticalScrollIndicator = YES;
self.showsHorizontalScrollIndicator = NO;
self.directionalLockEnabled = YES;
self.delegate = self;
self.pagingEnabled = NO;
self.canCancelContentTouches = NO;
self.delaysContentTouches = YES;
When the iPad is rotated bouncing will come and go for the scroll view as well and will have a bouncing glitch too.
Is this a bug ? or is it just me that I'm messing it up?
thanks in advance for your time and interest!
UPDATE:
I'm nesting two scrollviews that are actually Subclasses of UIScrollView. I'm doing this because I need to override hitTest an other methods as well. I also tried the Better solution described here http://openradar.appspot.com/8045239 and did not get any good results.
I'm answering my own question.
Open radar's bug solution that's posted on the question post, it's not very clean on what to isolate in order to stop the uiscrollview from resizing and cancel bounces
Basically EVERYTHING that can change the view's frame during layoutSubViews has to be done only ONCE by double checking that if the size is equal to the change that's coded in layoutSubviews, then that's not ran more than once.
-(void)layoutSubviews {
///...
if (!self.bounces) {
self.bounces = YES;
}
if(!self.scrollEnabled){
self.scrollEnabled = YES;
}
if (!CGSizeEqualToSize(rect.size, self.contentSize)) {
self.contentSize = rect.size;
}
if (self.zoomScale < MAXIMUM_ZOOM_SCALE ) {
[self zoomToRect:ZOOM_RECT_MAKE animated:NO];// otherwise this line of code won't do anything at all
}
}
Nesting scrollviews will always lead to touch-responder issues. I'm assuming that the inner is the primary one—the one users interact with more? In general, you're probably going to have to write some of your own touch responders (at least, that has been my experience, both in code and around SO). Make the vertical scrolling more forgiving, but only detect a narrow range of swipes and drags (near-horizontal lines) for the outer. That way, most of the touch inputs will be sent to the inner view.
As far as the bouncing goes, remove one of the places where you set the property and see what happens.
Which method is this in? Hopefully this code is residing in a UIView subclass…?

Resources