Можно ли создать iOS-приложение используя только CALayer без UIView?

Ответ

Технически возможно, но крайне непрактично. CALayer — это низкоуровневый объект отрисовки без обработки событий и управления жизненным циклом.

Ограничения использования только CALayer:

  • Нет обработки событий — слои не обрабатывают тапы, свайпы, жесты
  • Нет автоматического управления памятью — нужно вручную контролировать retain cycles
  • Нет готовых UI-компонентов — кнопки, текстовые поля, таблицы отсутствуют
  • Сложная анимация — отсутствуют удобные методы анимации UIView

Базовый пример создания слоя:

class LayerOnlyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        // Создание и настройка слоя
        let customLayer = CALayer()
        customLayer.frame = CGRect(x: 50, y: 100, width: 200, height: 150)
        customLayer.backgroundColor = UIColor.systemBlue.cgColor
        customLayer.cornerRadius = 12
        customLayer.borderWidth = 2
        customLayer.borderColor = UIColor.white.cgColor

        // Добавление на экран
        view.layer.addSublayer(customLayer)

        // Проблема: нет обработки тапов на этот слой
        // Решение: добавляем gesture recognizer на view
        let tapRecognizer = UITapGestureRecognizer(target: self, 
                                                   action: #selector(handleTap))
        view.addGestureRecognizer(tapRecognizer)
    }

    @objc func handleTap(_ recognizer: UITapGestureRecognizer) {
        let point = recognizer.location(in: view)
        // Вручную проверяем hit test для каждого слоя
        if let hitLayer = view.layer.hitTest(point) {
            print("Tapped layer: (hitLayer)")
        }
    }
}

Практическая рекомендация: Используйте UIView как контейнер для CALayer. UIView предоставляет:

  • Автоматическое создание и управление backing layer
  • Систему responder chain для обработки событий
  • Удобные методы анимации (UIView.animate)
  • Автоматическое обновление layout при изменении bounds

Гибридный подход (рекомендуется):

class CustomView: UIView {
    override class var layerClass: AnyClass {
        return CAShapeLayer.self // Кастомный тип слоя
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        // Настройка кастомного слоя
        if let shapeLayer = layer as? CAShapeLayer {
            shapeLayer.path = UIBezierPath(roundedRect: bounds, 
                                          cornerRadius: 10).cgPath
        }
    }
}

Ответ 18+ 🔞

А, ну ты глянь, какие вопросы задают! Можно ли на одних только CALayer интерфейс построить? Да, блядь, технически — можно, как же! Это ж просто объект отрисовки, низкоуровневый такой, фундамент. Но это будет как строить дом, используя только кирпичи и цемент, а про двери, окна, проводку и, ёпта, крышу — забыть. Можно, но жить там будет пиздец как неудобно, и каждый раз, когда дверь открыть захочешь, придётся кирпичи разбирать.

Вот смотри, в чём подвох, если только CALayer юзать:

  • События — нихуя. Ты пальцем ткни — а ему похуй. Ни тапов, ни свайпов, ни жестов. Слой просто картинка, он не в курсе, что его трогают.
  • Память — сам разбирайся. За ним уследить надо, чтобы он тебя не увёл в вечный цикл и приложение не накрылось медным тазом.
  • Готовых штук — ноль. Хочешь кнопку? Рисуй сам. Текстовое поле? Рисуй сам. Таблицу? О, ёперный театр, это вообще отдельная песня. Всё с нуля, голыми руками.
  • Анимация — мозг сломаешь. В UIView есть animate(withDuration:) — красота. Тут же всё вручную, через CABasicAnimation, и волнение ебать, чтобы всё гладко было.

Вот, на коленке, пример такого убогого подхода:

class LayerOnlyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        // Делаем слой, красивый такой, синий
        let customLayer = CALayer()
        customLayer.frame = CGRect(x: 50, y: 100, width: 200, height: 150)
        customLayer.backgroundColor = UIColor.systemBlue.cgColor
        customLayer.cornerRadius = 12
        customLayer.borderWidth = 2
        customLayer.borderColor = UIColor.white.cgColor

        // Кидаем его на экран
        view.layer.addSublayer(customLayer)

        // А теперь, сука, проблема: как на него тапнуть?
        // Придётся на всю вьюху жест вешать и потом вручную проверять, куда тыкнули
        let tapRecognizer = UITapGestureRecognizer(target: self, 
                                                   action: #selector(handleTap))
        view.addGestureRecognizer(tapRecognizer)
    }

    @objc func handleTap(_ recognizer: UITapGestureRecognizer) {
        let point = recognizer.location(in: view)
        // И вот тут начинается цирк: проверяем для каждого слоя, попали ли в него
        if let hitLayer = view.layer.hitTest(point) {
            print("Тыкнули в слой: (hitLayer)") // Ура, работает! Но какой геморрой, блядь!
        }
    }
}

Видишь этот костыль? Это же пиздопроебищно! Каждый чих — своя подпорка.

Так что слушай сюда, умный совет: не выёбывайся. Используй UIView как нормальную, удобную обёртку для твоего CALayer. Зачем? Да потому что вьюха:

  • Сама создаст и будет рулить своим слоем.
  • Сама разберётся с тапами и прочими жестами (responder chain, ёпта!).
  • Даст тебе красивые и простые анимации.
  • Когда размер поменяется (bounds), сама всё пересчитает и перерисует, если надо.

Вот как по-умному, гибридно сделать:

class CustomView: UIView {
    // Говорим системе: "Друг, вместо стандартного слоя, дай мне кастомный, CAShapeLayer"
    override class var layerClass: AnyClass {
        return CAShapeLayer.self
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        // А тут, когда вьюха устаканилась, настраиваем наш кастомный слой
        if let shapeLayer = layer as? CAShapeLayer {
            shapeLayer.path = UIBezierPath(roundedRect: bounds, 
                                          cornerRadius: 10).cgPath
        }
    }
}

Вот это — мощь! Ты получаешь всю мощь низкоуровневого CALayer (в данном случае CAShapeLayer) для отрисовки, но при этом живешь в уютном мире UIView со всеми его плюшками. И тебе не нужно, блядь, вручную тапы ловить и память считать. Красота, а не жизнь.