按下按钮以使用数组中的值更改 CGRect 的颜色。如何做到这一点?

Press down on button to change the color of CGRect using a value in an array. How can this be accomplished?

你好 Stack Overflow 社区,

我正在使用 Swift 3.0 在 Xcode 8.0 中开发一个项目。我正在尝试开发的应用程序是我 class' 最终项目的 Magic the Gathering 生命计数器。第一张图片是当用户从应用程序的主屏幕导航到生命计数器屏幕时出现在屏幕上的图片。

这是 a link 一张图片,展示了用户从应用程序的主屏幕导航到此视图时所看到的内容。

您会在照片中看到名为 lifeCounterViewController 的视图控制器,并且在该视图控制器内部是另外两个视图。这些视图分别命名为 player1View 和 player2View。

在每个视图中都绘制了一个矩形。它是 CGRect class 的成员。以下代码来自 cocoa touch swift 文件。它包含我重写绘图函数的代码。

//
//  player1LifeCounterDrawing.swift

import UIKit

class player1LifeCounterDrawing: UIView {

    let MagicRed = UIColor.init(red: 247.0/255.0, green: 170.0/255.0, blue: 146.0/255.0, alpha: 1.0)
    let Purple = UIColor.init(red: 102.0/255.0, green: 0.0/255.0, blue: 204.0/255.0, alpha: 1.0)
    let Magenta = UIColor.init(red: 102.0/255.0, green: 0.0/255.0, blue: 51.0/255.0, alpha: 1.0)
    let Orange = UIColor.init(red: 255.0/255.0, green: 166.0/255.0, blue: 0.0/255.0, alpha: 1.0)
    let Plum = UIColor.init(red: 179.0/255.0, green: 0.0/255.0, blue: 89.0/255.0, alpha: 1.0)

    let Emerald = UIColor.init(red: 0.0/255.0, green: 204.0/255.0, blue: 102.0/255.0, alpha: 1.0)
    let Teal = UIColor.init(red: 0.0/255.0, green: 153.0/255.0, blue: 153.0/255.0, alpha: 1.0)

    let SlateBlue = UIColor.init(red: 0.0/255.0, green: 153.0/255.0, blue: 255.0/255.0, alpha: 1.0)
    let Forest = UIColor.init(red: 51.0/255.0, green: 153.0/255.0, blue: 51.0/255.0, alpha: 1.0)
    let NavyBlue = UIColor.init(red: 51.0/255.0, green: 102.0/255.0, blue: 153.0/255.0, alpha: 1.0)
    let MagicBlue = UIColor.init(red: 157.0/255.0, green: 216.0/255.0, blue: 255.0/255.0, alpha: 1.0)
    let MagicBlack = UIColor.init(red: 194.0/255.0, green: 179.0/255.0, blue: 180.0/255.0, alpha: 1.0)
    let MagicGreen = UIColor.init(red: 141.0/255.0, green: 203.0/255.0, blue: 158.0/255.0, alpha: 1.0)
    var colorLibrary = [UIColor]()
    var index = 0
    func fillLibrary(library: [UIColor]) -> [UIColor]{
        let capture = [UIColor](arrayLiteral: MagicRed, MagicBlue, MagicGreen, MagicBlack, Purple, Magenta, Orange, Plum, Emerald, Teal, SlateBlue, Forest, NavyBlue )
        return capture
    }
    func calling(){
        colorLibrary = fillLibrary(library: colorLibrary)
    }

    override func draw(_ rect: CGRect){
        calling()
        let lineWidth = CGFloat(3.0)
        let rectangle = CGRect(
            origin: CGPoint(x: 0, y: 0),
            size: CGSize(width: bounds.maxX, height: bounds.maxY)
        )
        let rectPath = UIBezierPath(
            roundedRect: rectangle,
            byRoundingCorners: UIRectCorner(rawValue: 0),
            cornerRadii: CGSize(width: 0, height: 0)
        )
        rectPath.lineWidth = lineWidth
        rectPath.lineCapStyle = CGLineCap.square
        if index >= colorLibrary.count{
            index = 0
        }
        colorLibrary[index].setFill()
        colorLibrary[index].setStroke()

        index += 1

        rectPath.stroke()
        rectPath.fill()
    }

}

现在这个解决方案可行,但它很丑。正如您所看到的,每当调用 draw 函数时,名为 colorLibrary 的数组都会被重新初始化。

我担心这是对内存的大量浪费,我需要帮助找到解决方案,以便每当用户按下按钮时调用 setNeedsDisplay() 以及 setStroke() 和 setFill() 方法更改颜色以反映 [index].

处的 colorLibrary 中表示的颜色

如何实现?

另请记住,建议的解决方案需要适用于 player1View 和 player2View。我只显示了 player1View 中的代码,但它在 player2View 中是相同的,只是标识哪个视图发生了变化的数字。

同样,在我看来,这是对内存的巨大浪费,我希望在星期二之前解决这个问题,因为那时我需要向我的 class 提交我的申请。如果到那时我还不能得到一个可行的解决方案,我将继续使用当前的解决方案。

您已经设置了所有内容,颜色数组的实例变量等,但是您在每次绘制循环时都重新创建此数组,除了第一个可能不需要。

考虑在 draw(_:) 开始时更改对 calling() 函数的调用,以便您可以重复使用之前的颜色数组。这不会节省任何内存,但会减少不必要和重复分配的次数,显着提高效率。最佳

if colorLibrary.count == 0 {
calling()
}

更改绘制代码中的索引将导致每次绘制时增加(设备旋转 -> 重绘,转到后台并再次打开 -> 重绘,...)。但是您只想在按下按钮时执行此操作。

我的建议是将颜色和索引功能提取到专门的 class 中(因为它是独立于 ui 的逻辑,视图应该只绘制一些东西而不是更多)。您还可以使用延迟加载来避免创建过多的对象。

例如:

final class ColorLibrary {

    // increase index logic (including overflow handling)
    private var currentIndex: Int = 0
    func increaseIndex() {
        if currentIndex == colors.count - 1 {
            currentIndex = 0
        } else {
            currentIndex += 1
        }
    }

    // lazy loading: color array will be created only once and only if needed
    private var _colors: [UIColor]?
    private var colors: [UIColor] {
        if _colors == nil {
            _colors = [
                UIColor.init(red: 247.0/255.0, green: 170.0/255.0, blue: 146.0/255.0, alpha: 1.0),
                UIColor.init(red: 102.0/255.0, green: 0.0/255.0, blue: 204.0/255.0, alpha: 1.0),
                UIColor.init(red: 102.0/255.0, green: 0.0/255.0, blue: 51.0/255.0, alpha: 1.0),
                UIColor.init(red: 255.0/255.0, green: 166.0/255.0, blue: 0.0/255.0, alpha: 1.0),
                UIColor.init(red: 179.0/255.0, green: 0.0/255.0, blue: 89.0/255.0, alpha: 1.0),
                UIColor.init(red: 0.0/255.0, green: 204.0/255.0, blue: 102.0/255.0, alpha: 1.0),
                UIColor.init(red: 0.0/255.0, green: 153.0/255.0, blue: 153.0/255.0, alpha: 1.0),
                UIColor.init(red: 0.0/255.0, green: 153.0/255.0, blue: 255.0/255.0, alpha: 1.0),
                UIColor.init(red: 51.0/255.0, green: 153.0/255.0, blue: 51.0/255.0, alpha: 1.0),
                UIColor.init(red: 51.0/255.0, green: 102.0/255.0, blue: 153.0/255.0, alpha: 1.0),
                UIColor.init(red: 157.0/255.0, green: 216.0/255.0, blue: 255.0/255.0, alpha: 1.0),
                UIColor.init(red: 194.0/255.0, green: 179.0/255.0, blue: 180.0/255.0, alpha: 1.0),
                UIColor.init(red: 141.0/255.0, green: 203.0/255.0, blue: 158.0/255.0, alpha: 1.0)
            ]
        }
        return _colors!
    }

    var currentColor: UIColor {
        get { return colors[currentIndex] }
    }

}

您现在可以通过以下方式在您的视图中构建对象:

    private(set) var colorLibrary: ColorLibrary = ColorLibrary()

访问绘图中的颜色:

    colorLibrary.currentColor.setFill()

按下按钮,您现在可以呼叫:

    cardView.colorLibrary.increaseIndex()
    cardView.setNeedsLayout()

Swift 支持 延迟初始化 来避免您担心的重复。您可以将 colorLibrary 替换为:

lazy var colorLibrary: [UIColor] = [self.MagicRed, self.MagicBlue, self.MagicGreen,
                                   self.MagicBlack, self.Purple, self.Magenta,
                                   self.Orange, self.Plum, self.Emerald, self.Teal,
                                   self.SlateBlue, self.Forest, self.NavyBlue]

请注意,这使用了数组文字 ([ ... ])。

现在变量 colorLibrary 将在首次使用时初始化并保留其值。

(准确地说,变量 可能 被初始化多次,特别是在多线程场景中,但这不需要您担心。)

另外请注意,您使用如下代码管理索引:

  if index >= colorLibrary.count{
     index = 0
  }
  // use index...      
  index += 1

像这样管理索引的常用方法是使用modular算法;那是环绕的算术,将最大值加一得到最小值。 (对于一个真实世界的例子,考虑一个里程表或电表上的计数器,在达到所有 9 之后这些循环到所有 0。)

可以使用 mod 运算符 % 执行模运算,为此您可以将其视为返回除法的 余数 。上面的代码大纲变为:

  // use index...      
  index = (index + 1) % colorLibrary.count

并且避免了if

HTH