Итак, есть задача: переделать экран авторизации на новый дизайн, добавить новые способы авторизации. И заодно опять же перейти на 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.
Если интересно, подписывайтесь на мой ТГ, там выкладываю рилсы про разработку: канал тут.
Revolt-or-die
А зачем поддерживать iOS 15?
kosyakus Автор
Аналитики проанализировали данные с Firebase по пользователям и пока весомый процент людей ещё ходит со старыми айфонами. Увы, я бы тоже давно отказалась.