Костыли, костыли и ещё раз костыли. Или поддержка ios15 на SwiftUI

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

Итак, есть задача: переделать экран авторизации на новый дизайн, добавить новые способы авторизации. И заодно опять же перейти на SwiftUI. Кстати, у нас довольно объёмный проект, и я думала мы будем очень долго переходить с UIKit, но прошло пол года, а уже треть почти переделана, без особых напрягов.

Так вот, кусочек дизайна выглядит примерно так (все экраны нет смысла показывать, т к в данной статье хочу показать только злополучное модальное окно).

новый дизайн
новый дизайн

Есть: начальный экран с кнопками для входа и модальный экран с согласием на обработку данных. На первый взгляд никакого подвоха, всё должно быть нативно. 

Пишем код основной вью:

struct AuthView: View {
    
    @StateObject private var viewModel = AuthViewModel()
    @StateObject private var agreementViewModel = AuthAgreementModalViewModel()
        
    var body: some View {
        NavigationView {
            VStack(spacing: 0) {
                // Image
                Image("authPicture")
                    
                // Title
                Text("Quick login")
                
                // Кнопки мессенджеров
                VStack(spacing: 8) {
                  ...
                  // в действие по нажатию на кнопку добавляем
                  // появление модального окна:

                  agreementViewModel.showModal = true
                  
                }
                .padding(.top, 24)
                
                Spacer()
                
                // Дополнительный лейбл
                    Text("If you have any problems with authorization, please contact our support service support@domain.com")
                
            }
            .navigationBarHidden(true)
        }
    }
}

Добавляем ViewModel:

class AuthViewModel: ObservableObject {
    @Published vars ...
  
    var completionHandler: (() -> Void)?
    
    func handleLoginAfterAgreement() {
        guard let button = selectedButton else { return }
        handleLogin(for: button)
        selectedButton = nil
    }
    
    func handleLogin(for button: LoginButtonId) {
        switch button {
        case .apple:
            print("Apple ID login tapped")
            startSignInWithAppleFlow()
        case .gmail:
            print("gmail login tapped")
            // логику для gmail
        case .email:
            print("email login tapped")
            // логику для email
        }
    }
    
    func startSignInWithAppleFlow() {
        ...
    }
    
    func onFinish() {
        completionHandler?()
    }
}

Полный код будет на GitHub, там полностью всё расписала, в том числе и регистрацию через AppleID.

По нажатию на одну из кнопок регистрации нужно сначала показать окно с соглашением, и только после согласия пользователя с правилами - производить действие по регистрации.

Собственно, самое простое, как показать модальное окно:

.sheet(isPresented: $agreementViewModel.showModal) {
    AuthAgreementModalView()
}

Но что мы видим при такой имплементации:

Вроде всё норм, но модальное окно не подстраивается по высоте содержимого. Это легко решается с помощью: .presentationDetents

.sheet(isPresented: $agreementViewModel.showModal) {
    AuthAgreementModalView()
        .presentationDetents([.medium, .large]) // Опционально несколько размеров
        .presentationDragIndicator(.visible)   // Показывает индикатор перетаскивания
}

Так же, как в UIKit, сделать, к сожалению, нельзя. То есть нам всё равно придётся примерно выбрать высоту. Но на ios15 даже такое не работает. Поэтому возвращаемся к любимому костылю:

.overlay(
  Group {
    if agreementViewModel.showModal {
      Color(white: 0, opacity: 0.5)
        .edgesIgnoringSafeArea(.all)
      AuthAgreementModalView(viewModel: agreementViewModel, onDismiss: {
      // Обработчик закрытия модального окна
        agreementViewModel.showModal = false
        viewModel.handleLoginAfterAgreement()
      })
      .transition(.move(edge: .bottom))
      .zIndex(1)
    }
  }
)
.animation(.easeInOut, value: agreementViewModel.showModal)

Прописываем прозрачный серый бэкграунд, откуда появляется окно, а так же действие по закрытию. А в сам AuthAgreementModalView добавим скругление верхних углов .cornerRadius(24, corners: [.topLeft, .topRight])

Ну вот и всё, после скрытия модального окна открывается нужная регистрация:

Полный код тут: ссылка на GitHub.

Если интересно, подписывайтесь на мой ТГ, там выкладываю рилсы про разработку: канал тут

Источник: https://habr.com/ru/articles/874258/


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

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

Когда клиент откладывает покупку, ожидая скидки, важно не потерять его интерес. Уведомления о снижении цен на избранные товары помогают вовремя сообщать пользователям о выгодных предложениях, возвраща...
Цель этой статьи — показать вам, как использовать Humanizer, чтобы вывести работу со временем и датой в ваших C#-проектах на новый уровень. Я расскажу о том, как преобразовать объекты DateTime и TimeS...
Добавляем функционал быстрых ответов на любом тарифе Битрикс24Как добавить одну из самых полезных функций для чатов открытых линий для любого коммерческого тарифа? Все очень просто, нужно лишь..... ...
Приветствую всех на Хабре. Я Сергей Михнов, CEO компании Пуск, интегратора Битрикс24. В свое время мы выбрали особый путь – специализацию на коробочной версии Битрикс24. В коммуникациях на сайте или п...
Photo by Victor Rodriguez on Unsplash Часто мы получаем от клиентов (включая даже крупных) сообщения, в которых сквозит общий мотив: «У %provider_name% нам не хватало 192 ядер, а...