Анимация появления/исчезновения для серии точек

У меня есть загрузка UIButton, и я пытаюсь реализовать анимацию, в которой у меня есть группа отдельных точек, которые выстраиваются горизонтально под кнопкой, и самая левая точка появляется/исчезает, а точка рядом с ней исчезает/ наружу и так далее до самой правой точки, затем снова начните цикл слева. Я могу заставить это работать для одной точки, но что было бы наиболее эффективным способом сделать это для нескольких точек?

func dotAnimation(){
    let xCoord = self.recButt.frame.origin.x + 25
    let yCoord = self.recButt.frame.origin.y + 5
    let radius = 3

    let dotPath = UIBezierPath(ovalIn: CGRect(x: Int(xCoord), y: Int(yCoord), width: radius, height: radius))
    let layer = CAShapeLayer()
    layer.path = dotPath.cgPath
    layer.strokeColor = UIColor(red: 95.00/255, green: 106.00/255, blue: 255/255, alpha: 1.00).cgColor
    self.view.layer.addSublayer(layer)

    let animation : CABasicAnimation = CABasicAnimation(keyPath: "opacity");
    animation.autoreverses = true
    animation.fromValue = 0
    animation.toValue = 1
    animation.duration = 2.0
    layer.add(animation, forKey: nil)
}

person Brosef    schedule 22.02.2017    source источник


Ответы (2)


Вы описываете CAReplicatorLayer. Вот тот, который делает то, что вам нужно, используя полосы вместо точек:

введите описание изображения здесь

Вот код для этого. Обратите внимание, что есть только один слой с красной полосой; репликация в пять и анимация затухания со смещением обрабатываются содержащим слоем репликатора:

    let lay = CAReplicatorLayer()
    lay.frame = CGRect(0,0,100,20)
    let bar = CALayer()
    bar.frame = CGRect(0,0,10,20)
    bar.backgroundColor = UIColor.red.cgColor
    lay.addSublayer(bar)
    lay.instanceCount = 5
    lay.instanceTransform = CATransform3DMakeTranslation(20, 0, 0)
    let anim = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
    anim.fromValue = 1.0
    anim.toValue = 0.2
    anim.duration = 1
    anim.repeatCount = .infinity
    bar.add(anim, forKey: nil)
    lay.instanceDelay = anim.duration / Double(lay.instanceCount)
    // and now just add `lay` to the interface
person matt    schedule 22.02.2017
comment
Похоже, так и будет. Просто прочитайте документацию Apple. Итак, в этом примере есть два слоя, слой и полоса подслоя, и слой реплицируется 5 раз? - person Brosef; 22.02.2017
comment
И это именно то, что вы хотели бы сделать. Конечно, мои размеры, формы, цвета, синхронизация и т. д. — это все параметры, которые вы можете полностью изменить для своих целей. - person matt; 22.02.2017
comment
Ладно, почти заработало. Как я могу сделать так, чтобы за раз появлялась только одна точка, чтобы к тому времени, когда одна точка начала исчезать, та, что слева, уже исчезла. Я предполагаю, что это связано с instanceDelay? - person Brosef; 22.02.2017
comment
Да, это должен быть вопрос жонглирования задержкой и продолжительностью анимации. Но лично у меня всегда были небольшие проблемы с тонкой настройкой. :) Обычно я просто перестаю настраивать, когда это достаточно хорошо. - person matt; 22.02.2017
comment
@matt может установить разную задержку для каждой репликации, в основном то, что я хочу, это 1 2 3, затем 3, 2,1, например, показать 1, затем 1 и 2, затем 1, 2 и 3, затем 1 и 2, затем 1. Возможно ли использовать CAReplicatorLayer ? - person Iraniya Naynesh; 12.06.2018
comment
Если это не работает для вас, убедитесь, что вы добавили слой в другом месте, кроме viewDidLoad (например, viewDidLayoutSubviews) - person Oded Ben Dov; 23.01.2021

Вот еще вариант анимации трех точек в любом UILabel

private var displayLink: CADisplayLink?

private var loadingLabeltext: String = ""
private var loadingLabel: UILabel? = {
    let label = UILabel()
    label.textColor = UIColor.appRed
    label.textAlignment = .center
    label.font = UIFont.init(name: "CirceRounded-Bold", size: 15)
    label.minimumScaleFactor = 0.6
    return label
}()

override func viewDidLoad(_ animated: Bool) {
    super.viewDidLoad(animated)

    view.addSubview(loadingLabel!)
    loadingLabel!.text = "Loading ..."
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    animateLabelDots(label: loadingLabel!)
}

private func animateLabelDots(label: UILabel) {
    guard var text = label.text else { return }
    text = String(text.dropLast(3))
    loadingLabeltext = text
    displayLink = CADisplayLink(target: self, selector: #selector(showHideDots))
    displayLink?.add(to: .main, forMode: .common)
    displayLink?.preferredFramesPerSecond = 4
}

@objc private func showHideDots() {
    if !loadingLabeltext.contains("...") {
        loadingLabeltext = loadingLabeltext.appending(".")
    } else {
        loadingLabeltext = "Loading "
    }

    loadingLabel!.text = loadingLabeltext
}

И когда вы хотите остановить его, просто аннулируйте и деинитизируйте отображаемую ссылку следующим образом:

private func hideLoadingSpinner() {
    displayLink?.invalidate()
    displayLink = nil

    UIView.animate(withDuration: 0.5, animations: {
        loadingLabel?.text = "Finished!"
        loadingLabel?.alpha = 0
    })
}

Удачного кодирования...

person Coder ACJHP    schedule 03.05.2019