SwiftUI для Swift: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Что такое SwiftUI и зачем он нужен?

SwiftUI — это современный, декларативный фреймворк для построения пользовательских интерфейсов на всех платформах Apple: iOS, macOS, watchOS и tvOS. В отличие от традиционного подхода UIKit (где вы вручную управляете состоянием и обновлением вьюх), SwiftUI позволяет описывать, каким должен быть интерфейс, а не как его построить. Фреймворк автоматически отслеживает изменения данных и перерисовывает только те части интерфейса, которые изменились.

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

  • Меньше кода: Один и тот же код работает на всех платформах Apple.
  • Декларативность: Вы описываете интерфейс как набор простых структур, а не как последовательность команд.
  • Автоматическое управление состоянием: @State, @Binding, @ObservableObject — SwiftUI сам решает, когда перерисовывать интерфейс.
  • Живой превью: В Xcode вы видите изменения в реальном времени.
  • Анимации: Добавление анимаций — это одна строка кода.

SwiftUI — это не библиотека, которую нужно устанавливать отдельно. Это встроенный фреймворк, доступный с iOS 13, macOS 10.15, watchOS 6 и tvOS 13. Он поставляется вместе с Xcode и SDK Apple.

Установка (Настройка проекта)

Поскольку SwiftUI — это часть стандартного SDK Apple, его не нужно устанавливать через pip, npm или CocoaPods. Всё, что вам нужно — это Xcode (версия 11 или новее) и проект, настроенный на iOS 13+ / macOS 10.15+.

Как создать новый проект с SwiftUI:

  1. Откройте Xcode.
  2. Выберите File → New → Project.
  3. Выберите шаблон "App" (или "SwiftUI App" в более старых версиях).
  4. Убедитесь, что в поле "Interface" выбран "SwiftUI".
  5. Нажмите "Next" и создайте проект.

Если вы работаете с существующим проектом UIKit, вы можете добавить SwiftUI файл (File → New → File → SwiftUI View). SwiftUI и UIKit могут сосуществовать в одном проекте через UIHostingController.

Быстрый старт с примером кода

Давайте создадим простое приложение "Счётчик кликов". Это классический пример, который показывает базовые концепции SwiftUI: состояние, кнопки и текст.

import SwiftUI

struct ContentView: View {
    // @State — это свойство, которое хранит состояние и автоматически перерисовывает View при его изменении
    @State private var count = 0

    var body: some View {
        VStack(spacing: 20) {
            Text("Счётчик: \(count)")
                .font(.largeTitle)
                .padding()

            HStack(spacing: 20) {
                Button("Увеличить") {
                    count += 1
                }
                .padding()
                .background(Color.blue)
                .foregroundColor(.white)
                .cornerRadius(10)

                Button("Сбросить") {
                    count = 0
                }
                .padding()
                .background(Color.red)
                .foregroundColor(.white)
                .cornerRadius(10)
            }
        }
    }
}

// Точка входа в приложение
@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Объяснение:

  • @State — говорит SwiftUI, что это свойство нужно отслеживать. При изменении count, SwiftUI автоматически перерисовывает ContentView.
  • VStack и HStack — это контейнеры для вертикального и горизонтального расположения элементов.
  • Button — создаёт кнопку, которая выполняет замыкание при нажатии.
  • Модификаторы (.font(), .padding(), .background()) — это способ настройки внешнего вида.

Основные методы и функции SwiftUI (минимум 10)

1. @State

Что делает: Позволяет объявить изменяемое свойство, которое является источником истины для данного View. Когда значение меняется, View перерисовывается.

Параметры: Просто объявляется как @State private var имя: Тип = значение. Рекомендуется использовать private для инкапсуляции.

struct ExampleView: View {
    @State private var isOn = false

    var body: some View {
        Toggle("Включено", isOn: $isOn)
    }
}

2. @Binding

Что делает: Создаёт двустороннюю связь с состоянием, которое принадлежит другому View. Позволяет дочернему View изменять состояние родительского.

Параметры: Используется с символом $ для передачи ссылки на состояние.

struct ParentView: View {
    @State private var text = ""

    var body: some View {
        ChildView(text: $text)
    }
}

struct ChildView: View {
    @Binding var text: String

    var body: some View {
        TextField("Введите текст", text: $text)
    }
}

3. VStack, HStack, ZStack

Что делает: Контейнеры для компоновки дочерних вьюх. VStack — вертикально, HStack — горизонтально, ZStack — слоями (один поверх другого).

Параметры: spacing (расстояние между элементами), alignment (выравнивание).

struct StackExample: View {
    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            Text("Верх")
            HStack {
                Text("Левый")
                Text("Правый")
            }
            ZStack {
                Color.red
                Text("Поверх красного")
            }
            .frame(height: 100)
        }
    }
}

4. Text

Что делает: Отображает статический или динамический текст с возможностью форматирования.

Параметры: Строка или LocalizedStringKey для локализации.

struct TextExample: View {
    var body: some View {
        Text("Hello, World!")
            .font(.title)
            .fontWeight(.bold)
            .foregroundColor(.blue)
            .multilineTextAlignment(.center)
            .lineLimit(2)
    }
}

5. Image

Что делает: Отображает изображение из Assets, SF Symbols или загруженное из сети.

Параметры: Имя изображения (String) или системное имя SF Symbol.

struct ImageExample: View {
    var body: some View {
        Image(systemName: "star.fill")
            .resizable()
            .frame(width: 50, height: 50)
            .foregroundColor(.yellow)
    }
}

6. Button

Что делает: Создаёт интерактивную кнопку, которая выполняет действие при нажатии.

Параметры: action (замыкание), label (вьюха для отображения).

struct ButtonExample: View {
    @State private var message = ""

    var body: some View {
        VStack {
            Text(message)
            Button("Нажми меня") {
                message = "Кнопка нажата!"
            }
            .buttonStyle(.borderedProminent)
        }
    }
}

7. List

Что делает: Отображает прокручиваемый список данных. Аналог UITableView в UIKit.

Параметры: Массив данных, замыкание для создания строки.

struct ListExample: View {
    let fruits = ["Яблоко", "Банан", "Апельсин", "Киви"]

    var body: some View {
        List(fruits, id: \.self) { fruit in
            Text(fruit)
        }
    }
}

8. NavigationStack (или NavigationView для старых версий)

Что делает: Создаёт навигационный стек для перехода между экранами.

Параметры: Корневая вьюха и NavigationLink для переходов.

struct NavigationExample: View {
    var body: some View {
        NavigationStack {
            List(1..<10) { i in
                NavigationLink("Элемент \(i)", value: i)
            }
            .navigationDestination(for: Int.self) { value in
                Text("Детали для \(value)")
                    .font(.largeTitle)
            }
            .navigationTitle("Список")
        }
    }
}

9. @ObservableObject и @Published

Что делает: Позволяет создать внешнюю модель данных (ViewModel), которая может быть разделена между несколькими View.

Параметры: Класс, наследующий от ObservableObject, и свойства с @Published.

class UserSettings: ObservableObject {
    @Published var username = "Гость"
}

struct ProfileView: View {
    @ObservedObject var settings = UserSettings()

    var body: some View {
        Text("Привет, \(settings.username)!")
        TextField("Имя", text: $settings.username)
    }
}

10. GeometryReader

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

Параметры: Замыкание, которое получает объект GeometryProxy.

struct GeometryReaderExample: View {
    var body: some View {
        GeometryReader { geometry in
            VStack {
                Text("Ширина: \(geometry.size.width)")
                Text("Высота: \(geometry.size.height)")
            }
            .frame(width: geometry.size.width / 2, height: geometry.size.height / 2)
            .background(Color.green)
        }
        .frame(height: 200)
    }
}

11. ForEach

Что делает: Итерация по коллекции для динамического создания вьюх.

Параметры: Массив данных, идентификатор (id) и замыкание для построения вьюхи.

struct ForEachExample: View {
    let colors: [Color] = [.red, .green, .blue]

    var body: some View {
        HStack {
            ForEach(colors, id: \.self) { color in
                RoundedRectangle(cornerRadius: 10)
                    .fill(color)
                    .frame(width: 50, height: 50)
            }
        }
    }
}

Реальный практический пример: Приложение "Список задач" (To-Do List)

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

import SwiftUI

// Модель данных
struct Task: Identifiable {
    let id = UUID()
    var title: String
    var isCompleted: Bool = false
}

// ViewModel
class TaskViewModel: ObservableObject {
    @Published var tasks: [Task] = []

    func addTask(title: String) {
        tasks.append(Task(title: title))
    }

    func toggleTask(_ task: Task) {
        if let index = tasks.firstIndex(where: { $0.id == task.id }) {
            tasks[index].isCompleted.toggle()
        }
    }

    func deleteTask(at offsets: IndexSet) {
        tasks.remove(atOffsets: offsets)
    }
}

// Главный экран
struct TaskListView: View {
    @StateObject private var viewModel = TaskViewModel()
    @State private var newTaskTitle = ""

    var body: some View {
        NavigationStack {
            VStack {
                // Поле для ввода новой задачи
                HStack {
                    TextField("Новая задача", text: $newTaskTitle)
                        .textFieldStyle(.roundedBorder)
                    Button("Добавить") {
                        guard !newTaskTitle.isEmpty else { return }
                        viewModel.addTask(title: newTaskTitle)
                        newTaskTitle = ""
                    }
                    .buttonStyle(.borderedProminent)
                }
                .padding()

                // Список задач
                List {
                    ForEach(viewModel.tasks) { task in
                        HStack {
                            Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle")
                                .foregroundColor(task.isCompleted ? .green : .gray)
                                .onTapGesture {
                                    viewModel.toggleTask(task)                    

Рекомендаций