User ViewModel и User View
UserViewModel использует ProfileDataManager для выборки и предоставить данные профиля пользователя:
Код: Выделить всё
class UserViewModel: ObservableObject {
@Published var userProfile: UserProfile?
private var dataManager: ProfileDataManager
init(dataManager: ProfileDataManager = .shared) {
self.dataManager = dataManager
dataManager.$userProfile
.assign(to: &$userProfile)
}
}
Код: Выделить всё
struct UserView: View {
@StateObject private var viewModel = UserViewModel()
@State private var showEditProfile = false
var body: some View {
// ...
Button("Edit Profile") {
showEditProfile = true
}
.sheet(isPresented: $showEditProfile) {
if let userProfile = viewModel.userProfile {
EditView(userProfile: userProfile)
}
}
}
}
Редактировать представление и редактировать модель представленияEditView позволяет пользователю редактировать свой профиль.
Код: Выделить всё
struct EditView: View {
@Environment(\.presentationMode) var presentationMode
@StateObject private var viewModel: EditViewModel
@State private var showImagePicker = false
@State private var showGenderModal = false
init(userProfile: UserProfile) {
_viewModel = StateObject(wrappedValue: EditViewModel(userProfile: userProfile))
}
var body: some View {
NavigationView {
Form {
Section(header: Text("Avatar")) {
AvatarSection(avatarData: Binding(
get: { viewModel.userProfile.avatarData },
set: { viewModel.userProfile.avatarData = $0 }
)) {
showImagePicker = true
}
}
Section(header: Text("Personal Info")) {
AttributeButton(title: "Gender", value: viewModel.userProfile.gender) {
showGenderModal = true
}
.sheet(isPresented: $showGenderModal) {
GenderModalView(gender: $viewModel.userProfile.gender)
}
}
}
.navigationBarTitle("Edit Profile", displayMode: .inline)
.navigationBarItems(
leading: Button("Cancel") {
viewModel.cancelChanges()
presentationMode.wrappedValue.dismiss()
},
trailing: Button("Save") {
viewModel.saveChanges()
presentationMode.wrappedValue.dismiss()
}
)
}
}
}
Код: Выделить всё
class EditViewModel: ObservableObject {
@Published var userProfile: UserProfile
private var dataManager: ProfileDataManager
init(userProfile: UserProfile, dataManager: ProfileDataManager = .shared) {
self.userProfile = userProfile
self.dataManager = dataManager
}
func saveChanges() {
dataManager.saveContext()
}
func cancelChanges() {
dataManager.rollback()
}
}
В настоящее время изменения, внесенные в EditView, немедленно отражаются в профиле пользователя, даже до нажатия кнопки «Сохранить». Конечно, он отбрасывается, если пользователь нажимает «Отменить». Но все равно атрибуты привязаны к UserProfile. Такое поведение противоречит здравому смыслу, поскольку страница редактирования не должна иметь к нему доступа. Я хочу, чтобы изменения применялись только после сохранения. Отмена должна закрыть представление без изменения профиля пользователя. Я говорю это потому, что отмена без изменения чего-либо в EditView может повлиять на данные UserView!
Я попытался передать копию UserProfile в EditView, но это вызвало значительные проблемы с использованием памяти и процессора. Я сделал это, создав расширение clone() для NSManagedObject. Клонирование UserProfile в UserView и передача клонированного UserProfile в EditView.
NSManagedObject:
Код: Выделить всё
extension NSManagedObject {
func clone() -> NSManagedObject? {
guard let context = self.managedObjectContext else { return nil }
let entityName = self.entity.name ?? ""
guard let clonedObject = NSEntityDescription.insertNewObject(forEntityName: entityName, into: context) as? Self else { return nil }
for attribute in self.entity.attributesByName {
clonedObject.setValue(self.value(forKey: attribute.key), forKey: attribute.key)
}
return clonedObject
}
}
Код: Выделить всё
func cloneUserProfile() -> UserProfile? {
return userProfile?.clone() as? UserProfile
}
func saveProfile(_ modifiedProfile: UserProfile) {
guard let userProfile = userProfile else { return }
userProfile.setValuesForKeys(modifiedProfile.dictionaryWithValues(forKeys: Array(modifiedProfile.entity.attributesByName.keys)))
dataManager.saveContext()
}
Код: Выделить всё
.sheet(isPresented: $showEditProfile) {
if let clonedProfile = viewModel.cloneUserProfile() {
EditView(userProfile: clonedProfile) { modifiedProfile in
viewModel.saveProfile(modifiedProfile)
}
}
}
Код: Выделить всё
trailing: Button("Save") {
onSave(viewModel.userProfile)
presentationMode.wrappedValue.dismiss()
}
Временные данные для модальных представлений
Вот пример того, как я использую временные данные в модальных представлениях, например, средство выбора пола:
Код: Выделить всё
struct GenderModalView: View {
@Environment(\.presentationMode) var presentationMode
@State private var tempGender: String
@Binding var gender: String?
init(gender: Binding) {
self._gender = gender
self._tempGender = State(initialValue: gender.wrappedValue ?? "Other")
}
var body: some View {
NavigationView {
Form {
Picker("Gender", selection: $tempGender) {
Text("Male").tag("Male")
Text("Female").tag("Female")
Text("Other").tag("Other")
}
.pickerStyle(.segmented)
Section {
Button("Remove Gender") {
gender = nil
presentationMode.wrappedValue.dismiss()
}
.foregroundColor(.red)
}
}
.navigationBarTitle("Gender", displayMode: .inline)
.navigationBarItems(
leading: Button("Cancel") {
presentationMode.wrappedValue.dismiss()
},
trailing: Button("Save") {
gender = tempGender
presentationMode.wrappedValue.dismiss()
}
)
}
}
}
- Как следует обрабатывать данные в EditView, чтобы избежать преждевременных изменений в модели?< /li>
Следую ли я здесь передовому опыту в MVVM? - Делаю ли я какую-нибудь глупость?
Мой ГЛАВНЫЙ вопрос: как передать данные UserProfile в Edit (без изменений Edit, непосредственно влияющих на хранилище) для манипуляций , и когда я «сохраняю», отправляю новые данные обратно, чтобы обновить их в UserProfile простым и эффективным способом, следуя рекомендациям?
Подробнее здесь: https://stackoverflow.com/questions/791 ... y-modifyin
Мобильная версия