界面iOS学习笔记iOS Developer

iOS Review | 你所不知道的10种Layer

2017-09-15  本文已影响110人  清無

1. CALayer

UIView和CALayer的关系
UIView和CALayer的关系
CALayer Basic Properties
let layer = view.layer
layer.masksToBounds = true // 必须为true
layer.cornerRadius = 10
let layer = view.layer
layer.borderColor = UIColor.red.cgColor
layer.borderWidth = 10
let layer = view.layer
layer.shadowColor = UIColor.green.cgColor
layer.shadowRadius = 10 // 半径
layer.shadowOpacity = 0.5 // 透明度
layer.shadowOffset = CGSize(width: 0, height: 10) // 偏移: x, y
let layer = view.layer
layer.contentsGravity = kCAGravityCenter
layer.contentsScale = UIScreen.main.scale
layer.contents = UIImage.init(named: "star")?.cgImage

2. CAScrollLayer

scrollingViewLayer.scrollMode = kCAScrollBoth
ScrollingView + UIImageView
import QuartzCore

class ScrollingView: UIView {
  override class var layerClass : AnyClass {
    return CAScrollLayer.self
  }
}
    @IBAction func panRecognized(_ sender: UIPanGestureRecognizer) {
// 计算滚动点
        var newPoint = scrollingView.bounds.origin
        newPoint.x -= sender.translation(in: scrollingView).x
        newPoint.y -= sender.translation(in: scrollingView).y
        sender.setTranslation(CGPoint.zero, in: scrollingView)
// 滚动到新点
        scrollingViewLayer.scroll(to: newPoint)
    }
CAScrollLayer

3. CATextLayer

    let string = String.init(repeating: "这是测试CATextLayer的文字--", count: 10)

    textLayer.string = string // 可以是NSAttributedString
    textLayer.font = CTFontCreateWithName("Noteworthy-Light" as CFString, 0, nil)
    textLayer.fontSize = 18
    textLayer.foregroundColor = UIColor.red.cgColor // 字体颜色
    textLayer.isWrapped = true // 单行/多行
    textLayer.alignmentMode = kCAAlignmentLeft
    textLayer.truncationMode = kCATruncationEnd
    textLayer.contentsScale = UIScreen.main.scale
CATextLayer

4. AVPlayerLayer

var player: AVPlayer!

override func viewDidLoad() {
  super.viewDidLoad()

  // 1
  let playerLayer = AVPlayerLayer()
  playerLayer.frame = someView.bounds
  
  // 2
  let url = Bundle.main.url(forResource: "someVideo", withExtension: "m4v")
  player = AVPlayer(url: url!)
  
  // 3

  // 播放完成后执行的操作--无、暂停、下一个`advance`(只适用于AVQueuePlayer)
  player.actionAtItemEnd = .none 
  playerLayer.player = player
  someView.layer.addSublayer(playerLayer)
  
  // 4
  NotificationCenter.default.addObserver(self,
                                         selector: #selector(playerDidReachEnd),
                                         name: .AVPlayerItemDidPlayToEndTime,
                                         object: player.currentItem)
}

deinit {
  NotificationCenter.default.removeObserver(self)
}
AVPlayerLayer

5. CAGradientLayer

func cgColor(red: CGFloat, green: CGFloat, blue: CGFloat) -> CGColor {
  return UIColor(red: red/255.0, green: green/255.0, blue: blue/255.0, alpha: 1.0).cgColor
}

let gradientLayer = CAGradientLayer()
gradientLayer.frame = someView.bounds
gradientLayer.colors = [cgColor(red: 209.0, green: 0.0, blue: 0.0),
                        cgColor(red: 255.0, green: 102.0, blue: 34.0),
                        cgColor(red: 255.0, green: 218.0, blue: 33.0),
                        cgColor(red: 51.0, green: 221.0, blue: 0.0),
                        cgColor(red: 17.0, green: 51.0, blue: 204.0),
                        cgColor(red: 34.0, green: 0.0, blue: 102.0),
                        cgColor(red: 51.0, green: 0.0, blue: 68.0)]

gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 0, y: 1)
someView.layer.addSublayer(gradientLayer)
gradientLayer.locations: [NSNumber] = [:]
垂直渐变 水平渐变

6. CAReplicatorLayer

// 复制器层
replicatorLayer.backgroundColor = UIColor.clear.cgColor
replicatorLayer.instanceCount = 30
replicatorLayer.instanceDelay = CFTimeInterval(1 / 30.0)
replicatorLayer.preservesDepth = false
replicatorLayer.instanceColor = UIColor.red.cgColor
replicatorLayer.instanceRedOffset = 0
replicatorLayer.instanceGreenOffset = -1
replicatorLayer.instanceBlueOffset = -1
replicatorLayer.instanceAlphaOffset = -1 / Float(replicatorLayer.instanceCount)
replicatorLayer.instanceTransform = CATransform3DMakeRotation(CGFloat.pi * 2 / 30, 0, 0, 1)

// 实例层
let layerWidth: CGFloat = 10
let instanceLayer = CALayer()
instanceLayer.backgroundColor = UIColor.white.cgColor
let midX = replicatorLayer.bounds.midX - layerWidth / 2
instanceLayer.frame = CGRect(x: midX, y: 0, width: layerWidth, height: layerWidth * 3)
replicatorLayer.addSublayer(instanceLayer)
RGBA offset = 0, -1, -1, (-1 / Float(replicatorLayer.instanceCount))
// 动画
let fadeAnimation = CABasicAnimation.init(keyPath: "opacity")
fadeAnimation.fromValue = 1
fadeAnimation.toValue = 0
fadeAnimation.duration = 1
fadeAnimation.repeatCount = Float(Int.max)
instanceLayer.opacity = 0
instanceLayer.add(fadeAnimation, forKey: "FadeAnimation")
动画效果

7. CATiledLayer

levelsOfDetailBias = 1 levelsOfDetailBias = 5
class TiledBackgroundView: UIView {

    let sideLength: CGFloat = 50
    override class var layerClass: AnyClass{
        return CATiledLayer.self
    }
    
    override func draw(_ rect: CGRect) {
        let context = UIGraphicsGetCurrentContext()
        let red = CGFloat(drand48())
        let green = CGFloat(drand48())
        let blue = CGFloat(drand48())
        context?.setFillColor(red: red, green: green, blue: blue, alpha: 1)
        context?.fill(rect)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        
        let layer = self.layer as! CATiledLayer
        let scale = UIScreen.main.scale
        layer.contentsScale = scale
        layer.tileSize = CGSize(width: sideLength * scale, height: sideLength * scale)
    }

}
image.png
extension UIImage {
  
  class func saveTileOfSize(_ size: CGSize, name: String) -> () {
    let cachesPath = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)[0] as String
    let filePath = "\(cachesPath)/\(name)_0_0.png"
    let fileManager = FileManager.default
    let fileExists = fileManager.fileExists(atPath: filePath)
        
    if fileExists == false {
      var tileSize = size
      let scale = Float(UIScreen.main.scale)
      
      if let image = UIImage(named: "\(name).jpg") {
        let imageRef = image.cgImage
        let totalColumns = Int(ceilf(Float(image.size.width / tileSize.width)) * scale)
        let totalRows = Int(ceilf(Float(image.size.height / tileSize.height)) * scale)
        let partialColumnWidth = Int(image.size.width.truncatingRemainder(dividingBy: tileSize.width))
        let partialRowHeight = Int(image.size.height.truncatingRemainder(dividingBy: tileSize.height))
        
        DispatchQueue.global(qos: .default).async {
          for y in 0..<totalRows {
            for x in 0..<totalColumns {
              if partialRowHeight > 0 && y + 1 == totalRows {
                tileSize.height = CGFloat(partialRowHeight)
              }
              
              if partialColumnWidth > 0 && x + 1 == totalColumns {
                tileSize.width = CGFloat(partialColumnWidth)
              }
              
              let xOffset = CGFloat(x) * tileSize.width
              let yOffset = CGFloat(y) * tileSize.height
              let point = CGPoint(x: xOffset, y: yOffset)
              
              if let tileImageRef = imageRef?.cropping(to: CGRect(origin: point, size: tileSize)), let imageData = UIImagePNGRepresentation(UIImage(cgImage: tileImageRef)) {
                let path = "\(cachesPath)/\(name)_\(x)_\(y).png"
                try? imageData.write(to: URL(fileURLWithPath: path), options: [])
              }
            }
          }
        }
      }
    }
  }
  
}
override func draw(_ rect: CGRect) {
    let firstColumn = Int(rect.minX / sideLength)
    let lastColumn = Int(rect.maxX / sideLength)
    let firstRow = Int(rect.minY / sideLength)
    let lastRow = Int(rect.maxY / sideLength)
    
    for row in firstRow...lastRow {
        for column in firstColumn...lastColumn{
            guard let image = imageForTile(atColumn: column, row: row) else { continue }
            let x = sideLength * CGFloat(column)
            let y = sideLength * CGFloat(row)
            let tileRect = CGRect.init(x: x, y: y, width: sideLength, height: sideLength)
            image.draw(in: tileRect)
        }
    }
}

func imageForTile(atColumn column: Int, row: Int) -> UIImage? {
    let filePath = "\(cachesPath)/\(fileName)_\(column)_\(row).png"
    return UIImage(contentsOfFile: filePath)
}
异步绘制分块图像

8. CAShapeLayer

9. CATransformLayer

CATransformLayer

创建六面体

override func viewDidLoad() {
    super.viewDidLoad()
    
    // 前
    var layer = sideLayer(color: .red)
    transformLayer.addSublayer(layer)
    layer.transform = CATransform3DMakeTranslation(0.0, 0.0, sideLength / 2)
    
    // 后
    layer = sideLayer(color: .green)
    transformLayer.addSublayer(layer)
    layer.transform = CATransform3DMakeTranslation(0.0, 0.0, -sideLength / 2)
    
    // 上
    layer = sideLayer(color: .orange)
    transformLayer.addSublayer(layer)
    var transform = CATransform3DMakeTranslation(0.0, sideLength / 2, 0.0)
    transform = CATransform3DRotate(transform, degreesToRadians(90.0), 1.0, 0.0, 0.0)
    layer.transform = transform
    
    // 下
    layer = sideLayer(color: .blue)
    transformLayer.addSublayer(layer)
    transform = CATransform3DMakeTranslation(0.0, -sideLength / 2, 0.0)
    transform = CATransform3DRotate(transform, degreesToRadians(90.0), 1.0, 0.0, 0.0)
    layer.transform = transform
    
    // 左
    layer = sideLayer(color: .cyan)
    transformLayer.addSublayer(layer)
    transform = CATransform3DMakeTranslation(-sideLength / 2, 0.0, 0.0)
    transform = CATransform3DRotate(transform, degreesToRadians(90.0), 0.0, 1.0, 0.0)
    layer.transform = transform
    
    // 右
    layer = sideLayer(color: .purple)
    transformLayer.addSublayer(layer)
    transform = CATransform3DMakeTranslation(sideLength / 2, 0.0, 0.0)
    transform = CATransform3DRotate(transform, degreesToRadians(90.0), 0.0, 1.0, 0.0)
    layer.transform = transform
    
    rotate(xOffset: 200, yOffset: 200)
}

func sideLayer(color: UIColor) -> CALayer {
    let layer = CALayer()
    layer.backgroundColor =
        color.withAlphaComponent(0.6).cgColor
    layer.frame = CGRect(origin: .zero, size: CGSize(width: sideLength, height: sideLength))
    layer.position = CGPoint(x: transformLayerView.bounds.midX, y: transformLayerView.bounds.midY)
    return layer
}

func degreesToRadians(_ degrees: Double) -> CGFloat {
    return CGFloat(degrees * .pi / 180.0)
}

旋转变换

func rotate(xOffset: Double, yOffset: Double) {
    let totalOffset = sqrt(xOffset * xOffset + yOffset * yOffset)
    let totalRotation = CGFloat(totalOffset * .pi / 180.0)
    let xRotationalFactor = CGFloat(totalOffset) / totalRotation
    let yRotationalFactor = CGFloat(totalOffset) / totalRotation
    let currentTransform = CATransform3DTranslate(transformLayer.sublayerTransform, 0.0, 0.0, 0.0)
    let x = xRotationalFactor * currentTransform.m12 - yRotationalFactor * currentTransform.m11
    let y = xRotationalFactor * currentTransform.m22 - yRotationalFactor * currentTransform.m21
    let z = xRotationalFactor * currentTransform.m32 - yRotationalFactor * currentTransform.m31
    let rotation = CATransform3DRotate(transformLayer.sublayerTransform, totalRotation, x, y, z)
    transformLayer.sublayerTransform = rotation
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let location = touches.first?.location(in: transformLayerView) else {
        return
    }
    rotate(xOffset: Double(location.x / 50), yOffset: Double(location.y / 50))
}

hitTest

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let location = touches.first?.location(in: transformLayerView) else {
        return
    }
    for layer in transformLayer.sublayers! where layer.hitTest(location) != nil {
        print("Touched Sublayer")
    }
}

10. CAEmitterLayer

setupEmitterLayer

func setupEmitterLayer() {
    emitterLayer.emitterCells = [emitterCell]
    emitterLayer.seed = UInt32(Date().timeIntervalSince1970)
    emitterLayer.renderMode = kCAEmitterLayerAdditive
    emitterLayer.drawsAsynchronously = true
    setEmitterPosition(CGPoint(x: view.bounds.midX, y: view.bounds.midY))
}

setupEmitterCell

func setupEmitterCell(){
    emitterCell.contents = UIImage.init(named: "smallStar")?.cgImage
    
    emitterCell.velocity = 50.0
    emitterCell.velocityRange = 500.0
    
    emitterCell.color = UIColor.black.cgColor
    emitterCell.redRange = 1.0
    emitterCell.greenRange = 1.0
    emitterCell.blueRange = 1.0
    emitterCell.alphaRange = 0.0
    emitterCell.redSpeed = 0.0
    emitterCell.greenSpeed = 0.0
    emitterCell.blueSpeed = 0.0
    emitterCell.alphaSpeed = -0.5
    
    let zeroDegreesInRadians = degreesToRadians(0.0)
    emitterCell.spin = degreesToRadians(130.0)
    emitterCell.spinRange = zeroDegreesInRadians
    emitterCell.emissionRange = degreesToRadians(360.0)
    
    emitterCell.lifetime = 1.0
    emitterCell.birthRate = 250.0
    emitterCell.xAcceleration = -800.0
    emitterCell.yAcceleration = 1000.0
}

其他设置

func setEmitterPosition(_ position: CGPoint) {
    emitterLayer.emitterPosition = position
}

func degreesToRadians(_ degrees: Double) -> CGFloat {
    return CGFloat(degrees * .pi / 180.0)
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let location = touches.first?.location(in: view) else {
        return
    }
    setEmitterPosition(location)
}
CAEmitterLayer

以上就是全部的CALayer相关的知识了,工程文件见https://github.com/BackWorld/LayerPlayer

如果对你有帮助,别忘了点个👍或关注下哦~

上一篇下一篇

猜你喜欢

热点阅读