Swift | Вопросы собесов
2.13K subscribers
28 photos
953 links
Download Telegram
🤔 Какие способы передачи данных есть?

1. Прямой вызов: через свойства или методы объекта.
2. Делегаты: передача данных через протоколы.
3. Замыкания: использование callback-функций.
4. Уведомления: через NotificationCenter.
5. Кросс-контроллеры: с использованием segue или Dependency Injection.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Состояние сцены и переход (Scene phases and transitions)?

Сцены и фазы переходов (Scene phases and transitions) — важные аспекты управления состоянием приложения в SwiftUI, особенно когда речь идет о многозадачности и жизненном цикле приложения. SwiftUI предоставляет механизмы для обработки этих состояний и переходов через специальные property wrappers и API.

🚩Состояние сцены (Scene Phases)

🟠active
Приложение активно и отображает контент на экране.
🟠inactive
Приложение находится на переднем плане, но не взаимодействует с пользователем (например, при входящем вызове).
🟠background
Приложение находится в фоне и не отображается на экране.

Пример использования @Environment(\.scenePhase)
import SwiftUI

struct ContentView: View {
@Environment(\.scenePhase) var scenePhase

var body: some View {
Text("Привет, мир!")
.onChange(of: scenePhase) { newPhase in
switch newPhase {
case .active:
print("Сцена активна")
case .inactive:
print("Сцена неактивна")
case .background:
print("Сцена в фоне")
@unknown default:
print("Неизвестное состояние сцены")
}
}
}
}


🚩Переходы между фазами сцены (Transitions)

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

Для приложений, использующих UIKit и SwiftUI вместе, обработка фаз сцены может выполняться в классе SceneDelegate.
import UIKit
import SwiftUI

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let contentView = ContentView()
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}

func sceneDidBecomeActive(_ scene: UIScene) {
// Сцена стала активной
print("Сцена активна")
}

func sceneWillResignActive(_ scene: UIScene) {
// Сцена станет неактивной
print("Сцена неактивна")
}

func sceneDidEnterBackground(_ scene: UIScene) {
// Сцена перешла в фоновый режим
print("Сцена в фоне")
}

func sceneWillEnterForeground(_ scene: UIScene) {
// Сцена перейдет в передний план
print("Сцена на переднем плане")
}
}


🚩Плюсы

Сохранение состояния
Позволяет сохранять важные данные, когда приложение переходит в фоновый режим, и восстанавливать их при возвращении.
Оптимизация ресурсов
Позволяет освобождать ресурсы, когда приложение не активно, и выделять их, когда оно становится активным.
Улучшение UX
Обеспечивает плавный и предсказуемый опыт для пользователя при смене состояний приложения.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какие виды диспетчеризации существуют Static Dispatch: метод вызова известен на этапе компиляции?

1. Dynamic Dispatch: метод определяется во время выполнения через таблицу виртуальных функций (vtable).
2. Direct Dispatch: прямой вызов метода без участия vtable.
3. Protocol Witness Table (PWT): используется для вызовов методов, реализованных через протоколы.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какие модификаторы изменяют View life cycle events?

В SwiftUI существует несколько модификаторов, которые позволяют изменять и обрабатывать события жизненного цикла вью. Эти модификаторы помогают реагировать на различные состояния представления и управлять его поведением.

🟠`onAppear`
Выполняет действие, когда представление появляется на экране.
🟠`onDisappear`
Выполняет действие, когда представление исчезает с экрана.
🟠`task`
Выполняет асинхронное действие при появлении представления на экране.
🟠`onChange`
Реагирует на изменения определенного состояния или значения.
🟠`onReceive`
Реагирует на обновления из Publisher.

🚩Модификаторы жизненного цикла представлений

🟠onAppear
onAppear выполняет действие, когда представление появляется на экране.
struct ContentView: View {
var body: some View {
Text("Привет, мир!")
.onAppear {
print("Представление появилось на экране")
}
}
}


🟠onDisappear
onDisappear выполняет действие, когда представление исчезает с экрана.
struct ContentView: View {
var body: some View {
Text("Привет, мир!")
.onDisappear {
print("Представление исчезло с экрана")
}
}
}


🟠task
task выполняет асинхронное действие при появлении представления на экране. Это полезно для загрузки данных или выполнения других асинхронных задач.
struct ContentView: View {
var body: some View {
Text("Привет, мир!")
.task {
await loadData()
}
}

func loadData() async {
// Асинхронная загрузка данных
print("Данные загружаются")
}
}


🟠onChange
onChange реагирует на изменения определенного состояния или значения.
struct ContentView: View {
@State private var counter = 0

var body: some View {
VStack {
Text("Счётчик: \(counter)")
Button("Увеличить счётчик") {
counter += 1
}
}
.onChange(of: counter) { newValue in
print("Счётчик изменился на \(newValue)")
}
}
}


🟠onReceive
onReceive реагирует на обновления из Publisher. Это полезно для обработки данных из Combine.
import Combine

struct ContentView: View {
@State private var data: String = "Загрузка..."
let publisher = PassthroughSubject<String, Never>()

var body: some View {
Text(data)
.onReceive(publisher) { value in
data = value
}
}
}


🚩Плюсы

Управление состоянием
Легко управлять состоянием представлений в различные моменты их жизненного цикла.
Асинхронные задачи
Выполнение асинхронных задач, таких как загрузка данных, при появлении представлений на экране.
Обработка изменений
Реакция на изменения состояний и значений в представлении.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Зачем нужен словарь?

Словарь (dictionary) используется для хранения данных в формате ключ-значение. Это позволяет быстро искать, добавлять и удалять элементы по ключу, обеспечивая оптимальную производительность (обычно O(1)).

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Преимущества и недостатки синхронного и асинхронного соединения?

Синхронные и асинхронные соединения имеют свои уникальные преимущества и недостатки. Понимание этих различий важно для правильного выбора подхода в зависимости от контекста и требований приложения.

🚩Синхронное соединение

Плюсы
Простота реализации
Синхронные операции проще понимать и реализовывать, так как они выполняются последовательно.

Легкость отладки
Отладка синхронного кода проще, так как последовательность выполнения предсказуема.

Определённость выполнения
Синхронный код выполняется по порядку, что делает логику более прозрачной.

Минусы
Блокировка ресурсов
Синхронные операции могут блокировать основной поток выполнения, что приводит к замедлению работы интерфейса или приложения в целом.

Низкая отзывчивость
Приложения с синхронными операциями могут становиться менее отзывчивыми, так как долгие операции могут замораживать пользовательский интерфейс.

Меньшая масштабируемость
Синхронный подход может ограничивать возможности масштабирования, так как каждая операция ожидает завершения предыдущей.

🚩Асинхронное соединение

Плюсы

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

Отзывчивость интерфейса
Асинхронный подход позволяет поддерживать отзывчивость пользовательского интерфейса, так как долгие операции выполняются в фоновом режиме.

Масштабируемость
Асинхронный код лучше масштабируется, так как позволяет обрабатывать множество запросов одновременно.

Минусы

Сложность реализации
Асинхронные операции требуют более сложной реализации и понимания концепций, таких как колбэки, промисы или async/await.

Сложность отладки
Отладка асинхронного кода может быть сложнее из-за непредсказуемой последовательности выполнения.

Неопределенность состояния
Асинхронные операции могут приводить к неопределенным состояниям, если не обрабатывать их правильно, что может привести к трудноуловимым ошибкам.

🚩Пример в Swift

Синхронный пример
func fetchDataSynchronously() {
let url = URL(string: "https://api.example.com/data")!
let data = try? Data(contentsOf: url)
if let data = data {
print("Данные загружены: \(data)")
} else {
print("Ошибка загрузки данных")
}
}


Асинхронный пример
func fetchDataAsynchronously() {
let url = URL(string: "https://api.example.com/data")!
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
print("Данные загружены: \(data)")
} else if let error = error {
print("Ошибка загрузки данных: \(error)")
}
}.resume()
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое final?

Это модификатор, который предотвращает изменения:
- Класс: нельзя наследовать.
- Метод: нельзя переопределять.
- Переменная: значение нельзя изменить после инициализации.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Можно ли имитировать множественное наследование в свифте?

Не поддерживает множественное наследование для классов, что является общим выбором в современных объектно-ориентированных языках программирования из-за сложностей и проблем, которые множественное наследование может создать (например, проблема алмаза смерти). Однако предлагаются альтернативные механизмы, позволяющие имитировать некоторые аспекты множественного наследования.

🟠Протоколы
Похожи на интерфейсы в других языках. Они позволяют классам, структурам и перечислениям принимать "контракты", которые определяют методы и свойства, которые должны быть реализованы. Протоколы могут наследоваться и расширяться, предоставляя гибкий способ добавления функциональности к типам.
protocol Movable {
func move()
}

protocol Drawable {
func draw()
}

class Character: Movable, Drawable {
func move() {
print("Персонаж двигается")
}

func draw() {
print("Персонаж рисуется")
}
}


🟠Расширения
Позволяют добавлять новую функциональность к существующим типам, включая те, которые определены в стандартной библиотеке или внешних библиотеках. Хотя расширения не могут добавлять хранимые свойства, они могут добавлять новые вычисляемые свойства, методы и протоколы.
extension Character: Drawable {
func draw() {
print("Персонаж рисуется через расширение")
}
}


🟠Композиция
Это альтернативный подход к наследованию, при котором один тип включает другой как часть своей реализации, вместо того чтобы наследовать от него. Это предпочтительный метод для достижения повторного использования кода, позволяющий избежать ограничений и сложностей наследования.
class Engine {
func start() {
print("Двигатель запущен")
}
}

class Car {
let engine = Engine()

func startEngine() {
engine.start()
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
1
🤔 В чём разница между View и Layer?

- View: отображает контент на экране и управляет взаимодействием с пользователем.
- Layer: низкоуровневый компонент (из Core Animation), который отвечает за рендеринг содержимого и может использоваться для сложных анимаций или оптимизаций.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2👍1
🤔 Что известно про capture list?

Используется в замыканиях (closures) для управления тем, как замыкание захватывает переменные и константы из окружающего контекста. Могут захватывать и хранить ссылки на любые константы и переменные из контекста, в котором они определены. Это удобно, но может привести к сильным ссылочным циклам и утечкам памяти, если замыкание захватывает self или другие экземпляры класса.

🚩Как он работает

Предоставляет способ определить правила захвата переменных в замыкании. Она задаётся в начале замыкания и позволяет избежать нежелательных сильных ссылок, особенно при работе с self в методах класса, что очень важно для предотвращения утечек памяти в приложениях.

Синтаксис Capture List
{ [capture rule] (parameters) -> return type in
// Код замыкания
}


Пример с простым замыканием
var a = 0
var b = 0
let closure = { [a] in
print(a, b)
}

a = 10
b = 10
closure() // Выведет "0 10"


Использование Capture List для предотвращения сильных ссылочных циклов
class MyClass {
var property = "Property"

func doSomething() {
let closure = { [weak self] in
print(self?.property ?? "нет self")
}
closure()
}

deinit {
print("MyClass экземпляр был деинициализирован")
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
2
🤔 Когда value type может храниться в куче?

Value type может быть размещён в куче, если он используется внутри reference type, например, передан как свойство объекта класса.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какую структуру имеет диспетчирезацию?

Термин "диспетчеризация" часто используется для описания механизма, посредством которого вызывается метод или функция в ответ на вызов. В контексте ООП, особенно в языках с поддержкой полиморфизма, диспетчеризация может быть статической (ранней) или динамической (поздней).

🚩Статическая диспетчеризация (Static Dispatch)

Использует компиляцию времени компиляции для определения того, какой метод будет вызван. Это означает, что компилятор определяет адрес вызываемого метода на этапе компиляции, и этот адрес не изменяется во время выполнения программы.

🟠Плюсы
Быстродействие: Так как адрес метода известен на этапе компиляции, нет необходимости в дополнительных проверках или вычислениях во время выполнения.
Простота: Проще для оптимизации компилятором.

🟠Структура
В языках, таких как C и Swift (при использовании final классов или структур), методы, известные во время компиляции, могут быть вызваны без дополнительной нагрузки, связанной с поиском в таблице виртуальных функций.

🚩Динамическая диспетчеризация (Dynamic Dispatch)

Используется в языках программирования, поддерживающих полиморфизм и наследование, и означает, что метод, который будет вызван, определяется во время выполнения. Это позволяет объектам различных классов отвечать на одни и те же сообщения (вызовы методов), каждый по-своему.

🟠Плюсы
Гибкость: Позволяет объектам разных классов обрабатывать одинаковые вызовы методов различным образом.
Поддержка полиморфизма: Один и тот же вызов метода может вести к выполнению разных функций.

🟠Структура
Таблица виртуальных методов (VMT): В объектно-ориентированных языках, таких как C++ или Java, каждый класс с виртуальными методами имеет таблицу виртуальных методов. Эта таблица содержит адреса всех виртуальных методов, которые могут быть вызваны для объекта данного класса. Поиск по таблице: Во время выполнения, когда вызывается метод, производится поиск соответствующего метода в таблице VMT, и используется адрес, найденный в таблице, что вносит задержку по сравнению со статической диспетчеризацией.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Чем отличаются слабые и сильные ссылки?

- Сильные ссылки (strong): увеличивают счётчик ссылок объекта, предотвращая его удаление.
- Слабые ссылки (weak): не увеличивают счётчик ссылок, используются для предотвращения циклических зависимостей.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥1
🤔 Как устроено наследование?

Это ключевой механизм ООП, позволяющий классам наследовать свойства, методы и другие характеристики от других классов. Это позволяет создавать новые классы на основе существующих, расширяя их функциональность или изменяя её.

🚩Основы наследования

🟠Определение базового класса
Базовый класс определяет общие свойства и методы, которые могут быть унаследованы подклассами.

🟠Создание подкласса
Подкласс наследует (или "расширяет") базовый класс. Он может переопределять унаследованные методы и свойства, добавлять новые методы и свойства, а также добавлять инициализаторы или изменять существующие.

🟠Переопределение методов и свойств
Подклассы могут переопределять методы, свойства и индексаторы базового класса для изменения или расширения их поведения.

🟠Предотвращение переопределения
Можно предотвратить переопределение методов, свойств или индексаторов с помощью ключевого слова final. Если метод, свойство или индексатор объявлен как final, то он не может быть переопределён в подклассе.
class Vehicle {
var currentSpeed = 0.0
var description: String {
return "traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
// Этот метод будет переопределен в подклассах, если необходимо
}
}

class Bicycle: Vehicle {
var hasBasket = false
}

class Car: Vehicle {
var gear = 1
final func drive() {
print("Car is moving")
}
override func makeNoise() {
print("Vroom!")
}
}


🚩Использование super

Подклассы могут вызывать методы своего суперкласса с помощью ключевого слова super. Это позволяет подклассам расширять, а не заменять поведение суперкласса.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое Grand Central Dispatch (GCD)?

Это технология для работы с многопоточностью в iOS и macOS. Она управляет очередями задач (sync/async, serial/concurrent) и позволяет эффективно распределять задачи между потоками.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 За счёт чего стек быстрее кучи?

Обычно имеют в виду различия между использованием стека вызовов (память стека) и кучи (heap) в контексте управления памятью в программировании. Эти два типа памяти имеют разные способы выделения и освобождения памяти, каждый со своими преимуществами и недостатками.

🟠Способ выделения памяти
Память в стеке выделяется и освобождается по очень простому и быстрому принципу: LIFO (Last In, First Out). Это означает, что для выделения памяти достаточно переместить указатель стека вверх (при добавлении данных) или вниз (при удалении данных). Этот процесс почти мгновенен и не требует дополнительных вычислений.
В куче память выделяется динамически, что требует управления доступными блоками памяти. Аллокатор памяти должен найти достаточно большой свободный блок, что может занять значительное время, особенно если память фрагментирована. Также освобождение памяти в куче требует более сложной обработки, включая возможную дефрагментацию.

🟠Скорость доступа
Доступ к данным в стеке очень быстрый, потому что данные всегда добавляются и удаляются с "вершины" стека, где находится указатель стека. Это делает доступ к текущим локальным переменным очень быстрым и предсказуемым. Доступ к данным в куче может быть менее эффективным, поскольку данные могут быть разбросаны по разным частям памяти. Кроме того, дополнительное время требуется для поиска и управления блоками памяти.

🟠Детерминированное управление памятью
Его память автоматически очищается при выходе из области видимости, что упрощает управление памятью и снижает риск утечек памяти. Память, выделенная в куче, остаётся занятой до тех пор, пока явно не будет освобождена. Это увеличивает риск утечек памяти, если разработчик забудет освободить память.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
🤔 Что под капотом кучи?

Это область памяти, управляемая системой, где объекты выделяются динамически. Управление включает:
- Аллокацию памяти.
- Освобождение через сборщик мусора (в Java, Swift) или вручную (в C++).
- Компактирование для предотвращения фрагментации.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2🤯2👍1
🤔 Что под капотом стэка?

Это абстрактная структура данных, работающая по принципу LIFO (Last In, First Out), что означает "последний пришёл — первый вышел". Это значит, что последний добавленный элемент будет первым при извлечении из стека. Под капотом реализации стека могут быть разные, и они зависят от конкретного языка программирования и задач, которые необходимо решить.

🟠Массивы
Один из самых распространённых способов реализации стека — это использование массива. В такой реализации элементы стека хранятся в массиве, и индекс последнего элемента (вершина стека) отслеживается отдельной переменной.
struct Stack<Element> {
private var storage: [Element] = []

mutating func push(_ element: Element) {
storage.append(element)
}

mutating func pop() -> Element? {
return storage.popLast()
}

func peek() -> Element? {
return storage.last
}

var isEmpty: Bool {
return storage.isEmpty
}
}


🟠Связные списки
Стек можно реализовать с использованием связных списков, где каждый элемент списка содержит данные и ссылку на следующий элемент в стеке. Вершина стека в такой реализации — это начало связного списка.
class Node<Element> {
var value: Element
var next: Node?

init(value: Element) {
self.value = value
}
}

struct Stack<Element> {
private var head: Node<Element>?

mutating func push(_ element: Element) {
let node = Node(value: element)
node.next = head
head = node
}

mutating func pop() -> Element? {
let node = head
head = head?.next
return node?.value
}

func peek() -> Element? {
return head?.value
}

var isEmpty: Bool {
return head == nil
}
}


🟠Стек вызовов
Это системный стек, который используется во время выполнения программы для хранения информации о вызовах функций/методов. Он хранит адреса возврата, параметры функций, локальные переменные и другие данные, необходимые для управления вызовами функций и их возврата.

🚩Зачем он нужен?

Обратную польскую нотацию для вычисления арифметических выражений. Управление вызовами функций в программном стеке. Поддержка операций undo в приложениях.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
4
🤔 Когда value типы могут стать reference?

Value типы становятся reference, если они упакованы (например, через Box в Swift) или если они хранятся в контейнерах, которые управляются ссылочным механизмом (например, массивы при копировании).

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как создаешь анимации в приложении?

В iOS-приложениях можно создавать анимации несколькими способами.

🟠Использование `UIView.animate`
Самый простой способ анимации представлений (views) в iOS - это использование метода UIView.animate. Вот пример кода, который изменяет положение и прозрачность представления за 1 секунду:
UIView.animate(withDuration: 1.0) {
myView.frame.origin.y += 100
myView.alpha = 0.5
}


🟠Использование `CABasicAnimation`
Для более сложных анимаций можно использовать Core Animation, например, CABasicAnimation. Вот пример анимации изменения позиции слоя (layer):
let animation = CABasicAnimation(keyPath: "position")
animation.fromValue = NSValue(cgPoint: CGPoint(x: 50, y: 50))
animation.toValue = NSValue(cgPoint: CGPoint(x: 150, y: 150))
animation.duration = 1.0
myView.layer.add(animation, forKey: "positionAnimation")


🟠Использование `UIViewPropertyAnimator`
Этот класс предоставляет более детальный контроль над анимациями. Его можно использовать для создания и управления анимациями в реальном времени:
let animator = UIViewPropertyAnimator(duration: 1.0, curve: .easeInOut) {
myView.frame.origin.y += 100
myView.alpha = 0.5
}
animator.startAnimation()


🟠Использование анимаций с пружинным эффектом
Для создания реалистичных анимаций с пружинным эффектом можно использовать метод UIView.animate с параметрами пружинного демпфирования:
UIView.animate(withDuration: 1.0,
delay: 0,
usingSpringWithDamping: 0.5,
initialSpringVelocity: 0.5,
options: [],
animations: {
myView.frame.origin.y += 100
myView.alpha = 0.5
}, completion: nil)


🟠Анимация переходов между контроллерами
Для анимации переходов между экранами используется UIViewControllerAnimatedTransitioning. Это требует реализации методов протокола UIViewControllerAnimatedTransitioning:
class CustomTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.5
}

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromView = transitionContext.view(forKey: .from),
let toView = transitionContext.view(forKey: .to) else { return }

transitionContext.containerView.addSubview(toView)
toView.alpha = 0.0

UIView.animate(withDuration: 0.5, animations: {
toView.alpha = 1.0
}, completion: { finished in
transitionContext.completeTransition(finished)
})
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое UIResponder?

Это базовый класс для обработки событий в UIKit. Объекты типа UIView, UIViewController, UIApplication наследуются от UIResponder и участвуют в цепочке обработки событий, таких как нажатия, свайпы или жесты.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM