官术网_书友最值得收藏!

The game screen

Before implementing the game, let's proceed to build the layout of the cards on the table.

The structure

Now let's implement a new class called MemoryViewController, which extends the UIVewController class. This will be used to manage the actual view where the Memory Game will be played. The first thing we do is add the class life cycle functions:

class MemoryViewController: UIViewController {
    private let difficulty: Difficulty

    init(difficulty: Difficulty) {
        self.difficulty = difficulty
        super.init(nibName: nil, bundle: nil)
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    deinit{
        print("deinit")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        setup()
    }
}
// MARK: Setup
private extension MemoryViewController {
    func setup() {
        view.backgroundColor = .greenSea()
    }
}

Besides the initializer that accepts the chosen difficulty, although it's not used, we need to add the required initializer with NSCoder. Moreover, you should note that we need to call the parent initializer with nibName and the bundle, used when UIViewController is built from an XIB file. If we call a plain super.init() function, we will receive a runtime error because the empty one is a convenience initializer, an initializer that calls a required initializer in the same class that, in our case, is not implemented.

Although not mandatory, we have implemented the deinitializer as well, inserting just a debug log statement to verify that the class is correctly removed from the memory when dismissed. Thus, a retain cycle is avoided.

Finally, we come to this comment:

// MARK: Setup

This is a special comment that tells Xcode to present the sentence in the structure of a class in order to facilitate navigation to a different part of the class.

The last element of the status bar of the code editor of Xcode must be selected.

After this, a menu with all the functions appears, with a bold entry where we put the //MARK: comment.

Adding a collection view

Let's move on to implementing the layout of the card. We'll use UICollectionView to lay the cards on the table. UICollectionView is a view that arranges the contained cells to follow a layout we set during the setup. In this case, we set a flow layout in which each card follows the previous one, and it creates a new row when the right border is reached.

We set the properties for the view and a model to fulfill the collection view:

private var collectionView: UICollectionView!
private var deck: Array<Int>!

Next, we write the function calls to set up everything in viewDidLoad so that the functions are called when the view is loaded:

override func viewDidLoad() {
    super.viewDidLoad()
    setup()
}

The setup() function basically creates and configures CollectionView:

// MARK: Setup
private extension MemoryViewController {
    func setup() {
        view.backgroundColor = .greenSea()

        let space: CGFloat = 5
        let (covWidth, covHeight) = collectionViewSizeDifficulty(difficulty, space: space)
        let layout = layoutCardSize(cardSizeDifficulty(difficulty, space: space), space: space)

        collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: covWidth, height: covHeight),collectionViewLayout: layout)
        collectionView.center = view.center
        collectionView.dataSource = self
        collectionView.delegate = self
        collectionView.scrollEnabled = false
        collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "cardCell")
        collectionView.backgroundColor = .clearColor()

        self.view.addSubview(collectionView)
    }

After setting the color of the collectionview, we define a constant, space, to set the space between every two cards.

Next, we calculate the size of the collectionview given the difficulty, and hence, the number of rows and columns; then, the layout. Finally, we put everything together to build the collectionview:

    func collectionViewSizeDifficulty(difficulty: Difficulty, space: CGFloat) -> (CGFloat, CGFloat) {
        let (columns, rows) = sizeDifficulty(difficulty)
        let (cardWidth, cardHeight) = cardSizeDifficulty(difficulty, space: space)

        let covWidth = columns*(cardWidth + 2*space)
        let covHeight = rows*(cardHeight + space)
        return (covWidth, covHeight)
    }

The cardSizeDifficulty() function calculates the size of the collection view by multiplying the size of each card by the number of rows or columns:

    func cardSizeDifficulty(difficulty: Difficulty, space: CGFloat) -> (CGFloat, CGFloat) {
        let ratio: CGFloat = 1.452

        let (_, rows) = sizeDifficulty(difficulty)
        let cardHeight: CGFloat = view.frame.height/rows - 2*space
        let cardWidth: CGFloat = cardHeight/ratio
        return (cardWidth, cardHeight)
    }

The sizeDifficulty()function will be introduced later; just to make it buildable, let's implement it with only one hardcoded value:

    func sizeDifficulty(difficulty: Difficulty) -> (CGFloat, CGFloat) {
        return (4,3)
    }

Because the column value returned by the sizeDifficulty()function is not used anywhere, we can safely associate it with the wildcard keyword _.

Sizing the components

As mentioned at the start of this chapter, we are not using Auto Layout, but we need to handle the issue of different screen sizes somehow. Hence, using basic math, we adapt the size of each card to the available size on the screen:

    func layoutCardSize(cardSize: (cardWidth: CGFloat, cardHeight: CGFloat), space: CGFloat) -> UICollectionViewLayout {
        let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
        layout.sectionInset = UIEdgeInsets(top: space, left: space, bottom: space, right: space)
        layout.itemSize = CGSize(width: cardSize.cardWidth, height: cardSize.cardHeight)
        layout.minimumLineSpacing = space
        return layout
    }

As mentioned earlier, the UICollectionView class shows a series of cells in its content view, but the way in which the cells are presented—as a grid or a vertical pile—the space between them is defined by an instance of UICollectionViewFlowLayout.

Finally, we set up the layout, defining the size of each cell and how they are separated and laid out.

We have seen that there is a connection between the difficulty setting and the size of the grid of the cards, and this relation is implemented simply using switch statements:

// MARK: Difficulty
private extension MemoryViewController {
    func sizeDifficulty(difficulty: Difficulty) -> (CGFloat, CGFloat) {
        switch difficulty {
            case .Easy:
                return (4,3)
            case .Medium:
                return (6,4)
            case .Hard:
                return (8,4)
        }
    }

    func numCardsNeededDifficulty(difficulty: Difficulty) -> Int {
        let (columns, rows) = sizeDifficulty(difficulty)
        return Int(columns * rows)
    }
}
主站蜘蛛池模板: 精河县| 黎平县| 特克斯县| 喀喇| 磐安县| 邵武市| 保康县| 通江县| 会东县| 宣汉县| 威海市| 鱼台县| 肥西县| 平阳县| 涿鹿县| 新营市| 旺苍县| 潼南县| 灵璧县| 沙田区| 自贡市| 西畴县| 鱼台县| 贡觉县| 繁昌县| 凤台县| 河池市| 镶黄旗| 临桂县| 扎兰屯市| 临沧市| 隆德县| 建湖县| 永登县| 静乐县| 万载县| 贵州省| 金秀| 苗栗县| 灵寿县| 贵德县|