在视图的每一侧对齐四个 CAEmitterLayer(),指向内部

Aligning four CAEmitterLayer() on each side of view, pointing inward

我正在努力将 4 CAEmitterLayers 添加到子视图。每个 CAEmitterLayer 是一条线,将被定位在每一边。旋转将通过 CGAffineTransform

控制

我的问题:

我无法让 .Left 和 .Right emitterPosition 与视图的左侧和右侧正确对齐

这是我的进度:

override func awakeFromNib() {
    super.awakeFromNib()
    self.layer.cornerRadius = GlobalConstants.smallCornerRadius
    createParticles(direction: .Up)
    createParticles(direction: .Down)
    createParticles(direction: .Left)
    createParticles(direction: .Right)
}

enum EmitTo {
    case Up
    case Down
    case Left
    case Right
}

func createParticles(direction: EmitTo) {
    self.layoutSubviews()
    let particleEmitter = CAEmitterLayer()
    particleEmitter.position = CGPoint(x: self.bounds.midX, y: self.bounds.midY)
    particleEmitter.bounds = CGRect(x: 0, y: 0, width: self.bounds.size.width, height: self.bounds.size.height)
    
    
    if direction == .Up {
        particleEmitter.setAffineTransform(CGAffineTransform(rotationAngle: 0))
    } else if (direction == .Down) {
        particleEmitter.setAffineTransform(CGAffineTransform(rotationAngle: .pi))
    } else if (direction == .Left) {
        particleEmitter.setAffineTransform(CGAffineTransform(rotationAngle: .pi / 2))
    } else if (direction == .Right) {
        particleEmitter.setAffineTransform(CGAffineTransform(rotationAngle: -.pi / 2))
    }
    
    if direction == .Up || direction == .Down {
        particleEmitter.emitterPosition = CGPoint(x: self.frame.width / 2, y: 0)
    }
    if direction == .Left {
        particleEmitter.emitterPosition = CGPoint(x: self.frame.height / 2, y: self.frame.height)
    }
    if direction == .Right {
        particleEmitter.emitterPosition = CGPoint(x: self.frame.height / 2, y: -50)
    }
    
    
    particleEmitter.emitterSize = self.bounds.size
    particleEmitter.emitterShape = CAEmitterLayerEmitterShape.line
    particleEmitter.zPosition = 1
    
    let triangle = makeEmitterCell(direction: direction)
    particleEmitter.emitterCells = [triangle]

    self.layer.addSublayer(particleEmitter)
}

func makeEmitterCell(direction: EmitTo) -> CAEmitterCell {
    let cell = CAEmitterCell()
    cell.birthRate = 25
    cell.lifetime = 3
    cell.lifetimeRange = 0
    cell.velocity = 10
    cell.velocityRange = 5
    cell.emissionLongitude = .pi
    
    cell.spin = 2
    cell.spinRange = 3
    cell.scale = 0.4
    cell.scaleRange = 0.6
    cell.scaleSpeed = -0.10

    cell.contents = UIImage(named: "greenTriangle")?.cgImage
    return cell
}

发生了什么:

我的问题

如何让 .Left.Right CAEmitterLayer 与灰色视图的左侧和右侧正确对齐?

绿色三角形图像:

您尝试使用 CGAffineTransform 的方法很有前途,因为线发射器是一条只有宽度尺寸的水平线。通过旋转它,你可以让它从左边或右边发射。

但是您需要使用发射器边界,视图高度作为宽度,视图宽度作为高度。

particleEmitter.bounds = CGRect(x: 0, y: 0, width: bounds.size.height, height: bounds.size.width)

相应地,将 emitterSize 的宽度设置为视图的高度:

particleEmitter.emitterSize = CGSize(width: bounds.size.height, height: 0)

必须以同样的方式调整发射器位置。

为什么会这样?看看下面的插图,上面有一个发射:

如果将图层旋转 90 度,您只会覆盖中间的一小块区域。排放物也将不可见,因为它们在左侧太远了。

发射器在左边或右边的区别只是用 -.pi / 2 .pi / 2 调用 setAffineTransform

顶部和底部的发射器当然应该有正常的边界,它们只需要像你在问题中那样旋转。

提示

在您的 createParticles 方法中调用 self.layoutSubviews()。应该避免这种情况。

Apple 文档明确指出:

You should not call this method directly.

参见 https://developer.apple.com/documentation/uikit/uiview/1622482-layoutsubviews

相反,您可以覆盖 layoutSubviews 并从那里调用您的 createParticles 方法。确保移除之前创建的发射器层。

一件小事:Swift switch cases 按照惯例以小写字母开头。

代码

帮助您入门的完整示例可能如下所示:

import UIKit

class EmitterView: UIView {
    
    private var emitters = [CAEmitterLayer]()
    
    enum EmitTo {
        case up
        case down
        case left
        case right
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        emitters.forEach { [=12=].removeFromSuperlayer() }
        emitters.removeAll()
        createParticles(direction: .up)
        createParticles(direction: .down)
        createParticles(direction: .left)
        createParticles(direction: .right)
    }
    
    func createParticles(direction: EmitTo) {
        let particleEmitter = CAEmitterLayer()
        emitters.append(particleEmitter)
        particleEmitter.position = CGPoint(x: bounds.midX, y: bounds.midY)
        particleEmitter.emitterShape = .line

        switch direction {
        case .up, .down:
            particleEmitter.bounds = self.bounds
            particleEmitter.emitterPosition = CGPoint(x: bounds.size.width / 2, y: 0)
            particleEmitter.emitterSize = CGSize(width: bounds.size.width, height: 0)
            if direction == .up {
                particleEmitter.setAffineTransform(CGAffineTransform(rotationAngle: -.pi))
            }
        case .left, .right:
            particleEmitter.bounds = CGRect(x: 0, y: 0, width: bounds.size.height, height: bounds.size.width)
            particleEmitter.emitterPosition = CGPoint(x: bounds.height / 2, y: 0)
            particleEmitter.emitterSize = CGSize(width: bounds.size.height, height: 0)
            let rotationAngle: CGFloat = direction == EmitTo.left ?  -.pi / 2 : .pi / 2
            particleEmitter.setAffineTransform(CGAffineTransform(rotationAngle: rotationAngle))
        }
        particleEmitter.emitterCells = [makeEmitterCell()]
        
        layer.addSublayer( particleEmitter)
    }
    
    func makeEmitterCell() -> CAEmitterCell {
        let cell = CAEmitterCell()
        cell.birthRate = 25
        cell.lifetime = 3
        cell.lifetimeRange = 0
        cell.velocity = 10
        cell.velocityRange = 5
        cell.emissionLongitude = .pi
        
        cell.spin = 2
        cell.spinRange = 3
        cell.scale = 0.4
        cell.scaleRange = 0.6
        cell.scaleSpeed = -0.10
        
        cell.contents = UIImage(named: "greenTriangle")?.cgImage
        return cell
    }
}

测试