Можно ли изменять Auto Layout констрейнты вне основного потока в iOS?

Ответ

Нет, все изменения Auto Layout констрейнтов должны выполняться на главном потоке (main thread). UIKit не является потокобезопасным, и нарушение этого правила приводит к неопределённому поведению или крешам.

Правильный подход:

// Обновление констрейнтов на главном потоке
DispatchQueue.main.async {
    view.widthAnchor.constraint(equalToConstant: 100).isActive = true
    view.heightAnchor.constraint(equalToConstant: 200).isActive = true

    // Принудительное обновление layout, если нужно
    self.view.layoutIfNeeded()
}

Что можно делать в фоновом потоке:

  • Вычисление размеров с помощью systemLayoutSizeFitting()
  • Подготовка данных для констрейнтов
  • Создание NSLayoutConstraint объектов (но не их активация)

Почему это важно:

  1. UIKit обрабатывает все обновления интерфейса в run loop главного потока
  2. Конкурентный доступ к слоям (CALayer) вызывает race conditions
  3. Auto Layout engine не синхронизирован для многопоточного доступа

Ответ 18+ 🔞

О, слушай, смотри, тут такая история, блядь. Все эти ваши констрейнты, Auto Layout — это как тонкая нервная система у интерфейса, понимаешь? И управлять ею можно только из одной, главной комнаты управления. С балкона, с кухни, из туалета — нихуя не получится, только с центрального пульта!

Вот представь: твой UI — это как ёбаный оркестр. А главный поток — это дирижёр, сука. И если каждый музыкант начнёт сам по себе пиликать, когда ему вздумается, из-за кулис, из подсобки — получится не музыка, а пиздец, блядь, какофония. Приложение просто накроется медным тазом, и все, концерт окончен.

Так вот, все команды типа «эй, видят, стань шириной в 100 пикселей» или «подрости до 200» — это прямые указания дирижёру. И давать их нужно, только когда ты стоишь прямо перед ним, на главном потоке, а не кричишь из-за двери, пока он с другим элементом разбирается.

// Делай вот так, по-человечески, на главной сцене
DispatchQueue.main.async {
    view.widthAnchor.constraint(equalToConstant: 100).isActive = true
    view.heightAnchor.constraint(equalToConstant: 200).isActive = true

    // А если совсем припёрло, чтобы сразу всё встало как надо
    self.view.layoutIfNeeded()
}

А то некоторые думают: «ой, да ёбта, я тут в фоне быстренько констрейнт создам, ничего же не происходит». Создать-то создашь, объект в памяти повисит. Но активировать его, впендюрить в живую иерархию вьюх — это уже действие, блядь. Это как взять и прилепить новую руку манекену прямо на складе, пока его несут на витрину. Рука, может, и приклеится, а манекен — хуй знает куда упадёт.

Чем можно заниматься в подполье, в фоновом потоке? Ну, например, прикидывать, каких размеров всему этому безобразию быть, с помощью systemLayoutSizeFitting(). Или данные для этих размеров приготовить. Создать сами объекты NSLayoutConstraint — тоже можно, они же пока просто бумажки с планом. Но вот команду «исполнить!» — отдаёшь только главному дирижёру.

А почему, блядь, так строго?

  1. Весь UIKit — это один большой, сложный, ёбаный механизм, который тикает в такт главному циклу. Подкручивать шестерёнки на ходу, когда он работает, — это прямой путь в креш-логи, в рот меня чих-пых.
  2. Все эти слои, CALayer — они как общаги, где живут пиксели. Если с разных потоков начать туда-сюда мебель двигать, они друг другу ноги оттопчут, race condition наступит, и всё посыпется.
  3. Движок Auto Layout — он не учился работать с многопоточностью. Он один, и он скромный. Не надо его насиловать одновременными запросами из всех щелей.

Короче, запомни, как «Отче наш»: всё, что касается внешнего вида, обновляется ТОЛЬКО на главном потоке. Не хочешь охуенных багов и мистических падений — соблюдай правило, чувак.