#swift #swiftui #toggle
#swift #swiftui #переключение
Вопрос:
Я создаю функцию, аналогичную функции «избранное» в примере приложения Apple Fruta SwiftUI. Пользователи этого приложения могут просматривать список смузи и отмечать любой смузи как «любимый». Список избранного сохраняется и отображается на отдельном экране.
Приведенный ниже пример представления выполняет две важные функции:
- Он проверяет, есть ли смузи уже в списке избранных
isFavorite
, чтобы это могло быть отражено изображением кнопки. - Он добавляет / удаляет этот коктейль в / из списка избранного с
toggleFavorite
помощью .
Модель:
struct Smoothie: Identifiable, Codable {
var id: String
var title: String
var description: String
var hasFreeRecipe = false
}
class FrutaModel: ObservableObject {
@Published private(set) var favoriteSmoothieIDs = Set<Smoothie.ID>()
func toggleFavorite(smoothie: Smoothie) {
if favoriteSmoothieIDs.contains(smoothie.id) {
favoriteSmoothieIDs.remove(smoothie.id)
} else {
favoriteSmoothieIDs.insert(smoothie.id)
}
}
func isFavorite(smoothie: Smoothie) -> Bool {
favoriteSmoothieIDs.contains(smoothie.id)
}
}
Вид кнопки, который используется для (отмены) добавления смузи в избранное:
struct SmoothieFavoriteButton: View {
@EnvironmentObject private var model: FrutaModel
var smoothie: Smoothie?
var isFavorite: Bool {
guard let smoothie = smoothie else { return false }
return model.favoriteSmoothieIDs.contains(smoothie.id)
}
var body: some View {
Button(action: toggleFavorite) {
Label("Favorite", systemImage: isFavorite ? "heart.fill" : "heart")
}
}
func toggleFavorite() {
guard let smoothie = smoothie else { return }
model.toggleFavorite(smoothie: smoothie)
}
}
Моя проблема: как я могу добиться аналогичного результата при использовании переключателя вместо кнопки? Для переключения требуется привязка в качестве свойства, но поскольку isFavorite
значение для конкретного смузи берется из Set ( favoriteSmoothieIDs
), который содержит полный список избранного, я не могу просто предоставить привязку для переключателя.
Комментарии:
1. Я полагаю, что кто-то уже ответил бы на это за вас, если бы этот код содержал список, на который вы ссылаетесь. (model.favoriteSmoothieIDs пуст). Пожалуйста, предоставьте достаточно кода, чтобы продемонстрировать вашу проблему. Это включает в себя добавление определения для Smoothie и представления, в котором отображается список.
2. Я только что добавил структуру, которая определяет
Smoothie
, если вы это имеете в виду. Я предполагаю, что не имеет значения, какие фактические значения хранятся в model.favoriteSmoothieIDs. Список избранных идентификаторов смузи может быть пустым, что также является состоянием по умолчанию, в котором пользователь еще не пометил ни один смузи как любимый.3. ДА. Я понимаю. Я взял определение smoothie из оригинального руководства и запустил ваш код, прежде чем сделать свой первый комментарий. Запуск вашего кода как есть на самом деле ничего не делает. Кроме того: вы добавили MeasuredIngredient, который не определен, поэтому опять же кому-то трудно вам помочь, поскольку вы не предоставили значимого рабочего примера.
Ответ №1:
Это необходимый код:
@State var isToggleFavorite = false
Toggle("", isOn: $isToggleFavorite)
.onChange(of: isToggleFavorite) { value in
guard let smoothie = smoothie else { return }
model.toggleFavorite(smoothie: smoothie)
}
Здесь я добавил код, который отвечает на ваш запрос, и оставил оригинал нетронутым.
struct SmoothieFavoriteButton: View {
@EnvironmentObject private var model: FrutaModel
var smoothie: Smoothie?
@State var isToggleFavorite = false // <== Newly Added
var isFavorite: Bool {
guard let smoothie = smoothie else { return false }
return model.favoriteSmoothieIDs.contains(smoothie.id)
}
var body: some View {
Toggle("", isOn: $isToggleFavorite)
.onChange(of: isToggleFavorite) { value in
guard let smoothie = smoothie else { return }
model.toggleFavorite(smoothie: smoothie)
} // <== Newly Added
Button(action: toggleFavorite) {
Label("Favorite", systemImage: isFavorite ? "heart.fill" : "heart")
}
.foregroundColor(isFavorite ? .accentColor : nil)
.accessibility(label: Text("(isFavorite ? "Remove from" : "Add to") Favorites"))
} // <== Newly Added
}
func toggleFavorite() {
guard let smoothie = smoothie else { return }
model.toggleFavorite(smoothie: smoothie)
}
}
[]
Вы можете заменить кнопку текстовым объектом:
Text(isToggleFavorite ? Image(systemName: "heart.fill"): Image(systemName: "heart"))
Но тогда вам нужно будет сохранить значение isFavorite.
Если наличие сердечка в левой части переключателя приемлемо, вы можете полностью удалить кнопку и поместить изображение сердечка в метку переключателя:
Toggle(isOn: $isToggleFavorite) {
isToggleFavorite ?
Image(systemName: "heart.fill") : Image(systemName: "heart")
}
.onChange(of: isToggleFavorite) { value in
guard let smoothie = smoothie else { return }
model.toggleFavorite(smoothie: smoothie)
}
Вам доступно несколько опций.
Комментарии:
1. Спасибо за обширную помощь! Это решает 2-ю часть проблемы, но не 1-ю часть: проверьте, есть ли смузи уже в списке избранных при первой загрузке страницы, чтобы показать правильное состояние переключателя. Вы не можете решить эту проблему, установив правильное
isToggleFavorite
состояние в модификаторе onAppear, потому что это немедленно вызовет состояние onChange переключателя. Теперь я используюonTapGesture
вместоonChange
переключателя, но это не будет хорошо работать для переключений с меткой.2. Я уже потратил на это пару часов. Если у меня будет возможность вернуться к нему, я это сделаю. Мне нужно сосредоточиться на других вещах. Подумайте о том, чтобы проголосовать за него, если мой ответ был полезен до сих пор, чтобы дать другим шанс найти его. Кто-то другой может дать вам ответ, который я не предоставил. Это было бы идеально. 🍀