msqar msqar - 4 months ago 29
iOS Question

iCarousel on Game Swift+SpriteKit not working properly

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.

Answer

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)
}

Update

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
    }