Анимирующий CALayer не работает в iOS 14

#ios #swift #ios14

#iOS #swift #ios14

Вопрос:

У меня проблема с анимацией CALayer после обновления до Xcode12 и iOS14 — анимации, которые раньше работали на iOS13, не работают на iOS14. Вот код для анимации:

 private func animateCellPress() {
    let lightShadowAnimation = CABasicAnimation(keyPath: "shadowOffset")
    lightShadowAnimation.fromValue = CGSize(width: -self.shadowRadius, height: -self.shadowRadius)
    lightShadowAnimation.toValue = CGSize(width: self.shadowRadius, height: self.shadowRadius)
    lightShadowAnimation.fillMode = .forwards;
    lightShadowAnimation.isRemovedOnCompletion = false;
    lightShadowAnimation.duration = self.animationDuration
    self.lightShadow.add(lightShadowAnimation, forKey: lightShadowAnimation.keyPath)

    let darkShadowAnimation = CABasicAnimation(keyPath: "shadowOffset")
    darkShadowAnimation.fromValue = CGSize(width: self.shadowRadius, height: self.shadowRadius)
    darkShadowAnimation.toValue = CGSize(width: -self.shadowRadius, height: -self.shadowRadius)
    darkShadowAnimation.fillMode = .forwards;
    darkShadowAnimation.isRemovedOnCompletion = false;
    darkShadowAnimation.duration = self.animationDuration
    self.darkShadow.add(darkShadowAnimation, forKey: darkShadowAnimation.keyPath)
}
  

lightShadow и darkShadow хранятся как свойства, и вот код инициализации для них:

 private func initializeDarkShadow() {
    darkShadow = CALayer()
    darkShadow.frame = bounds
    darkShadow.backgroundColor = backgroundColor?.cgColor
    darkShadow.cornerRadius = cornerRadius
    darkShadow.shadowOffset = CGSize(width: shadowRadius, height: shadowRadius)
    darkShadow.shadowOpacity = 1
    darkShadow.shadowRadius = shadowRadius
    darkShadow.shadowColor = Asset.Colors.darkShadow.color.cgColor
    let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: bounds.size)
    darkShadow.shadowPath = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).cgPath
    layer.insertSublayer(darkShadow, at: 0)
}

private func initializeLightShadow() {
    lightShadow = CALayer()
    lightShadow.frame = bounds
    lightShadow.backgroundColor = backgroundColor?.cgColor
    lightShadow.cornerRadius = cornerRadius
    lightShadow.shadowOffset = CGSize(width: -shadowRadius, height: -shadowRadius)
    lightShadow.shadowOpacity = 1
    lightShadow.shadowRadius = shadowRadius
    lightShadow.shadowColor = Asset.Colors.lightShadow.color.cgColor
    let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: bounds.size)
    lightShadow.shadowPath = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).cgPath
    layer.insertSublayer(lightShadow, at: 0)
}
  

Есть идеи о том, что не так и как я могу это исправить? Также я не получаю никаких ошибок компилятора или предупреждений. Журнал консоли также пуст.

Комментарии:

1. Где вы инициализируете слои? Есть ли у вас какой-либо код в layoutSubviews для обновления фреймов? Границы ячеек, вероятно, равны нулю при ее создании. Или, может быть, новое поведение — это обрезка слоев

2. Я инициализирую тени при инициализации, но в layoutSubviews я повторно инициализирую их — удаляю их из суперслоя и вызываю те же функции инициализации, которые показаны выше. Но тени отображаются правильно, это просто анимация их, что не работает.

Ответ №1:

Это работает. Я думаю, что каждый раз, когда вы вызываете анимированные layoutSubviews, вызывается, таким образом, удаляя слой и анимацию. Вы могли бы либо просто обновить фрейм там, либо проверить, был ли слой уже добавлен. Если это не проблема, то что-то не так с вашей функцией нажатия или касания. Создайте проект с одним представлением, скопируйте и вставьте его в свой файл ViewController.

 import UIKit

class TableViewCell : UITableViewCell{
    let darkShadowColor = UIColor.green
    let lightShadowColor = UIColor.red
    let animationDuration : Double = 2
    
    var darkShadow = CALayer()
    var lightShadow = CALayer()
    let cornerRadius : CGFloat = 4
    let shadowRadius : CGFloat = 3
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        initializeDarkShadow()
        initializeLightShadow()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        initializeDarkShadow()
        initializeLightShadow()
    }
    
    private func initializeDarkShadow() {
        darkShadow = CALayer()
        darkShadow.frame = bounds
        darkShadow.backgroundColor = backgroundColor?.cgColor
        darkShadow.cornerRadius = cornerRadius
        darkShadow.shadowOffset = CGSize(width: shadowRadius, height: shadowRadius)
        darkShadow.shadowOpacity = 1
        darkShadow.shadowRadius = shadowRadius
        darkShadow.shadowColor = darkShadowColor.cgColor
        let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: bounds.size)
        darkShadow.shadowPath = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).cgPath
        layer.insertSublayer(darkShadow, at: 0)
    }

    private func initializeLightShadow() {
        lightShadow = CALayer()
        lightShadow.frame = bounds
        lightShadow.backgroundColor = backgroundColor?.cgColor
        lightShadow.cornerRadius = cornerRadius
        lightShadow.shadowOffset = CGSize(width: -shadowRadius, height: -shadowRadius)
        lightShadow.shadowOpacity = 1
        lightShadow.shadowRadius = shadowRadius
        lightShadow.shadowColor = lightShadowColor.cgColor
        let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: bounds.size)
        lightShadow.shadowPath = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).cgPath
        layer.insertSublayer(lightShadow, at: 0)
    }
    
    func animateCellPress() {
        print("called")
        let lightShadowAnimation = CABasicAnimation(keyPath: "shadowOffset")
        lightShadowAnimation.fromValue = CGSize(width: -self.shadowRadius, height: -self.shadowRadius)
        lightShadowAnimation.toValue = CGSize(width: self.shadowRadius, height: self.shadowRadius)
        lightShadowAnimation.beginTime = CACurrentMediaTime()
        lightShadowAnimation.fillMode = .both;
        lightShadowAnimation.isRemovedOnCompletion = false;
        lightShadowAnimation.duration = self.animationDuration
        self.lightShadow.add(lightShadowAnimation, forKey: nil)


        let darkShadowAnimation = CABasicAnimation(keyPath: "shadowOffset")
        darkShadowAnimation.fromValue = CGSize(width: self.shadowRadius, height: self.shadowRadius)
        darkShadowAnimation.toValue = CGSize(width: -self.shadowRadius, height: -self.shadowRadius)
        darkShadowAnimation.beginTime = CACurrentMediaTime()
        darkShadowAnimation.fillMode = .both;
        darkShadowAnimation.isRemovedOnCompletion = false;
        darkShadowAnimation.duration = self.animationDuration
        self.darkShadow.add(darkShadowAnimation, forKey: nil)
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        self.lightShadow.frame = self.bounds
        self.darkShadow.frame = self.bounds
    }
}

class ViewController: UIViewController {

    let identifier = "someCellID"
    lazy var tableView : UITableView = {
        let tv = UITableView(frame: self.view.bounds)
        tv.delegate = self
        tv.dataSource = self
        tv.autoresizingMask = [.flexibleWidth,.flexibleHeight]
        tv.register(TableViewCell.self, forCellReuseIdentifier: identifier)
        return tv
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        self.view.addSubview(tableView)
    }
}

extension ViewController : UITableViewDelegate,UITableViewDataSource{
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 20
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath) as? TableViewCell{
            cell.selectionStyle = .none
            return cell
        }
        print("error")
        return UITableViewCell()
    }
    
    
    
    func tableView(_ tableView: UITableView, didHighlightRowAt indexPath: IndexPath) {
        if let cell = tableView.cellForRow(at: indexPath) as? TableViewCell{
            cell.animateCellPress()
        }
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 50
    }
}
  

Комментарии:

1. Я попытался удалить первый. Я нажал enter по ошибке

2. Возможно, восстановить и отредактировать его было бы правильным способом. Не уверен, как SO ведет себя

3. Вы правы, проблема заключалась в повторной инициализации теней в методе layoutSubviews (хотя я обновлял свойства в этом методе). Спасибо за помощь!

4. Да, это был бы правильный путь. Я постоянно делаю подобные вещи: вы публикуете ответ, понимаете, что он неправильный, удаляете его, затем редактируете до тех пор, пока он не станет правильным, затем восстанавливаете его. Нет проблем, вы привыкнете к этому.