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

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)
    }
}
主站蜘蛛池模板: 图木舒克市| 喀喇沁旗| 武山县| 邹城市| 乳源| 大方县| 漠河县| 通道| 德清县| 安阳县| 班戈县| 长子县| 宁都县| 万年县| 江永县| 聂荣县| 紫阳县| 南郑县| 安吉县| 拜泉县| 湘潭市| 崇州市| 当阳市| 东海县| 襄汾县| 右玉县| 尼勒克县| 鲜城| 读书| 湘西| 富顺县| 林州市| 茌平县| 茶陵县| 巴林右旗| 盐山县| 神木县| 吉水县| 库车县| 乐昌市| 赤峰市|