在swift中为Shape指定引用

时间:2017-05-19 14:16:13

标签: ios swift cashapelayer

刚刚开始使用Swift(和一般大约一周前的OOO)所以请耐心等待。

按照这个“教程”(https://www.youtube.com/watch?v=bwDJspl5sbg),我能够得到以下代码,这些代码绘制了一个圆圈网格。

 //get the bounds of the view we're in
    let viewWidth = self.view.bounds.width
    let viewHeight = self.view.bounds.height

    //set parameters for gride of circles
    let margin: CGFloat = 25
    let xMargin = margin
    let yMargin = margin
    let circleBreathingSpace: CGFloat = 5
    let circleLineWidth: CGFloat = 4

    let across: CGFloat = 6
    let gridRowWidth: CGFloat = viewWidth - (2.0 * margin)
    let cellWidth: CGFloat = gridRowWidth / across
    let cellHeight: CGFloat = cellWidth
    let down: CGFloat = (viewHeight - (2 * yMargin)) / cellHeight

    let circleRadius: CGFloat = (cellWidth - (2 * circleBreathingSpace)) / 2


    //draw the grid
    for xx in 0..<Int(across){
        for yy in 0..<Int(down){
            let circleX: CGFloat = xMargin + ((CGFloat(xx) + 0.5) * cellWidth)
            let circleY: CGFloat = yMargin + ((CGFloat(yy) + 0.5) * cellHeight)

            //Draw Circle [ref: http://stackoverflow.com/questions/29616992/how-do-i-draw-a-circle-in-ios-swift ]
            let circlePath = UIBezierPath(arcCenter: CGPoint(x: circleX,y: circleY),
                                          radius: circleRadius, startAngle: CGFloat(0),
                                          endAngle:CGFloat(M_PI * 2),
                                          clockwise: true)


            let shapeLayer = CAShapeLayer()
            shapeLayer.path = circlePath.cgPath

            //change the fill color
            shapeLayer.fillColor = UIColor.blue.cgColor
            //you can change the stroke color
            shapeLayer.strokeColor = UIColor.black.cgColor
            //you can change the line width
            shapeLayer.lineWidth = 6.0

            view.layer.addSublayer(shapeLayer)




        }
    }

问题 现在我想知道的是我如何能够为这些个体中的每个圆圈提供一个“参考”,以便稍后操纵它们的属性。

上下文
我基本上想要根据时间的输入让这些圆圈的网格慢慢消失。因此,如果我有20分钟作为输入,我将获得20个圆圈,每个将花费60秒消失。这只是我正在做的事情的背景,不一定是问题本身(尽管该领域的任何方向都不会伤害:))

我的想法 我的想法可能是创建一个数组并将当前正在创建的圆添加到此数组中。然后我可以循环遍历数组,然后慢慢减小每个圆圈的大小,使它们缩小并逐个消失。即使这是一个可行的解决方案,我也不太清楚如何去做。

提前致谢!

2 个答案:

答案 0 :(得分:0)

绘图和以后更改图层的说明如下:https://www.raywenderlich.com/90488/calayer-in-ios-with-swift-10-examples 在您的情况下,您可以将图层放在一个数组中,然后按照您在想法中提到的那样更改它们。

或者,您可以尝试使用CABasicAnimation https://developer.apple.com/reference/quartzcore/cabasicanimation并将动画属性设置为半径。

答案 1 :(得分:0)

是的,您可以为这些图层创建一个数组:

var shapeLayers = [CAShapeLayer]()

然后,在创建形状图层时,可以add将它们添加到该数组中。

或者,您可能希望使用数组数组(实际上是二维数组),以便更准确地镜像可视化表示。你可以做类似的事情,创建一个数组并为每一行的数组添加形状,然后创建另一个数组来保存形状图层行的那些单独的数组。但也许更优雅,您可以使用map调用来构建CAShapeLayer个对象数组的数组。

基本思想是你采用一系列整数来构建数组。让我们考虑一个简单的例子,我想要一个10 Int个对象的简单数组,0,2,4 ...... 18:

let array = (0 ..< 10).map { (i) -> Int in
    return i * 2
}

但这也适用于构建CAShapeLayer个对象的数组:

let shapeArray = (0 ..< 10).map { (xx) -> CAShapeLayer in
    let shapeLayer = CAShapeLayer()
    // ... configure that shapeLayer here
    return shapeLayer
}

这样就构建了一个&#34;行&#34; CAShapeLayer个对象,xx从0到10进展。

然后,您也可以将其嵌套在map内的所有行中。因此内部map返回CAShapeLayer个对象的数组,但外部map返回这些数组的数组,最后返回[[CAShapeLayer]]

let arrayOfArrays = (0 ..< 20).map { (yy) -> [CAShapeLayer] in
    return (0 ..< 10).map { (xx) -> CAShapeLayer in
        let shapeLayer = CAShapeLayer()
        // ... configure that shapeLayer here
        return shapeLayer
    }
}

然后你可以迭代这样:

for shapeLayers in arrayOfArrays {
    for shapeLayer in shapeLayers {
        // do something with `shapeLayer`, e.g.

        view.layer.addSublayer(shapeLayer)
    }
}

所以,回到你的例子:

let viewWidth = view.bounds.width
let viewHeight = view.bounds.height

//set parameters for gride of circles
let margin: CGFloat = 25
let xMargin = margin
let yMargin = margin
let circleBreathingSpace: CGFloat = 5
let circleLineWidth: CGFloat = 6

let across: CGFloat = 6
let gridRowWidth: CGFloat = viewWidth - (2.0 * margin)
let cellWidth: CGFloat = gridRowWidth / across
let cellHeight: CGFloat = cellWidth
let down: CGFloat = (viewHeight - (2 * yMargin)) / cellHeight

let circleRadius: CGFloat = (cellWidth - (2 * circleBreathingSpace)) / 2

//build the grid
let circles = (0 ..< Int(down)).map { (yy) -> [CAShapeLayer] in
    return (0 ..< Int(across)).map { (xx) -> CAShapeLayer in
        let circleX: CGFloat = xMargin + ((CGFloat(xx) + 0.5) * cellWidth)
        let circleY: CGFloat = yMargin + ((CGFloat(yy) + 0.5) * cellHeight)

        let path = UIBezierPath(arcCenter: CGPoint(x: circleX, y: circleY),
                                      radius: circleRadius, startAngle: 0,
                                      endAngle: .pi * 2,
                                      clockwise: true)

        let shapeLayer = CAShapeLayer()
        shapeLayer.path = path.cgPath

        shapeLayer.fillColor = UIColor.blue.cgColor
        shapeLayer.strokeColor = UIColor.black.cgColor
        shapeLayer.lineWidth = circleLineWidth

        return shapeLayer
    }
}

for row in circles {
    for shapeLayer in row {
        view.layer.addSublayer(shapeLayer)
    }
}

顺便说一句,如果你希望它们随着时间的推移而消失,你可能会考虑重复计时器。并且我将它们设置为clear颜色的动画:

var row = 0
var col = 0
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in
    let shapeLayer = circles[col][row]

    let animation = CABasicAnimation(keyPath: "opacity")
    animation.fromValue = 1
    animation.toValue = 0
    animation.duration = 2
    shapeLayer.opacity = 0

    shapeLayer.add(animation, forKey: nil)

    row += 1
    if row >= circles[0].count {
        row = 0
        col += 1
        if col >= circles.count {
            timer.invalidate()
        }
    }
}

产量:

fade

注意,使用嵌套的for循环模式可能很有吸引力(就像我们在将图层层次添加到图层层次结构时所做的那样),为每个要删除的圆创建一个单独的计时器,但这引入了两个问题:

首先,如果你必须取消所有这些,你真的不想迭代计时器阵列以取消它们。能够invalidate单个重复计时器会更好。其次,未来安排的多个计时器的发射可以通过计时器合并&#34; (其中,为了节省电力,操作系统将开始分组远程计时器)。是的,您可以使用GCD计时器并关闭计时器合并,但重复计时器可以完全解决问题。

我注意到你想让圆圈缩小,而不是像我上面那样褪色。所以你可以做到以下几点:

let viewWidth = view.bounds.width
let viewHeight = view.bounds.height

//set parameters for gride of circles
let margin: CGFloat = 25
let xMargin = margin
let yMargin = margin
let circleBreathingSpace: CGFloat = 5
let circleLineWidth: CGFloat = 6

let across: CGFloat = 6
let gridRowWidth: CGFloat = viewWidth - (2.0 * margin)
let cellWidth: CGFloat = gridRowWidth / across
let cellHeight: CGFloat = cellWidth
let down: CGFloat = (viewHeight - (2 * yMargin)) / cellHeight

let circleRadius: CGFloat = (cellWidth - (2 * circleBreathingSpace)) / 2

//build the grid
let circles = (0 ..< Int(down)).map { (yy) -> [CAShapeLayer] in
    (0 ..< Int(across)).map { (xx) -> CAShapeLayer in
        let circleX: CGFloat = xMargin + ((CGFloat(xx) + 0.5) * cellWidth)
        let circleY: CGFloat = yMargin + ((CGFloat(yy) + 0.5) * cellHeight)

        let path = UIBezierPath(arcCenter: .zero,
                                radius: circleRadius, startAngle: 0,
                                endAngle: .pi * 2,
                                clockwise: true)

        let shapeLayer = CAShapeLayer()
        shapeLayer.position = CGPoint(x: circleX, y: circleY)
        shapeLayer.path = path.cgPath

        shapeLayer.fillColor = UIColor.blue.cgColor
        shapeLayer.strokeColor = UIColor.black.cgColor
        shapeLayer.lineWidth = circleLineWidth

        return shapeLayer
    }
}

//add grid to view's layer
for row in circles {
    for shapeLayer in row {
        view.layer.addSublayer(shapeLayer)
    }
}

注意,我调整了一下,以便路径大约是.zero,但是将图层的position移动到我想要圆圈的位置。这样,当我希望它们消失时,我可以简单地调整图层transform的比例:

var row = 0
var col = 0

Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in
    let shapeLayer = circles[col][row]

    let transformAnimation = CABasicAnimation(keyPath: "transform")

    let transform = CATransform3DMakeScale(0, 0, 1)
    transformAnimation.fromValue = shapeLayer.transform
    transformAnimation.toValue = transform
    transformAnimation.duration = 2
    shapeLayer.transform = transform

    shapeLayer.add(transformAnimation, forKey: nil)

    row += 1
    if row >= circles[0].count {
        row = 0
        col += 1
        if col >= circles.count {
            timer.invalidate()
        }
    }
}

产量:

shrinking