Новое:Talanto осенью: фильтры с числами, отклик по шагам и каталог компаний

+1 074новые вакансии за сутки·91 191вакансия открыта сейчас·Новое:Talanto осенью: фильтры с числами, отклик по шагам и каталог компаний

Новые вакансии в TelegramРаботодателям
Talanto
Вакансии
Компании7 116
РезюмеAIПодготовкаСтатьи
BoostТарифыBoost
Войти

Не нашли подходящее?

Пусть нужная вакансия найдёт вас сама.

  1. Подборка в TelegramНовые вакансии по вашему фильтру — сразу в бота.Подключить
  2. Мэтчи по резюмеЗагрузите резюме — покажем, где вы подходите лучше всего.Загрузить
  3. Сохранить фильтрНастройте поиск один раз и возвращайтесь к новым вакансиям.Настроить
Что нового на TalantoTalanto осенью: фильтры с числами, отклик по шагам и каталог компаний

Для работодателей

Опубликуйте вакансию — отклики придут в ваш кабинет.

  1. Вакансиябесплатно
  2. Откликиконтакты бесплатно
  3. Наймэтапы и интервью

40 компаний публикуют вакансии напрямую · 4 410 кандидатов открыты к предложениям

Разместить вакансию

Популярные направления

Все подборки
  • Python17 172
  • Облачные технологии14 136
  • QA вакансии3 451
  • Backend вакансии2 051
  • Инженер данных1 762
  • Продакт-менеджер1 226

Разработка

  • Android1 854
  • iOS1 694
  • Fullstack вакансии978
  • AI вакансии944
  • Frontend вакансии735
  • Mobile вакансии43

По стеку

  • Go8 740
  • Java6 589
  • Node.js2 029
  • .NET1 734
  • Kotlin1 615
  • Rust1 473
  • C#715
  • PHP692
  • C++581

DevOps и инфраструктура

  • Kubernetes7 760
  • DevOps вакансии6 073
  • Linux-инфраструктура5 498
  • Terraform / IaC4 270
  • CI/CD3 273
  • Мониторинг и Observability1 791
  • SRE553
  • Кибербезопасность444

Аналитика и данные

  • Data Scientist1 115
  • Бизнес-аналитик916
  • Аналитик данных832
  • Системный аналитик153

Другие направления

  • Проджект-менеджер606
  • UI/UX дизайн145
  • Ручное тестирование14

По регионам

  • Вакансии в США42 450
  • Вакансии в Европе23 519
  • Вакансии в Канаде4 333
  • Работа в России3 786

По формату

  • Удалённые вакансии72 089
  • Релокация в США42 450
  • Гибридная работа12 458
  • Офисная работа7 066

По уровню

  • Senior вакансии41 491
  • Middle вакансии20 265
  • Junior вакансии6 499

Сейчас нанимают

Все компании
  • Rakuten189 вакансий
  • Wise189 вакансий
  • ASML166 вакансий
  • Grab137 вакансий
  • Celonis38 вакансий
  • Spotify37 вакансий
  • Adyen28 вакансий
  • Infobip28 вакансий
  • Monzo20 вакансий
  • Darktrace17 вакансий

Вакансии из 344 источников: карьерные страницы компаний и джоб-борды.

КомпанииО насНовостиПубличная офертаУсловия использованияПолитика конфиденциальностиПоддержка
←К теме UIKitВсе вопросы
UIKitMiddleКодинг

Что такое Core Animation и как создавать базовые анимации в UIKit?

В закладки
Сначала попробуйте ответить сами30 секунд
Сравнить с ответом ↓

Core Animation работает через CALayer (model/presentation layer), render server и CATransaction. UIView.animate — удобная обёртка; CABasicAnimation даёт контроль над timing и fill mode; CADisplayLink синхронизирует покадровую логику с дисплеем.

Что такое Core Animation

Core Animation (CA) — графический движок Apple, работающий в отдельном процессе rendererd (iOS) или WindowServer (macOS). Он оперирует не UIView, а CALayer — backing layer каждого вью. Слои хранят два дерева состояния: model layer (то, что вы присваиваете в коде) и presentation layer (интерполированное значение в данный кадр). Это различие — источник большинства багов с hit-testing во время анимации.

UIKit предоставляет три уровня абстракции над CA:

  • UIView.animate(withDuration:…) — самый высокий, анимирует animatable-свойства вью через implicit transaction;
  • CABasicAnimation / CAKeyframeAnimation — явные анимации на уровне слоя, дают полный контроль над timing, fill mode и делегатом;
  • CADisplayLink — покадровый callback, синхронизированный с частотой дисплея (ProMotion: до 120 Hz).

Пример: UIView.animate и CABasicAnimation

import UIKit

final class AnimationDemoViewController: UIViewController {

    private let box = UIView()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemBackground

        box.frame = CGRect(x: 40, y: 200, width: 80, height: 80)
        box.backgroundColor = .systemBlue
        box.layer.cornerRadius = 12
        view.addSubview(box)

        let tapGR = UITapGestureRecognizer(target: self, action: #selector(runAnimations))
        view.addGestureRecognizer(tapGR)
    }

    // MARK: – UIView convenience API (implicit CATransaction)
    @objc private func runAnimations() {
        // 1. Простая анимация позиции и прозрачности
        UIView.animate(
            withDuration: 0.5,
            delay: 0,
            usingSpringWithDamping: 0.7,
            initialSpringVelocity: 0.5,
            options: [.curveEaseInOut]
        ) {
            self.box.center.x += 120
            self.box.alpha = 0.4
        } completion: { _ in
            // completion вызывается на главном потоке
            self.runCABasicAnimation()
        }
    }

    // MARK: – Явная CABasicAnimation
    private func runCABasicAnimation() {
        let anim = CABasicAnimation(keyPath: "transform.rotation.z")
        anim.fromValue = 0
        anim.toValue   = Double.pi * 2      // полный оборот
        anim.duration  = 0.8
        anim.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)

        // fillMode + isRemovedOnCompletion — частая ловушка
        anim.fillMode = .forwards
        anim.isRemovedOnCompletion = false  // ⚠️ слой живёт вечно, если забыть убрать

        anim.delegate = self                // AnimationDelegate
        box.layer.add(anim, forKey: "rotation")
    }

    // MARK: – CAKeyframeAnimation для нелинейного пути
    private func runKeyframe() {
        let pathAnim = CAKeyframeAnimation(keyPath: "position")
        pathAnim.path = makeBouncePath()
        pathAnim.duration = 1.2
        pathAnim.calculationMode = .cubicPaced
        box.layer.add(pathAnim, forKey: "pathMove")
    }

    private func makeBouncePath() -> CGPath {
        let path = UIBezierPath()
        path.move(to: box.center)
        path.addQuadCurve(
            to: CGPoint(x: box.center.x, y: 500),
            controlPoint: CGPoint(x: box.center.x + 80, y: 350)
        )
        return path.cgPath
    }
}

// MARK: – CAAnimationDelegate
extension AnimationDemoViewController: CAAnimationDelegate {
    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        guard flag else { return }          // отменена — не трогаем UI
        // Синхронизируем model layer с presentation layer
        box.layer.removeAnimation(forKey: "rotation")
        // Сбрасываем реальное значение, иначе при следующем изменении будет прыжок
        box.layer.transform = CATransform3DIdentity
    }
}

CADisplayLink для покадровой анимации

final class ParticleViewController: UIViewController {
    private var displayLink: CADisplayLink?
    private var phase: CGFloat = 0
    private let waveLayer = CAShapeLayer()

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        view.layer.addSublayer(waveLayer)
        waveLayer.strokeColor = UIColor.systemIndigo.cgColor
        waveLayer.fillColor   = UIColor.clear.cgColor
        waveLayer.lineWidth   = 2

        displayLink = CADisplayLink(target: self, selector: #selector(tick))
        // preferredFrameRateRange — ProMotion API (iOS 15+)
        displayLink?.preferredFrameRateRange = CAFrameRateRange(
            minimum: 60, maximum: 120, preferred: 120
        )
        displayLink?.add(to: .main, forMode: .common)
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        displayLink?.invalidate()   // ⚠️ без invalidate — retain cycle и утечка
        displayLink = nil
    }

    @objc private func tick(link: CADisplayLink) {
        // link.targetTimestamp — время следующего кадра (точнее, чем Date())
        phase += CGFloat(link.targetTimestamp - link.timestamp) * 3
        waveLayer.path = buildWavePath(phase: phase)
    }

    private func buildWavePath(phase: CGFloat) -> CGPath {
        let path = UIBezierPath()
        let w = view.bounds.width
        path.move(to: CGPoint(x: 0, y: view.bounds.midY))
        stride(from: CGFloat(0), through: w, by: 2).forEach { x in
            let y = view.bounds.midY + sin(x / 30 + phase) * 40
            path.addLine(to: CGPoint(x: x, y: y))
        }
        return path.cgPath
    }
}

Как работает render pipeline

При изменении свойства слоя CA создаёт неявный CATransaction. В конце run loop он фиксирует дерево, отправляет скомпилированные команды в render server по XPC. Render server работает в отдельном процессе — именно поэтому анимации не прерываются при занятом главном потоке короткое время. Но длинный main-thread jank (~16 мс) всё равно вызывает dropped frames, потому что CA не получает следующий commit.

Подводные камни

  • fillMode + isRemovedOnCompletion = false без cleanup. Presentation layer замирает в конечной точке, model layer — в исходной. При следующем изменении происходит видимый прыжок. Всегда явно задавайте финальное значение model layer в animationDidStop и затем вызывайте removeAnimation(forKey:).
  • Hit-testing бьёт по model layer. Во время анимации UIView.animate нажатие в визуальную позицию кнопки ничего не даёт — responder chain ищет view по frame из model layer (исходная позиция). Используйте layer.presentation()?.hitTest(point) или перемещайте view до начала анимации.
  • CADisplayLink и retain cycle. CADisplayLink удерживает target сильной ссылкой. Если target — UIViewController, и вы забыли invalidate() в viewDidDisappear, контроллер никогда не освободится. Альтернатива — weak proxy через NSProxy.
  • Анимация после layoutSubviews. Если изменить frame вью внутри блока анимации, а затем вызвать setNeedsLayout — AutoLayout пересчитает frame синхронно и отменит анимацию. Анимируйте constraint constant, а не frame напрямую.
  • preferredFramesPerSecond устарел. С iOS 15 используйте CAFrameRateRange. Старое API игнорируется на ProMotion-устройствах с адаптивной частотой.
  • Подводные камни
    • Проверяйте корректность данных после каждой операции.

Частые вопросы на собеседовании

  • Что такое Auto Layout и как создавать constraints программно по сравнению с Interface Builder?
  • Как работает навигация в UIKit с помощью UINavigationController?
  • В чём разница между UIView и UIViewController?
  • Как работает жизненный цикл UIViewController? Опишите каждый lifecycle-метод.

Типичные ошибки

  • Сводить «Core Animation и как создавать базовые анимации в UIKit» к синтаксису и не объяснять responder chain.
  • Игнорировать жизненный цикл, основной поток или момент освобождения ресурсов в сценарии uikit-18.
  • Выбирать API по привычке, не проверяя состояние, ошибки, доступность и платформенные ограничения.

Что проверяет интервьюер

  • Формулирует точную модель для «Core Animation и как создавать базовые анимации в UIKit» и подтверждает ее корректным примером.
  • Умеет связать ответ с reuse pool, тестированием и отладкой на устройстве.
  • Называет ограничения подхода uikit-18, включая производительность, память и сопровождение.

Как вы знаете этот вопрос?

Проверить себя по теме UIKit →

Источники

  • https://developer.apple.com/documentation/quartzcore

Связанные темы

UIKitimplementation-reasoningMobile-Engineeringui-rendering-performance