【问题标题】:Disabling UICollectionViewCell on tap, and randomizing the cells在点击时禁用 UICollectionViewCell,并随机化单元格
【发布时间】:2016-08-19 03:16:36
【问题描述】:

我正在开发一个非常简单的快速游戏。游戏的主要布局是一个带有 16 个单元(门)的 UICollectionView。其中 1 个单元(门)是一个单元格上的游戏。您需要点击(打开)除了游戏结束单元格之外的所有门。如果在单元格上的游戏被点击,单元格将全部随机化,您将失去生命。如果你打开除了游戏结束单元之外的所有门,你就赢了。

我的代码如下。

什么有效?

  • 在初始加载时,门会通过一组 15 个 DoorIdentifier 和 1 个 GameOverIdentifier。我就是这样 检测单元格上的游戏。

什么不起作用?

  • 如果我点击一些普通门(禁用它们),然后点击游戏 越过门,它会左右移动门(应该如此),但随后 其他随机门被禁用并看起来启用(1.0 alpha)。不打算。

  • 如果我点击一些普通门(禁用它们),然后点击 Game Over 门,所有门都变为 1.0 alpha,只有一些门有 启用交互。此外,在按下 Game Over 门后, 有时只有一扇门会维持 0.2 alpha。

我需要什么? 当一个普通的门被敲击时,那个牢房现在总是会被禁用。如果游戏结束门被点击,它将忽略已经被点击的单元格,因为它们应该“不在游戏范围内”。

import UIKit

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {

@IBOutlet weak var mainCollectionView: UICollectionView!


var gameOverDoorArray = ["GameOverIdentifier"]
var normalDoors = ["DoorIdentifier", "DoorIdentifier", "DoorIdentifier", "DoorIdentifier", "DoorIdentifier", "DoorIdentifier", "DoorIdentifier", "DoorIdentifier", "DoorIdentifier", "DoorIdentifier", "DoorIdentifier", "DoorIdentifier", "DoorIdentifier", "DoorIdentifier", "DoorIdentifier"]
var reuseIdentifiers = [String]()
var livesLeftCounter = 3


@IBAction func randomizeButton(sender: UIButton) {
    randomizeReuseIdentifiers()
}

override func viewDidLoad() {
    super.viewDidLoad()
    mainCollectionView.dataSource = self
    mainCollectionView.delegate = self
    mainCollectionView.backgroundColor = UIColor.clearColor()

    reuseIdentifiers = gameOverDoorArray + normalDoors

    randomizeReuseIdentifiers()
}


func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return reuseIdentifiers.count
}


func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let identifier = reuseIdentifiers[indexPath.item]
    let cell: DoorCollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier(identifier, forIndexPath: indexPath) as! DoorCollectionViewCell

    cell.backgroundColor = UIColor(patternImage: UIImage(named: "doorClosedImage")!)

    return cell
}

func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {

    let currentCell = mainCollectionView.cellForItemAtIndexPath(indexPath) as UICollectionViewCell!



    // Game Over door was pressed!
    if reuseIdentifiers[indexPath.item] == "GameOverIdentifier" {
        gameOverDetection()
        randomizeReuseIdentifiers()
        mainCollectionView.reloadData()
    }

    // Normal door was pressed!
    else if reuseIdentifiers[indexPath.item] == "DoorIdentifier"{
        if currentCell.userInteractionEnabled && (normalDoors.count > 0){
            currentCell.alpha = 0.2
            print("reuse ID's array - \(reuseIdentifiers)")
            // when all the above is done, the button gets disabled.
            currentCell.userInteractionEnabled = false
            // Remove last item in normalDoors Array.
            normalDoors.removeLast()
        }

        print("Array count of allTheDoors - \(reuseIdentifiers.count).")
    }



}


func randomizeReuseIdentifiers() {
    var randomized = [String]()

    for _ in reuseIdentifiers {
        let random = reuseIdentifiers.removeAtIndex(Int(arc4random_uniform(UInt32(reuseIdentifiers.count))))
        randomized.append(random)
    }

    reuseIdentifiers = randomized

}

func gameOverDetection() {
    livesLeftCounter -= 1
    if livesLeftCounter < 0 {
        print("GAME OVER!")
    }
    print(livesLeftCounter)
}

}

【问题讨论】:

  • 我不了解您游戏的所有细节,但我查看了代码并发现了一些可能导致这些问题的缺陷。请注意,您使用单元格本身来存储有关此门是否启用的信息。它不会工作。您应该创建一些属性(可能是数组)并将此信息存储在其中,而不是在单元格中。在cellForItemAtIndexPath 中,您需要根据该数组中的信息更新单元格的状态。
  • 通过保持重用标识符数组存储游戏结束单元格的信息看起来很奇怪。你只需要 1 个号码。

标签: ios swift random uicollectionview cell


【解决方案1】:

使用UICollectionView 时(以及一般使用MVC 时)最好将您将使用的所有数据与视图分开存储。每个门都有一些游戏状态属性,您当前正尝试使用 UICollectionViewCell 上的属性来存储这些属性。这是不正确的。您应该创建一个模型(我们称之为Door),并使用此模型上的属性来存储您的游戏状态。

首先,创建一个带有嵌套枚举的类来存储门的类型。

class Door {
    enum Type {
        case Normal
        case GameOver
    }

    var type: Type

    init(type: Type) {
        self.type = type
    }
}

然后,您需要存储每个Door 的打开/关闭状态:

class Door {
    enum Type {
        case Normal
        case GameOver
    }

    var type: Type

    var closed = true

    init(type: Type) {
        self.type = type
    }
}

最后,您应该编写一个方法或计算属性来为每种 Door 返回适当的重用标识符。

class Door {
    enum Type {
        case Normal
        case GameOver
    }

    var type: Type

    var closed = true

    var reuseIdentifier: String {
        switch type {
        case .Normal:
            return "DoorIdentifier"
        case .GameOver:
            return "GameOverIdentifier"
        default:
            return ""
    }

    init(type: Type) {
        self.type = type
    }
}

然后,您将初始化模型对象数组,而不是初始化 reuseIdentifiers 数组。

var doors = [Door]()

override func viewDidLoad() {
    super.viewDidLoad()

    // Your other initialization in viewDidLoad here...

    doors = [Door(.Normal), Door(.Normal), /* etc... */ Door(.GameOver)]
}

然后您需要重写 randomizeReuseIdentifiers 以改为随机化 doors

func randomizeDoors() {
    var randomized = [Door]()

    for _ in doors {
        let random = doors.removeAtIndex(Int(arc4random_uniform(UInt32(doors.count))))
        randomized.append(random)
    }

    doors = randomized
}

最后,您可以在 UICollectionViewDataSourceUICollectionViewDelegate 方法中使用这个新的数据模型:

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return reuseIdentifiers.count
}

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let door = doors[indexPath.item]
    let identifier = door.reuseIdentifier
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier(identifier, forIndexPath: indexPath) as! DoorCollectionViewCell

    // Here, use the closed property to set interaction enabled and alpha.
    if door.closed {
        cell.userInteractionEnabled = true
        cell.alpha = 1.0
    } else {
        cell.userInteractionEnabled = false
        cell.alpha = 0.2    
    }

    cell.backgroundColor = UIColor(patternImage: UIImage(named: "doorClosedImage")!)
    return cell
}     

func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {

    let currentCell = mainCollectionView.cellForItemAtIndexPath(indexPath) as UICollectionViewCell!
    let door = doors[indexPath.item]

    switch door.type {
    case .Normal:
        // Set the door to opened, so it is disabled on the next reload.
        door.closed = false
    case .GameOver:
        gameOverDetection()
        randomizeDoors()            
    }

    mainCollectionView.reloadData()
}

总的来说,我认为在您继续学习的过程中,阅读更多有关模型视图控制器 (MVC) 模式的信息会对您有所帮助。 Apple 有 some documentation that might be helpful,您可能会发现 these resources 也是说明性的。

【讨论】:

  • 感谢您的帮助。但是,在您的代码中出现一些错误。 var closed = true "枚举可能不包含存储的属性" / var reuseIdentifier "计算的属性必须有一个明确的类型" / random.append(random) "不能将“ViewController.Door”类型的值转换为预期的参数类型“字符串”/门 = 随机“无法将类型 '[String]' 的值分配给类型 '[ViewController.Door]'”
  • 修复了这些问题。抱歉 - 在发布之前没有运行此代码。
  • 你摇滚!我得到了这个工作。您的代码中有几个小问题,但我已经解决了。 Door 类中的重用标识符缺少右括号/门数组需要类型:- Door(type: .Normal) / currentCell 不需要在 didSelectItemAtIndexPath 中声明。还有一个问题.. 如果“游戏结束”门被点击,它会打乱整个收藏视图。是否可以只洗牌所有关闭的门,以便打开(alpha 1.0 / 启用)的门不会移动?
  • 查看filter method on SequenceType(Array 实现的)。作为提示:doors.filter({ $0.closed })
  • 谢谢,但是对于我目前的技能来说,这种语法太高级了。将保存以供将来参考。
猜你喜欢
  • 2011-07-08
  • 2015-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-14
  • 2011-04-04
  • 2014-11-11
相关资源
最近更新 更多