Диалоги подтверждения в SwiftUI

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

Третья версия SwiftUI принесла нам несколько модификаторов представления (view modifiers), которые позволяют нам одинаково обрабатывать семантически похожие операции для разных представлений. Например одним из таких модификаторов представления является onSubmit, который мы можем использовать для управления как формами, так и полями поиска (search fields). На этой неделе мы поговорим о другом модификаторе представления, который SwiftUI предоставляет нам для отображения диалоговых окон подтверждения (confirmationDialog).

Диалог подтверждения (confirmation dialog) — это очень распространенный шаблон UI/UX, который мы обычно используем для подтверждения любых опасных действий в наших приложениях. Например, мы можем выводить диалоговое окно подтверждения перед удалением каких-либо конфиденциальных данных из приложения.

struct ContentView: View {
    @StateObject var viewModel = ViewModel()

    var body: some View {
        NavigationView {
            List {
                ForEach(viewModel.messages, id: \.self) { message in
                    Text(message)
                        .swipeActions {
                            Button(
                                role: .destructive,
                                action: { 
                                    withAnimation {
                                        viewModel.delete(message) 
                                    } 
                                }
                            ) {
                                Image(systemName: "trash")
                            }
                        }
                }
            }
            .navigationTitle("Messages")
            .onAppear { viewModel.fetch() }
        }
    }
}

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

struct ContentView: View {
    @StateObject var viewModel = ViewModel()
    @State private var confirmationShown = false

    var body: some View {
        NavigationView {
            List {
                ForEach(viewModel.messages, id: \.self) { message in
                    Text(message)
                        .swipeActions {
                            Button(
                                role: .destructive,
                                action: { confirmationShown = true }
                            ) {
                                Image(systemName: "trash")
                            }
                        }
                        .confirmationDialog(
                            "Are you sure?",
                             isPresented: $confirmationShown
                        ) { 
                            Button("Yes") {
                                withAnimation { 
                                    viewModel.delete(message) 
                                }
                            }
                        }
                }
            }
            .navigationTitle("Messages")
            .onAppear { viewModel.fetch() }
        }
    }
}

Мы добавляем модификатор confirmationDialog к представлению Text, которое мы хотим удалить. Для отображения диалогового окна подтверждения потребуется несколько параметров:

  1. Первый - это заголовок конкретного диалогового окна подтверждения. Это может быть Text или LocalizedStringKey.

  2. Второй - привязка к логическому значению, которое указывает, когда следует отображать диалоговое окно подтверждения.

  3. Третий - это замыкание @ViewBuilder, которое мы можем использовать для предоставления доступных действий с помощью представлений кнопок. Имейте в виду, что мы можем использовать только кнопки с текстом.

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

.confirmationDialog("Are you sure?", isPresented: $confirmationShown) {
    Button("Yes") {
        withAnimation { 
            viewModel.delete(message) 
        }
    }

    Button("No", role: .cancel) {}
}

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

Я должен упомянуть, что система может переупорядочивать кнопки в зависимости от их ролей и важности. SwiftUI применяет более высокий приоритет для действия по умолчанию. Вы можете сделать любое из предоставленных действий действием по умолчанию, используя модификатор представления keyboardShortcut.

.confirmationDialog("Are you sure?", isPresented: $confirmationShown) {
    Button("Yes") {
        withAnimation { 
            viewModel.delete(message) 
        }
    }.keyboardShortcut(.defaultAction)

    Button("No", role: .cancel) {}
}

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

Чтобы узнать больше о всплывающих окнах и списках действий в SwiftUI, почитайте мою статью «Alerts, Action Sheets, Modals and Popovers in SwiftUI».

Модификатор представления confirmationDialog также предоставляет нам возможность контролировать titleVisibility представленного диалога. Параметр titleVisibility принимает инстанс перечисления Visibility с одним из следующих значений: automatic, visible, и hidden.

.confirmationDialog(
    "Are you sure?",
    isPresented: $confirmationShown,
    titleVisibility: .visible
) {
    Button("Yes") {
        withAnimation { 
            viewModel.delete(message) 
        }
    }.keyboardShortcut(.defaultAction)

    Button("No", role: .cancel) {}
}

Мы также можем добавить дополнительное сообщение под заголовком с помощью параметра сообщения, который принимает другое замыкание @ViewBuilder, чтобы создать представление для отображения пользовательского сообщения.

.confirmationDialog(
    "Are you sure?",
    isPresented: $confirmationShown,
    titleVisibility: .visible
) {
    Button("Yes") {
        withAnimation { 
            viewModel.delete(message) 
        }
    }.keyboardShortcut(.defaultAction)

    Button("No", role: .cancel) {}
} message: {
    Text("This action cannot be undone")
}

Модификатор представления confrimationDialog позволяет нам предоставлять дополнительные данные для передачи в замыкания @ViewBuilder как для сообщения, так и для действий.

.confirmationDialog(
    "Are you sure?",
    isPresented: $confirmationShown,
    titleVisibility: .visible,
    presenting: message
) { message in
    Button("Yes, delete: \(message)") {
        withAnimation { 
            viewModel.delete(message) 
        }
    }.keyboardShortcut(.defaultAction)

    Button("No", role: .cancel) {}
} message: { message in
    Text(message)
}

Чтобы узнать больше о преимуществах замыканий ViewBuilder в SwiftUI, читайте мою публикацию «The power of @ViewBuilder in SwiftUI».

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


Материал подготовлен в рамках специализации «iOS Developer».

Всех желающих приглашаем на открытый урок «Новые инструменты Swift, для работы с асинхронностью Async/Away/Actor». На открытом уроке поговорим о новых инструментах Swift 5.5 по работе с асинхронными задачами.

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

РЕГИСТРАЦИЯ

Источник: https://habr.com/ru/company/otus/blog/571570/


Интересные статьи

Интересные статьи

Время от времени мне нужно визуализировать данные в виде красивых графиков. В этой статье будет показано, как рисовать графики в SwiftUI-приложении.Создание пакета для по...
SWAP (своп) — это механизм виртуальной памяти, при котором часть данных из оперативной памяти (ОЗУ) перемещается на хранение на HDD (жёсткий диск), SSD (твёрдотельный накоп...
Стэнфордский университет, США — один из лучших в мире в области информатики (Computer Science). Он щедро делится своими курсами, и одним из самых популярных и успешных курсов является...
Мне было необходимо делать 2 раза в сутки бэкап сайта на «1С-Битрикс: Управление сайтом» (файлов и базы mysql) и хранить историю изменений за 90 дней. Сайт расположен на VDS под уп...
Cтатья будет полезна тем, кто думает какую выбрать CMS для интернет-магазина, сравнивает различные движки, ищет в них плюсы и минусы важные для себя.