I'm trying to add an iCarousel to my game that will be displayed in the MainMenu, where you will be able to select an item and unlock others while you play and do more progress within the game.
Tried looking into a few tutorials but i still have some problems and i can't figure it out.
This is the post I based my current code on and also this github
But the structure on that "SpriteKit iCarousel" github is different than mine too.
I can make the carousel be displayed but doesn't even work, and is also located on top-left of the screen like "stucked" for some reason.
So this is my GameViewController.swift code:
import UIKit
import SpriteKit
import GameKit
class GameViewController: UIViewController, iCarouselDataSource, iCarouselDelegate {
var imageArray: NSMutableArray = NSMutableArray()
var selectedIndex: Int!
var carousel : iCarousel!
deinit{
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func showCarousel(){
carousel.hidden = false
}
func hideCarousel(){
carousel.hidden = true
}
override func awakeFromNib(){
super.awakeFromNib()
self.imageArray = NSMutableArray(array: ["white","white2","white3"])
}
func carousel(carousel:iCarousel, didSelectItemAtIndex index:NSInteger) {
let scene = MenuScene(size:self.view.bounds.size)
scene.imageName = self.imageArray[index] as! String
self.hideCarousel()
}
override func viewDidLoad() {
super.viewDidLoad()
self.authenticateLocalPlayer()
self.setupCarousel()
}
func setupCarousel() {
carousel = iCarousel()
carousel.dataSource = self
carousel.delegate = self
carousel.type = .Linear
carousel.reloadData()
let spriteKitView = SKView()
spriteKitView.frame = CGRectMake(0, 0, 250, 250)
// self.view.insertSubview(spriteKitView, belowSubview: self.carousel) // this is showing an empty gray box
self.view.addSubview(self.carousel)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.showCarousel), name: "showBallPicker", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.hideCarousel), name: "hideBallPicker", object: nil)
}
func carousel(carousel: iCarousel, valueForOption option: iCarouselOption, withDefault value: CGFloat) -> CGFloat{
if (option == .Spacing){
return value * 2
}
return value
}
func carousel(carousel: iCarousel, viewForItemAtIndex index: Int, reusingView view: UIView?) -> UIView {
var imageView: UIImageView!
if view == nil {
imageView = UIImageView(frame: CGRectMake(0, 0, 250, 250))
imageView.backgroundColor = UIColor.redColor()
imageView.contentMode = .ScaleAspectFill
}else{
imageView = view as! UIImageView
}
imageView.image = UIImage(named: "\(imageArray.objectAtIndex(index))")
return imageView
}
func numberOfItemsInCarousel(carousel: iCarousel) -> Int {
return imageArray.count
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
let skView = self.view as! SKView
if skView.scene == nil {
skView.showsFPS = true
skView.showsNodeCount = true
skView.showsPhysics = false
skView.multipleTouchEnabled = true
let menuScene = MenuScene(size: CGSizeMake(375,667))
menuScene.scaleMode = .AspectFill
menuScene.imageName = self.imageArray[0] as! String
self.hideCarousel()
skView.presentScene(menuScene)
}
}
override func viewWillDisappear(animated: Bool) {
if let skView = self.view as? SKView {
skView.presentScene(nil)
}
}
override func viewDidDisappear(animated: Bool) {
if let skView = self.view as? SKView {
skView.presentScene(nil)
}
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
And this is what i'm doing on MenuScene.swift
var childNode = SKSpriteNode()
var imageName = "white"{
didSet{
self.childNode.texture = SKTexture(imageNamed: imageName)
}
}
func showBallPicker(){
NSNotificationCenter.defaultCenter().postNotificationName("showBallPicker", object: nil)
}
override init(size: CGSize) {
// here im supposed to create a childNode SKSpriteNode, but for what? it only adds the same image of the carousel and that's it.
self.childNode = SKSpriteNode(imageNamed: imageName)
self.childNode.anchorPoint = CGPointZero
self.childNode.zPosition = 30
self.childNode.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
self.addChild(self.childNode)
}
And then, on touchesEnded i'm calling from a button touch self.showBallPicker() to display the carousel.
As I said before, it's adding the carousel, but not even working and on top-left corner.
How can I achieve this? Once I can make it display properly, I can handle the rest.
Thanks.
You're not setting the frame of the carousel or setting constraints on it.
Try changing your setupCarousel
method to:
func setupCarousel() {
carousel = iCarousel()
carousel.dataSource = self
carousel.delegate = self
carousel.type = .Linear
carousel.reloadData()
// turn off autoresizing mask
carousel.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(self.carousel)
// Add constraints
carousel.topAnchor.constraintEqualToAnchor(self.topLayoutGuide.bottomAnchor).active = true
carousel.leadingAnchor.constraintEqualToAnchor(self.view.leadingAnchor).active = true
carousel.trailingAnchor.constraintEqualToAnchor(self.view.trailingAnchor).active = true
carousel.bottomAnchor.constraintEqualToAnchor(self.view.bottomAnchor).active = true
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.showCarousel), name: "showBallPicker", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.hideCarousel), name: "hideBallPicker", object: nil)
}
These constraints will pin the carousel to the edges of your view controller's view. If you want to position it differently, you could, for example, replace those constraints with something like:
carousel.centerXAnchor.constraintEqualToAnchor(self.view.centerXAnchor).active = true
carousel.centerYAnchor.constraintEqualToAnchor(self.view.centerYAnchor, constant: -100).active = true
carousel.widthAnchor.constraintEqualToAnchor(self.view.widthAnchor).active = true
carousel.heightAnchor.constraintEqualToConstant(200.0).active = true
This will center the carousel horizontally and place the carousel 100 points up from the center vertically. The carousel will be the same width as it's superview but will always be 200 points tall. You can look at Apple's documentation for more options for using anchors to create constraints.
If you want to add labels to your carousel items you should do that in viewForItemAtIndex
. You can create a simple UIView
subclass that will hold a UIImageView
and UILabel
and return that instead of just a UIImageView
. For example:
class CarouselItem: UIView {
let imageView:UIImageView =
{
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .ScaleAspectFill
return imageView
}()
let label:UILabel =
{
let label = UILabel()
label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.5
label.translatesAutoresizingMaskIntoConstraints = false
label.textAlignment = .Center
label.numberOfLines = 0
return label
}()
override init(frame: CGRect)
{
super.init(frame: frame)
self.commonInit()
}
required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
self.commonInit()
}
func commonInit()
{
self.addSubview(self.imageView)
self.addSubview(self.label)
self.imageView.topAnchor.constraintEqualToAnchor(self.topAnchor).active = true
self.imageView.centerXAnchor.constraintEqualToAnchor(self.centerXAnchor).active = true
self.imageView.widthAnchor.constraintEqualToAnchor(self.widthAnchor).active = true
self.imageView.heightAnchor.constraintEqualToAnchor(self.heightAnchor, multiplier: 0.9).active = true
self.label.topAnchor.constraintEqualToAnchor(self.imageView.bottomAnchor, constant: 8.0).active = true
self.label.centerXAnchor.constraintEqualToAnchor(self.centerXAnchor).active = true
}
}
Now in viewForItemAtIndex
func carousel(carousel: iCarousel, viewForItemAtIndex index: Int, reusingView view: UIView?) -> UIView {
var carouselItem: CarouselItem!
if view == nil {
carouselItem = CarouselItem(frame: CGRectMake(0, 0, 250, 250))
carouselItem.backgroundColor = UIColor.redColor()
}else{
carouselItem = view as! CarouselItem
}
carouselItem.imageView.image = UIImage(named: "\(imageArray.objectAtIndex(index))")
carouselItem.label.text = "Some Text"
return carouselItem
}