为什么Core Animation相比Core Graphics有延迟?

问题描述 投票:0回答:1

我正在尝试在 iOS 设备上实现自定义 UISlider。

首先,ThumbSlider 位于两端,期望的行为如下:当 ThumbSlider 向左或向右移动时,它们之间的 TopView 应同时展开或收缩。

当 TopView 实现为 UIView 时,上述视图可以正常工作。

TopView 作为 UIView

同样,使用 Core Graphics 时也能很好地工作。

TopView 作为核心图形

但是,当使用Core Animation实现TopView时,ThumbSlider和动画之间存在明显的延迟。

TopView 作为核心动画

这种行为与我的预期完全不同。虽然我知道 UIView 可能针对这种操作进行了优化,但我无法理解为什么 Core Animation 与 Core Graphics 相比会引入延迟。

这是我的最后一个问题:

  1. 为什么Core Animation的绘制比UIView慢?
  2. 为什么 Core Graphics(基于 CPU)比 Core Animation 更快?
final class EditSliderBar: UIControl {
  enum Constants {
    static let sliderWidth: CGFloat = 16
  }
  
  // MARK: - UI Components
  private let lowerThumbSlider = EditSlider(tintColor: .systemYellow)
  private let upperThumbSlider = EditSlider(tintColor: .systemYellow)
  private let topView = UIView()
  private let topLayer = CALayer()
  weak var currentHighlightedThumbSlider: EditSlider?
  
  private var previousLocation: CGPoint = .zero
  private var minimumValue: Double = 0
  private var maximumValue: Double = 100
  
  ...

  private(set) var lowerValue: Double = 0.0 {
    didSet {
      updateSliderFrame(lowerThumbSlider)
    }
  }
  

  private(set) var upperValue: Double = 100 {
    didSet {
      updateSliderFrame(upperThumbSlider)
    }
  }

  ...

  
  // MARK: - UIControl
  override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
    let location = touch.location(in: self)
    defer { previousLocation = location }

    if lowerSlider.contain(point: location) {
      lowerSlider.isHightlighted = true
      currentHighlightedSlider = lowerSlider
    } else if upperSlider.contain(point: location) {
      upperSlider.isHightlighted = true
      currentHighlightedSlider = upperSlider
    }
    
    return lowerSlider.isHightlighted || upperSlider.isHightlighted
  }
  
  override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
    let location = touch.location(in: self)
    defer { previousLocation = location }
    
    guard let slider = currentHighlightedThumbSlider else { return false }
    
    let sliderDeltaValue = deltaValue(from: previousLocation, to: location)
    
    if slider === lowerThumbSlider {
      self.lowerValue = updatedLowerValue(moved: sliderDeltaValue)
    } else if slider === upperThumbSlider {
      self.upperValue = updatedUpperValue(moved: sliderDeltaValue)
    }
    sendActions(for: .valueChanged)
    return true
  }
  
  override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
    ...
  }
  
  // MARK: - LayoutSubviews
  override func layoutSubviews() {
    super.layoutSubviews()
    updateSliderFrames()
  }
  
  // MARK: - Draw
  override func draw(_ rect: CGRect) {
    guard let context = UIGraphicsGetCurrentContext() else { return }
    
    let maskedRect = CGRect(
      x: lowerSlider.center.x,
      y: 0,
      width: upperThumbSlider.center.x - lowerThumbSlider.center.x,
      height: 5
    )
    
    context.setFillColor(UIColor.systemYellow.cgColor)
    context.fill(maskedRect)
  }
}

// MARK: - Private Methotds
private extension EditSliderBar {
  func updateSliderFrames() {
    updateSliderFrame(lowerThumbSlider)
    updateSliderFrame(upperThumbSlider)
  }
  
  func updateSliderFrame(_ slider: EditSlider) {
    let width = Constants.sliderWidth
    
    let leading = slider === lowerThumbSlider ? leading(of: lowerValue) : leading(of: upperValue)
    
    slider.frame = CGRect(
      x: leading,
      y: 0,
      width: width,
      height: bounds.height
    )
    setNeedsDisplay()
    
    /// Core Animation
    updateTopLayer()
    /// Core Graphics
    setNeedsDisplay()
    /// UIView
    updateTopView()
  }

  // Core Animation
  func updateTopLayer() {
    topLayer.frame = CGRect(
      x: lowerThumbSlider.center.x,
      y: 0,
      width: upperThumbSlider.frame.maxX - lowerThumbSlider.center.x,
      height: 5
    )
    topLayer.backgroundColor = UIColor.systemYellow.cgColor
  }
  
  // UIView
  func updateTopView() {
    topView.frame = CGRect(
      x: lowerSlider.center.x,
      y: 0,
      width: upperThumbSlider.frame.maxX - lowerThumbSlider.center.x,
      height: 5
    )
    topView.backgroundColor = UIColor.systemYellow
  }
  
  func updatedLowerValue(moved delta: Double) -> Double {
    return (lowerValue + delta).bound(lower: minimumValue, upper: upperValue - gapBetweenSliders)
  }
  
  func updatedUpperValue(moved delta: Double) -> Double {
    return (upperValue + delta).bound(lower: lowerValue + gapBetweenSliders, upper: maximumValue)
  }

  func deltaValue(from previous: CGPoint, to current: CGPoint) -> Double {
    let deltaLocation = Double(current.x - previous.x)
    
    return (maximumValue - minimumValue) * deltaLocation / Double(totalLength)
  }
  
  func leading(of value: Double) -> Double {
    return totalLength * value / maximumValue
  }
}
ios swift core-graphics core-animation swift5
1个回答
0
投票

一些

CALayer
属性是“隐式动画”的。这意味着当您更改属性时,系统会为您创建动画。

该代码可能如下所示:

CATransaction.begin()
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
// Change CALayer properties that you don’t want to animate here.
CATransaction.commit()
© www.soinside.com 2019 - 2024. All rights reserved.