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

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)
    }
}
主站蜘蛛池模板: 城固县| 同心县| 庄河市| 疏附县| 贵州省| 合山市| 桐梓县| 利川市| 东山县| 竹山县| 时尚| 阿拉善左旗| 南宁市| 邛崃市| 固镇县| 容城县| 富裕县| 台中县| 汕头市| 安泽县| 历史| 永城市| 库尔勒市| 越西县| 河津市| 湘阴县| 乌拉特中旗| 肃南| 凌云县| 呼和浩特市| 禄丰县| 密山市| 莱芜市| 商水县| 攀枝花市| 汉中市| 蛟河市| 梅河口市| 双牌县| 象州县| 四川省|