Создание поля ввода электронной почты в SwiftUI

Я пытаюсь создать поле ввода электронной почты в SwiftUI 2, которое позволяет вводить только определенные символы. Код здесь основан на фрагменте кода из https://stackoverflow.com/a/57829567/356105.

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

Представление EmailTextField:

import SwiftUI
import Combine

struct EmailTextField: View {
    private class EmailTextFieldViewModel: ObservableObject {
        @Published var text = String.Empty
        private var subCancellable: AnyCancellable!
        private var validCharSet = CharacterSet(charactersIn: "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._-+$!~&=#[]@")

        init() {
            subCancellable = $text.sink {
                value in

                /* Check if the new string contains any invalid characters. */
                if value.rangeOfCharacter(from: self.validCharSet.inverted) != nil {
                    /* Clean the string (do this on the main thread to avoid overlapping with the current ContentView update cycle). */
                    DispatchQueue.main.async {
                        self.text = String(self.text.unicodeScalars.filter {
                            self.validCharSet.contains($0)
                        })
                    }
                }
            }
        }

        deinit {
            subCancellable.cancel()
        }
    }

    @ObservedObject private var viewModel = EmailTextFieldViewModel()
    private let placeHolder: String

    var body: some View {
        TextField(placeHolder, text: $viewModel.text)
            .keyboardType(.emailAddress)
            .autocapitalization(.none)
            .disableAutocorrection(true)
    }

    init(_ placeHolder: String = .Empty, text: Binding<String>) {
        self.placeHolder = placeHolder
    }
}

В родительском представлении я пытаюсь сделать это:

var body: some View {
    VStack {
        EmailTextField("Email", text: $viewModel.email)
            .onChange(of: viewModel.email, perform: onEmailInputChanged)
    }
}

private func onEmailInputChanged(changedEmail: String) {
    // Nothing happens here!
    print("\(changedEmail)")
}

Как мне изменить код EmailTextField, чтобы иметь возможность связать переменную text в своей модели представления с аргументом text: Binding<String> в его конструкторе?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
66
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вот альтернатива вашему (несколько сложному) коду. Я думаю, что это проще, предоставляет многоразовый EmailInputView и вызывает onEmailInputChanged, и хорошо работает для меня:

import SwiftUI
import Combine

class ViewModel: ObservableObject {
    @Published var email = ""
}

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

    var body: some View {
        VStack {
            EmailInputView(placeHolder: "Email", txt: $viewModel.email)
                .onChange(of: viewModel.email, perform: onEmailInputChanged)
        }
    }
    
    private func onEmailInputChanged(changedEmail: String) {
        print("-----> in onEmailInputChanged: \(changedEmail) ")
    }
}

struct EmailInputView: View {
    var placeHolder: String = ""
    @Binding var txt: String
    
    var body: some View {
        TextField(placeHolder, text: $txt)
            .keyboardType(.emailAddress)
            .onReceive(Just(txt)) { newValue in
                let validString = newValue.filter { "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._-+$!~&=#[]@".contains($0) }
                if validString != newValue {
                    self.txt = validString
                }
        }
    }
}

Другие вопросы по теме